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

AxeShell

서비스 문서를 감싸는 런타임 워크스페이스 셸 — 레일·전환기는 런타임이, 세션·프롬프트는 위젯 iframe 이 채운다.
레이아웃axe-shellStatic specimenshellworkspace워크스페이스
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 이 채운다. 이 문서의 클래스는 그 두 위젯 호스트·프레임·퇴행 링크·스크림 6개뿐이다. ≥1440px 에서 패널은 3열의 마지막 트랙, 1024–1439px 에서는 우측 시트, ≤1023px 에서는 전체 폭 시트다. iframe 은 한 번 장착되면 옮기지 않는다(옮기면 재로드) — 폭 전환·시트·확장은 모두 CSS 기하다. 계정 메뉴의 세션 행은 매니페스트 viewer.auth 가 정한다: loginHref(게스트) = '로그인' 한 행, 로그인 뒤 = logoutHref '로그아웃' · switchHref '다른 계정으로 로그인'(생략 시 플랫폼 OP 계정 선택). 게스트에게는 결속할 신원(subHash)이 없어 위젯을 장착하지 않는다. 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"> 안(콘텐츠 표식 자리)에 들어간다.
widgetBasestringSHELL_WIDGET_ORIGIN위젯 문서 URL 접두어(오리진 + 선택 경로 접두어). 수신 검증·targetOrigin 은 그 오리진만 쓴다. design 데모·테스트만 바꾼다. DOM 속성에서는 읽지 않는다.
widgetsbooleantruefalse = 레일만. 위젯 호스트를 숨기고 본문 하단 예약을 푼다(루트 data-axe-shell-widgets="off"). SSR 부터 그 상태로 렌더해 부팅 때 3열·프롬프트 예약이 걷히는 시프트가 없다. 게스트 매니페스트(viewer.subHash 없음)도 같다.
deferBootbooleanfalsetrue = 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" 에서 위로 확장.
.axe-shell-frameinternal런타임이 호스트 안에 만드는 위젯 <iframe>. 호스트를 테두리 없이 채운다.
.axe-shell-fallbackinternal위젯이 10초 안에 ready 를 보내지 않을 때 런타임이 호스트에 남기는 워크스페이스 링크.
.axe-shell-scriminternal시트(<1440 패널)·모바일 프롬프트 확장 뒤 배경. 표시는 CSS 가 상태 속성으로 정하고, 클릭은 런타임이 collapse 로 받는다.
비-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 은 재로드되지 않는다. 모션 없음(고빈도 UI). forced-colors 에서는 호스트 경계가 CanvasText 1px 로 선다.

06 · Judgment

권장 · 지양

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