본문으로 건너뛰기
레이아웃 / axe-shell

AxeShell

서비스 문서를 감싸는 런타임 워크스페이스 셸 — 레일·전환기는 런타임이, 세션·프롬프트는 위젯 iframe 이 채운다.
레이아웃axe-shellStatic specimenshell셸workspace워크스페이스
01 · Specimen

먼저 보고, 그다음 계약을 읽습니다

서비스 문서를 감싸는 런타임 워크스페이스 셸 — 레일·전환기는 런타임이, 세션·프롬프트는 위젯 iframe 이 채운다.

Workbench 불러오는 중…
Component intent의도와 경계 읽기

AxeShell 은 정적 골격(axe-shell-skeleton.html) + <head> 동기 스텁(axe-shell-stub.js, ≤1KB) + defer 런타임(axe-shell.js) 세 조각이다. 서비스가 하는 일은 골격 두 반쪽 사이에 본문을 넣고 매니페스트 carrier(`<script type="application/json" id="axe-shell-manifest">`) 1개를 싣는 것뿐이다. 레일 메뉴·서비스/법인/계정 전환기·접기·⌘K 는 런타임이 기존 .axe-workspace-* 어휘로 그리고, 우측 세션 목록과 하단 프롬프트는 https://axelabs.ai/shell/* 위젯 iframe 이 채운다. 이 문서의 클래스는 위젯 호스트·프레임·퇴행 링크·스크림과 Claude Code 식 프롬프트 어휘다. ≥1440px 에서 패널은 3열의 마지막 트랙, 1024–1439px 에서는 우측 시트, ≤1023px 에서는 전체 폭 시트다. iframe 은 한 번 장착되면 옮기지 않는다(옮기면 재로드) — 폭 전환·시트·확장은 모두 CSS 기하다. 계정 메뉴의 세션 행은 매니페스트 viewer.auth 가 정한다: loginHref(게스트) = '로그인' 한 행, 로그인 뒤 = logoutHref '로그아웃' · switchHref '다른 계정으로 로그인'(생략 시 플랫폼 OP 계정 선택). 게스트에게는 결속할 신원(subHash)이 없어 위젯을 장착하지 않는다. 서비스 전환기 목록은 viewer.services(테넌트가 쓰는 서비스 key 배열)로 거른다 — 없으면 opt-in 아닌 카탈로그 전체, 현재 서비스는 늘 남는다. opt-in 항목(orbit·magnet)은 viewer.services 에 명시될 때만 서고, 목적지는 viewer.serviceHrefs(key → key 별 정적 허용 오리진 위 https URL)가 준다. React 소비자는 AxeShell 컴포넌트가 같은 골격을 렌더하고 boot/refresh/destroy 만 부른다. 정본 = src/lib/shell/SPEC.md.

02 · Use

예제

SSOT에 등록된 실제 API 기준 snippet입니다. standalone 배지만 독립 실행 단위이며, fragment는 주변 state·handler 문맥을 생략합니다.

01React 소비자tsxfragment
React 소비자
import { AxeShell } from "@axe/ui";<AxeShell manifest={manifest}>  <h1>결재함</h1></AxeShell>
02CSS-only 소비자 배선 (maud · Jinja)htmlfragment
CSS-only 소비자 배선 (maud · Jinja)
<head>  <script src="{kit}/shell/axe-shell-stub.js"></script>  <script type="application/json" id="axe-shell-manifest" data-axe-shell-entity>{…}</script>  <script src="{kit}/shell/axe-shell.js" defer></script>  <link rel="stylesheet" href="{kit}/axe-ui.css"></head><body>  <!-- {kit}/shell/axe-shell-skeleton.before.html -->  <h1>결재함</h1>  <!-- {kit}/shell/axe-shell-skeleton.after.html --></body>
03위젯 호스트 (골격 발췌)htmlfragment
위젯 호스트 (골격 발췌)
<main id="main" class="axe-app-shell__content">…</main><section id="axe-shell-panel" class="axe-shell-panel" aria-label="세션" data-axe-shell-slot="panel"></section><!-- __main 밖, 셸 루트 직계 --><section class="axe-shell-prompt" aria-label="프롬프트" data-axe-shell-slot="prompt"></section><div class="axe-shell-scrim" data-axe-shell-slot="scrim" hidden></div>
03 · React

Props

TSX 소스가 진실입니다. 주요 export 컴포넌트의 public API만 노출합니다.

이름타입필수기본값설명
manifestAxeShellManifest | null필수—서버가 만든 AxeShellManifest@1. 래퍼가 serializeManifestForScript 로 carrier <script> 에 직렬화한다. null = 아직 모름(오리진을 클라이언트에서만 아는 정적 렌더) — 골격만 렌더하고 부팅을 미룬다.
childrenReact.ReactNode필수—서비스 본문. 골격의 <main id="main"> 안(콘텐츠 표식 자리)에 들어간다.
widgetBasestring—SHELL_WIDGET_ORIGIN위젯 문서 URL 접두어(오리진 + 선택 경로 접두어). 수신 검증·targetOrigin 은 그 오리진만 쓴다. design 데모·테스트만 바꾼다. DOM 속성에서는 읽지 않는다.
widgetsboolean—truefalse = 레일만. 위젯 호스트를 숨기고 본문 하단 예약을 푼다(루트 data-axe-shell-widgets="off"). SSR 부터 그 상태로 렌더해 부팅 때 3열·프롬프트 예약이 걷히는 시프트가 없다. 게스트 매니페스트(viewer.subHash 없음)도 같다.
deferBootboolean—falsetrue = carrier·골격은 렌더하되 런타임 부팅을 미룬다. 뷰어를 클라이언트에서야 확정하는 정적 렌더 소비자(design)가 게스트로 한 번, 로그인으로 또 한 번 부팅하지 않도록 세션 확정 뒤 false 로 내린다.
onNavigate(url: URL) => void——위젯의 navigate 를 허용목록 재판정 뒤 받는다. 기본 = location.assign. SPA 소비자가 클라이언트 라우팅에 쓴다.
classNamestring——셸 루트에 덧붙일 클래스(골격 클래스는 유지). 소비자 편집 표면의 스코프용.
contentClassNamestring——<main id="main"> 에 덧붙일 클래스.
04 · Any stack

.axe-* 클래스 계약

React 밖에서도 같은 표면을 그리는 공개 계약입니다. stable은 minor 버전 안에서 이름을 바꾸지 않습니다.

클래스안정성용도
.axe-shell-panelstable골격의 우측 세션 위젯 호스트 <section>. ≥1440 은 3열 sticky, <1440 은 루트 data-axe-shell-panel="open" 일 때만 보이는 우측 시트.
.axe-shell-panel-togglestable바 우측의 시트 토글 버튼(.axe-workspace-bar__icon-button 과 함께). ≥1440 에서는 숨는다.
.axe-shell-promptstable골격의 하단 프롬프트 위젯 호스트 <section>. 가운데 열 경계에 맞춘 고정 바, 루트 data-axe-shell-prompt="expanded" 에서 바 아래부터 뷰포트 바닥까지 캔버스를 채운다. 접힘 기본 높이는 84px.
.axe-shell-frameinternal런타임이 호스트 안에 만드는 위젯 <iframe>. 호스트를 테두리 없이 채운다.
.axe-shell-fallbackinternal위젯이 10초 안에 ready 를 보내지 않을 때 런타임이 호스트에 남기는 워크스페이스 링크.
.axe-shell-scriminternal시트(<1440 패널)·모바일 프롬프트 확장 뒤 배경. 표시는 CSS 가 상태 속성으로 정하고, 클릭은 런타임이 collapse 로 받는다.
.axe-promptstablemono 프롬프트 루트. data-state=expanded 는 100vh, collapsed 는 auto.
.axe-prompt__{log|box|caret|input|menu|option|option-key|option-desc|option-empty|foot|foot-start|foot-end|link}stable대화 스크롤·입력·후보 listbox·마지막 한 줄 foot 어휘. 선택 후보·텍스트 링크는 accent.
.axe-prompt__{status|status-title|status-spacer}stable세션 모드의 mono 12.5px 상태 행·ink semibold 말줄임 제목·flex:1 spacer. 좁은 폭에서 감긴다.
.axe-prompt-livestable도트 + nowrap 라벨. data-state=waiting(success halo) / working(accent halo) / none(faint) / reconnecting(faint 빈 원). 모션 0.
.axe-prompt-live__dotstable8px live 상태 원, halo 는 정적 box-shadow. forced-colors 에서는 시스템 색 채움·빈 원으로 가시성을 보존한다.
.axe-prompt-line__{head|meta|tag}stable게재 제목과 우측 메타의 baseline flex 머리(좁으면 감김)·faint 12px nowrap 메타·11.5px muted 테두리 tag(세션에 올리지 않음).
.axe-prompt-line__body--mdstable__body 와 함께 쓰는 Markdown 본문. white-space:normal, mono 전사 안 p/ul/ol/li/pre/code/blockquote/h1–h4/table compact 간격, 긴 pre·table 내부 스크롤.
.axe-session-item__dot--livestable__dot 과 함께 쓰는 연결된 로컬 에이전트의 success 도트·정적 halo. forced-colors 는 CanvasText 채움.
.axe-session-item__presencestable패널 세션 행 이름 아래의 small muted 한 줄 말줄임. __link 의 직계 자식, 일반·compact 목록에서 도트 다음 열에 선다. 상태는 글로도 알린다.
.axe-prompt-linestable마커 2.2ch + 본문 대화 행.
.axe-prompt-line__{marker|body|name}stable글리프·pre-wrap 본문·굵은 도구명.
.axe-prompt-line--{user|ai|tool|result|spin|alert}stable사용자 밴드·답변 ink·도구 success·결과 ⎿·진행 accent·오류 danger.
.axe-prompt-welcomestableaccent 테두리의 시작 안내.
.axe-prompt-welcome__titlestable✻ 마커와 굵은 이름.
.axe-prompt-confirmstable대화 안 동의 상자, strong 테두리·muted 문단.
.axe-prompt-confirm__titlestable동의 상자의 굵은 제목.
.axe-prompt-choicesstable동의 선택지 listbox 열.
.axe-prompt-choicestable텍스트 버튼. aria-selected=true 는 accent 와 ❯ 마커.
.axe-prompt-helpstable14ch 키 + 본문 도움말 그리드.
.axe-prompt-help__headstable두 열 전체를 차지하는 muted 제목.
비-React 소비 노트
CSS 만으로 되는 것: 골격 기하(3열·시트·프롬프트 자리 예약), 스텁이 세운 접힘(<html data-axe-rail-collapsed>)의 첫 페인트, 법인 행 높이 예약. 레일 메뉴·전환기·위젯 장착·시트 열고 닫기는 axe-shell.js 런타임이 한다 — 런타임 셸에서는 워크스페이스 크롬의 무JS 동작을 보장하지 않는다.
05 · Inclusive

접근성

키보드, ARIA, 구현 노트를 함께 검토합니다.

Keyboard
  • ⌘K / Ctrl+K — 프롬프트 위젯에 이동 모드로 포커스
  • ⌘J / Ctrl+J — 프롬프트 위젯에 질문 모드로 포커스(AI 가 꺼진 서비스는 이동 모드)
  • Esc(포커스가 부모 문서에 있을 때) — 패널 시트 → 프롬프트 확장 → 레일 서랍 순으로 닫고 연 트리거로 포커스 복원
ARIA

두 호스트는 aria-label 이 붙은 <section>("세션"·"프롬프트"), 위젯 <iframe> 은 title 을 가진다. 시트 토글은 aria-controls="axe-shell-panel" + aria-expanded 로 상태를 알린다. 스킵 링크는 #main 으로 간다.

Notes

닫힌 시트는 visibility:hidden 이라 포커스·접근성 트리에서 빠지지만 iframe 은 재로드되지 않는다. ≥1024 프롬프트 확장은 본문만 inert, 레일·바·패널은 상호작용 가능하다. 시트는 배경 전체가 inert 다. 모션 없음(고빈도 UI). forced-colors 에서는 호스트 경계가 CanvasText 1px 로 선다.

06 · Judgment

권장 · 지양

권장
  • 골격은 킷의 before/after 두 반쪽을 그대로 include 한다 — 서비스가 셸 마크업을 손으로 옮기지 않는다.
  • 매니페스트 carrier 는 문서에 정확히 1개, 셸 루트보다 앞(head)에 둔다.
  • 법인 축이 있으면 carrier 에 data-axe-shell-entity 를 붙여 첫 페인트부터 identity 행 높이를 예약한다.
  • 동기 스텁은 셸 루트보다 앞에서 돌게 싣는다(head, 또는 React 중첩 레이아웃이면 본문 맨 앞 인라인) — 접힌 레일이 첫 페인트부터 접혀 있다.
  • 로그인 전 화면은 viewer.auth.loginHref 로 자기 로그인 경로를 준다 — 레일에서 로그인으로 갈 길이 생긴다.
  • 테넌트가 쓰지 않는 서비스는 viewer.services 로 전환기에서 뺀다 — 카탈로그 key 와 교집합한 값만 싣는다(모르는 key 하나면 매니페스트 전체가 무효).
  • 테넌트 전용 목적지(orbit 관리자·magnet 콘솔·frame 정문의 테넌트 경로·전용 hive 인스턴스)는 viewer.serviceHrefs 로 준다 — 오리진이 킷의 정적 허용 오리진 밖이면 매니페스트 전체가 무효다.
지양
  • 위젯 iframe 을 옮기거나 복제하지 말 것 — DOM 이동은 재로드이고 세션 상태가 사라진다.
  • 본문을 별도 스크롤 컨테이너로 바꾸지 말 것 — 프롬프트 자리 예약은 문서 스크롤을 전제한다.
  • 호스트·프레임에 전환 애니메이션을 얹지 말 것 — 하루 수백 번 열고 닫는 표면이다.