Nav

사이드바 같은 사이트나 앱의 내비게이션입니다. Nav.Root는 nav 랜드마크입니다. 각 Nav.Group은 목록이고, label이 있으면 그것이 이름이 됩니다. 각 Nav.Item은 Nav.Link를 담는 목록 항목입니다. Nav.Branch는 네이티브 <details>이며, 토글이 항목들의 중첩된 목록을 엽니다.

이 페이지의 내용
<Nav.Root aria-label="Docs">
  <Nav.Group label="Components">
    <Nav.Item>
      <Nav.Link href="/button">Button</Nav.Link>
    </Nav.Item>
    <Nav.Item>
      <Nav.Link href="/field" aria-current="page">
        Field
      </Nav.Link>
      <Nav.Branch label="Field pages" open>
        <Nav.Item>
          <Nav.Link href="/field/select">Select</Nav.Link>
        </Nav.Item>
      </Nav.Branch>
    </Nav.Item>
  </Nav.Group>
</Nav.Root>

링크와 가지(branch) 토글은 각각 하나의 Tab 정지점입니다. Nav에는 화살표 키도 선택도 없으므로 Tree가 아닙니다.

현재 페이지

지금 있는 페이지로 가는 링크에 aria-current="page"를 표시하세요. 비어 있거나 false가 아닌 값이면 링크를 현재 행으로 그리며, nav.item.bg.current, nav.item.fg.current, nav.item.font.current-weight 토큰을 씁니다. 속성은 라우터가 설정합니다.

Nav.Link는 Link처럼 render를 받습니다. 그래서 라우터의 링크가 <a>에 넣었을 모든 prop을 클래스까지 받아 행을 그립니다.

import NextLink from 'next/link';

<Nav.Link
  href="/billing"
  aria-current={pathname === '/billing' ? 'page' : undefined}
  render={({ href, ...props }) => (href ? <NextLink href={href} {...props} /> : <a {...props} />)}
>
  Billing
</Nav.Link>

링크 텍스트 앞의 아이콘은 nav.icon.size 토큰의 크기를 받습니다.

가지

같은 항목에서 Nav.Link 뒤에 오는 Nav.Branch는 그 페이지의 하위 페이지를 담습니다. 링크는 자기 행을 유지하고, 토글은 행 끝의 chevron이므로 label은 화면에서 숨겨지고 토글의 이름으로만 쓰입니다. 링크 자체의 텍스트가 아니라 "Field 하위 페이지"처럼 하위 페이지를 가리키는 이름을 붙이세요. 항목에 Nav.Branch만 있으면 label이 토글의 텍스트로 보이며, 자기 페이지가 없는 페이지 묶음에 씁니다.

가지는 네이티브 <details>입니다. open은 렌더링될 때와 prop이 바뀔 때마다 가지가 열려 있을지를 정합니다. true는 내비게이션이 가지 안으로 이동할 때처럼 가지를 열고, false는 닫습니다. 그 사이에는 사용자가 열고 닫으며, 이는 onToggle로 따라가세요. 중첩된 목록은 nav.indent만큼 들여 쓰고, nav.rule.color와 nav.rule.width로 그린 선 아래에 놓입니다.

chevron은 글리프입니다. GlyphsProvider에서 chevron을 설정하면 아이콘 라이브러리로 그립니다. 아래쪽을 가리키게 그리세요. 닫힌 가지는 이를 행의 끝 쪽으로 돌립니다.

접근성을 지키는 조합

Todae가 맡는 것: nav 랜드마크입니다. 각 그룹은 레이블이 aria-labelledby로 이름을 붙이는 ul role="list"이고, 각 항목은 목록 항목입니다. 가지는 <details>이며, 브라우저는 그 <summary>를 열림 상태가 있는 버튼으로 노출하고 label이 이름을 붙입니다. 링크 뒤에서는 레이블이 화면에서 숨겨지지만 여전히 토글의 이름이 됩니다. chevron은 aria-hidden입니다. 포커스 링은 행 안쪽에 그려지므로, 행의 옆을 자르는 스크롤 컨테이너 안에서도 온전히 보입니다.

직접 제공할 것: 페이지에 nav가 둘 이상이면 aria-label이나 aria-labelledby로 붙인 랜드마크 이름. 지금 있는 페이지로 가는 링크의 aria-current="page". 모든 가지의 label. 링크 뒤에서는 "Field 하위 페이지"처럼 토글이 보여 주는 것을 말하는 레이블.

주의할 점: aria-current 없이 스타일로만 표시한 현재 페이지. 링크의 텍스트를 되풀이하는 가지 레이블: 스크린 리더가 이름이 같은 컨트롤 두 개를 읽습니다. 검색 필드처럼 링크와 가지가 아닌 컨트롤을 nav 안에 두는 것: 목록 밖에 두어야 합니다.

언제 쓰나요

언제: 사이드바, 드로어, 메뉴 패널에서 그룹과 한두 단계의 하위 페이지를 두고 사이트나 앱의 페이지 사이를 이동할 때.

쓰지 않을 때: 현재 페이지 위의 경로 → Breadcrumbs. 한 페이지의 섹션 → Tabs. 파일처럼 단계별로 열어 보는 계층 → Tree. 동작 목록 → Menu.

Props

Nav.Root props
이름타입설명
aria-labelstring

랜드마크 이름. 헤더의 nav 옆에 있는 "Docs"처럼 페이지에 nav가 둘 이상이면 붙이세요.

childrenReact.ReactNode

Nav.Group 요소.

Nav.Group props
이름타입설명
labelReact.ReactNode

목록 위에 보이는 텍스트. 스크린 리더에게는 목록의 이름도 됩니다.

aria-labelstring

레이블이 없을 때, 또는 레이블 대신 목록의 이름을 붙입니다. aria-labelledby도 목록에 붙습니다.

childrenReact.ReactNode

Nav.Item 요소.

Nav.Item props
이름타입설명
childrenReact.ReactNode

Nav.Link, Nav.Branch, 또는 Nav.Link와 그 뒤에 오는 하위 페이지의 Nav.Branch.

Nav.Link props
이름타입설명
hrefstring

링크가 가는 곳.

render(props: React.ComponentPropsWithRef<"a">) => React.ReactElement

라우터 링크처럼 일반 a가 아닌 것을 렌더링합니다. 클래스 이름까지 모든 prop을 받으며, 이를 a에 넘겨야 합니다.

aria-current"page"| "step"| "location"| "date"| "time"| "true"| "false"

지금 있는 페이지로 가는 링크를 표시하며, 링크를 현재 행으로 그립니다.

childrenReact.ReactNode

링크 텍스트. 앞에 아이콘을 둘 수 있습니다.

Nav.Branch props
이름타입설명
label 필수React.ReactNode

토글의 텍스트. 같은 항목에서 Nav.Link 뒤에 오면 토글은 chevron만 보여 주고 이 텍스트는 이름으로만 쓰이므로, "Field 하위 페이지"처럼 하위 페이지를 가리키는 이름을 붙이세요.

openboolean

렌더링될 때와 prop이 바뀔 때마다 가지가 열려 있을지를 정합니다. true는 열고 false는 닫습니다. 그 사이에는 사용자가 열고 닫으며, 이는 onToggle로 따라가세요.

onToggle(event: React.ToggleEvent<HTMLDetailsElement>) => void

가지가 열리거나 닫힌 뒤 호출됩니다.

childrenReact.ReactNode

하위 페이지인 Nav.Item 요소.