본문으로 건너뛰기
내비게이션 / sidebar

Sidebar

앱 셸 좌측 세로 네비게이션 — 활성 항목 + 접이식 그룹.
내비게이션sidebarStatic specimensidebar사이드바side nav네비게이션
01 · Specimen

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

앱 셸 좌측 세로 네비게이션 — 활성 항목 + 접이식 그룹.

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

Radix 비의존 자체 구현. semantic <nav> + <ul> + collapsible 그룹 위에 controlled/uncontrolled 활성 상태와 그룹 펼침/접힘을 얹었다. items 배열로 data-driven 하게 쓰거나, SidebarItem/SidebarGroup 을 children 으로 조합(composition)해 쓴다 — 둘을 동시에 넘기지 말 것. collapsible=false 면 모든 그룹이 항상 펼쳐진 정적 헤더로 렌더된다. 보통 AppShell 의 sidebar 슬롯에 넣는다.

02 · Use

예제

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

01data-driven (controlled)tsxfragment
data-driven (controlled)
import { Sidebar } from "@axe/ui";<Sidebar  ariaLabel="Primary"  value={current}  onValueChange={setCurrent}  defaultExpanded={{ proj: true }}  items={[    { kind: "item", value: "home", label: "홈" },    {      kind: "group",      id: "proj",      label: "프로젝트",      items: [        { kind: "item", value: "frame", label: "frame" },        { kind: "item", value: "hive", label: "hive" },      ],    },  ]}/>
02composition (uncontrolled)tsxfragment
composition (uncontrolled)
import { Sidebar, SidebarGroup, SidebarItem } from "@axe/ui";<Sidebar defaultValue="home">  <SidebarItem value="home">홈</SidebarItem>  <SidebarGroup label="프로젝트" defaultOpen>    <SidebarItem value="frame">frame</SidebarItem>    <SidebarItem value="hive" href="/hive">hive</SidebarItem>  </SidebarGroup></Sidebar>
03CSS-only (비-React)htmlfragment
CSS-only (비-React)
<nav class="axe-sidebar" aria-label="Primary">  <ul class="axe-sidebar__list" role="list">    <li class="axe-sidebar__item axe-sidebar__item--active">      <a href="/home" class="axe-sidebar__link" aria-current="page">        <span class="axe-sidebar__label">홈</span>      </a>    </li>    <li class="axe-sidebar__group">      <button type="button" class="axe-sidebar__group-header"              aria-expanded="true" aria-controls="grp-proj">        <span class="axe-sidebar__group-label">프로젝트</span>        <span class="axe-sidebar__chevron" aria-hidden="true">›</span>      </button>      <ul id="grp-proj" role="list" class="axe-sidebar__group-list">        <li class="axe-sidebar__item">          <a href="/frame" class="axe-sidebar__link">            <span class="axe-sidebar__label">frame</span>          </a>        </li>      </ul>    </li>  </ul></nav>
03 · React

Props

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

이름타입필수기본값설명
itemsSidebarNode[]Data-driven 항목 트리. SidebarItemNode({ kind: 'item', value, label, icon?, href?, disabled? }) 와 SidebarGroupNode({ kind: 'group', label, icon?, id?, items }) 의 배열. children 과 동시 사용 금지.
valuestring활성 항목 value (controlled). 주면 onValueChange 도 함께 줘야 한다(아니면 dev 경고).
defaultValuestringuncontrolled 초기 활성 value.
onValueChange(value: string) => void항목 선택 시 호출.
collapsiblebooleantrue그룹 접기/펴기 허용. false 면 그룹 헤더가 비-interactive <div> 로 항상 펼침.
defaultExpandedRecord<string, boolean>group.id 별 초기 펼침 상태. 미지정 시 모든 그룹 펼침(보수적).
ariaLabelstring"Sidebar"루트 <nav> 의 aria-label(스크린리더).
childrenReact.ReactNodecomposition 모드 — SidebarItem/SidebarGroup 을 직접 배치. items 대신 사용.
...restOmit<React.HTMLAttributes<HTMLElement>, "onChange">나머지는 루트 <nav> 로 전파.
SidebarItem.valuestring필수활성 비교 키.
SidebarItem.activeboolean명시적 active — Provider 없이 쓰거나 강제 표시할 때. 미지정 시 컨텍스트의 active===value 로 판정.
SidebarItem.hrefstring주면 <a>, 없으면 <button> 으로 렌더.
SidebarItem.iconReact.ReactNode라벨 앞 아이콘(aria-hidden).
SidebarItem.disabledboolean비활성. 클릭 무시 + aria-disabled/disabled.
SidebarItem.onSelect(value: string) => void선택 시 항목 로컬 콜백(컨텍스트 setActive 와 별개).
SidebarGroup.labelReact.ReactNode필수그룹 헤더 라벨.
SidebarGroup.idstringdefaultExpanded 매핑 키. 미지정 시 label 의 string 값.
SidebarGroup.defaultOpenbooleantrueuncontrolled 초기 펼침.
SidebarGroup.openbooleancontrolled 펼침 상태.
SidebarGroup.onOpenChange(open: boolean) => void펼침 토글 시 호출.
04 · Any stack

.axe-* 클래스 계약

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

클래스안정성용도
.axe-sidebarstable루트 <nav>.
.axe-sidebar__liststable최상위 항목 <ul role="list">.
.axe-sidebar__itemstable단일 항목 <li>.
.axe-sidebar__item--activestable활성 항목 <li>.
.axe-sidebar__item--disabledstable비활성 항목 <li>.
.axe-sidebar__linkstable항목의 클릭 대상 <a>/<button>.
.axe-sidebar__iconstable항목/그룹 앞 아이콘 span.
.axe-sidebar__labelstable항목 라벨 텍스트 span.
.axe-sidebar__groupstable그룹 <li>.
.axe-sidebar__group-headerstable그룹 토글 <button>(collapsible) 헤더.
.axe-sidebar__group-header--staticstable비-collapsible 그룹 헤더 <div>(토글 불가).
.axe-sidebar__group-labelstable그룹 헤더 라벨 span.
.axe-sidebar__group-liststable그룹 하위 항목 패널 <ul>. 접힘 시 hidden 속성.
.axe-sidebar__chevroninternal그룹 펼침 표시 chevron span(장식).
비-React 소비 노트
비-React(jinja/maud)에서도 동일 markup 으로 재현 가능. 활성 표시는 <li> 에 axe-sidebar__item--active + 링크에 aria-current="page", 그룹 펼침은 헤더 aria-expanded 와 패널 <ul> 의 hidden 속성을 직접 토글한다(펼침 상태 CSS 훅 없음 — hidden 이 진실).
05 · Inclusive

접근성

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

Keyboard
  • Tab — 링크/버튼/그룹 헤더를 문서 순서로 이동(자체 roving tabindex 없음)
  • Enter/Space — 그룹 헤더에서 펼침/접힘(네이티브 <button>)
ARIA

루트 <nav aria-label> 안에 <ul role="list">. 활성 항목의 <a>/<button> 에 aria-current="page". 그룹 헤더는 <button aria-expanded aria-controls={panelId}>, 하위 패널 <ul> 은 접힘 시 hidden 속성으로 AX 트리에서 제외. disabled 항목은 <button disabled> 또는 <a aria-disabled="true">.

Notes

controlled 로 쓸 땐 value 와 onValueChange 를 반드시 함께 준다(하나만 주면 dev 콘솔 경고 + 선택이 silent 무시). collapsible=false 면 그룹 헤더가 <div class="...--static"> 로 렌더되어 포커스/토글 불가.

06 · Judgment

권장 · 지양

권장
  • controlled 로 쓸 땐 value 와 onValueChange 를 항상 쌍으로 넘긴다.
  • 라우팅이 있으면 SidebarItem 에 href 를 줘 <a> 로 렌더(우클릭/새 탭 지원).
  • 그룹 초기 펼침은 group.id + defaultExpanded 로 제어한다.
지양
  • items 와 children 을 동시에 넘기지 말 것(한쪽만).
  • aria-current 를 직접 붙이지 말 것 — 활성 판정이 자동 부여한다.
검색창을 열면 컴포넌트 인덱스를 불러옵니다.