Breadcrumb
지금 보고 있는 페이지 위쪽의 경로입니다. 화면이 계층 안에 놓여 있고 읽는 사람이 다시 위로 올라갈 수 있어야 할 때 씁니다.
import { Breadcrumb, BreadcrumbItem } from 'neba';
<Breadcrumb>
<BreadcrumbItem href="/">Home</BreadcrumbItem>
<BreadcrumbItem href="/projects">Projects</BreadcrumbItem>
<BreadcrumbItem>Settings</BreadcrumbItem>
</Breadcrumb>;Props
Breadcrumb
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| locale | string | — | BCP 47 태그. nav 이름과 … 버튼의 이름을 이 언어로 씁니다 |
| size공통 | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | 높이와 타입 스케일 |
| color공통 | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | 의미론적 색 역할. 임의 색상값은 받지 않습니다 |
| density공통 | 'default' | 'compact' | 'default' | 단계 사이의 간격만 바꿉니다 |
| separator | 'chevron' | 'arrow' | 'slash' | 'dot' | ReactNode | 'chevron' | 두 단계 사이에 그려지는 표시. 네 가지 이름 중 하나이거나 아무 노드나 됩니다. chevron과 arrow는 "그다음"을, slash는 "경로"를, dot은 "한 가지의 동급들"을 말합니다 |
| maxItems | number | — | 이 수를 넘으면 가운데를 …로 접습니다. 생략하면 아무리 길어도 전부 보여줍니다 |
| itemsBeforeCollapse | number | 1 | 접힌 트레일 앞쪽에 남길 단계 수 |
| itemsAfterCollapse | number | 1 | 뒤쪽에 남길 단계 수 |
| expandable | boolean | true | …를 누르면 그 자리에서 펼쳐지는지. 끄면 접힘 표시로만 남습니다 |
| label | string | — | 트레일이 읽히는 이름. nav의 aria-label입니다 |
| expandLabel | string | — | …가 읽히는 이름 |
| structuredData | boolean | false | schema.org BreadcrumbList를 JSON-LD로 함께 내보냅니다. 접힌 단계도 모두 포함됩니다 |
| baseUrl | string | — | structuredData에서 상대 href를 절대 URL로 만들 기준, 사이트의 origin |
| children | ReactNode | — | BreadcrumbItem들 |
나머지 <nav> 속성은 그대로 전달됩니다. 공용 축은 prop 규약에 있습니다.
BreadcrumbItem
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| href | string | — | 단계를 링크로 렌더링합니다 |
| onClick | MouseEventHandler<HTMLElement> | — | 단계를 누를 때. href가 없으면 버튼으로 렌더링됩니다 |
| startIcon | ReactNode | — | 라벨 앞의 내용, 홈 글리프, 저장소 아바타 |
| endIcon | ReactNode | — | 라벨 뒤의 내용 |
| current | boolean | — | 지금 보고 있는 페이지로 표시하고 링크를 걷어냅니다. 마지막 단계는 지정하지 않아도 현재이며, 다른 단계에 지정하면 마지막 단계에서 표시가 걷힙니다. 한 트레일에 현재는 하나뿐입니다 |
| disabled | boolean | false | 사용 불가. 트레일에는 그대로 남습니다 |
| children | ReactNode | — | 단계의 라벨 |
나머지 <li> 속성은 그대로 각 단계에 전달됩니다.
예시
separator
separator는 네 가지 이름(chevron·arrow·slash·dot) 중 하나이거나 아무 노드나 받습니다. 방향을 가리키는 둘은 RTL에서 반대로 돌아갑니다.
import { Breadcrumb, BreadcrumbItem } from 'neba';
export default function BreadcrumbSeparators() {
return (
<div className="flex flex-col gap-3">
{(['chevron', 'arrow', 'slash', 'dot'] as const).map((separator) => (
<Breadcrumb key={separator} separator={separator} label={separator}>
<BreadcrumbItem href="#home">Home</BreadcrumbItem>
<BreadcrumbItem href="#projects">Projects</BreadcrumbItem>
<BreadcrumbItem>{separator}</BreadcrumbItem>
</Breadcrumb>
))}
{/* Anything else is drawn as it is given. */}
<Breadcrumb separator="»" label="custom">
<BreadcrumbItem href="#home">Home</BreadcrumbItem>
<BreadcrumbItem href="#projects">Projects</BreadcrumbItem>
<BreadcrumbItem>custom</BreadcrumbItem>
</Breadcrumb>
</div>
);
}maxItems
maxItems를 넘는 트레일은 가운데를 … 뒤로 접고, 그것을 누르면 다시 펼칩니다. 양 끝에 몇 개를 남길지는 itemsBeforeCollapse와 itemsAfterCollapse가 정하며 둘 다 기본값은 1입니다. expandable={false}는 접힘을 표시로만 남깁니다.
접기는 두 단계 이상을 걷어낼 때만 일어납니다. 한 단계를 대신 서는 것은 트레일을 짧게 하는 대신 길게 만들기 때문입니다.
import { Breadcrumb, BreadcrumbItem } from 'neba';
export default function BreadcrumbCollapse() {
return (
<div className="flex flex-col gap-4">
{/* One step at each end, everything between folded behind the `…`. */}
<Breadcrumb maxItems={3} label="Folded">
<BreadcrumbItem href="#home">Home</BreadcrumbItem>
<BreadcrumbItem href="#org">Acme</BreadcrumbItem>
<BreadcrumbItem href="#team">Platform</BreadcrumbItem>
<BreadcrumbItem href="#repo">neba</BreadcrumbItem>
<BreadcrumbItem href="#branch">main</BreadcrumbItem>
<BreadcrumbItem>Button.tsx</BreadcrumbItem>
</Breadcrumb>
{/* Two kept at the front, two at the end. */}
<Breadcrumb maxItems={4} itemsBeforeCollapse={2} itemsAfterCollapse={2} label="Wider ends">
<BreadcrumbItem href="#home">Home</BreadcrumbItem>
<BreadcrumbItem href="#org">Acme</BreadcrumbItem>
<BreadcrumbItem href="#team">Platform</BreadcrumbItem>
<BreadcrumbItem href="#repo">neba</BreadcrumbItem>
<BreadcrumbItem href="#branch">main</BreadcrumbItem>
<BreadcrumbItem>Button.tsx</BreadcrumbItem>
</Breadcrumb>
</div>
);
}현재 단계
마지막 단계는 지금 보고 있는 페이지이므로, href를 주더라도 링크가 되지 않습니다. 앞쪽 단계에 current를 붙이면 그 표시가 옮겨 가고, 마지막 단계에서는 걷힙니다.
startIcon
import { Breadcrumb, BreadcrumbItem } from 'neba';
function HomeIcon() {
return (
<svg viewBox="0 0 16 16" fill="none">
<path
d="M2.5 7 8 2.5 13.5 7v6a.5.5 0 0 1-.5.5h-3v-4H6v4H3a.5.5 0 0 1-.5-.5V7Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinejoin="round"
/>
</svg>
);
}
function FileIcon() {
return (
<svg viewBox="0 0 16 16" fill="none">
<path d="M4 2h5l3 3v9H4V2Z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
<path d="M9 2v3h3" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
</svg>
);
}
export default function BreadcrumbIcons() {
return (
<Breadcrumb size="lg">
<BreadcrumbItem href="#home" startIcon={<HomeIcon />}>
Home
</BreadcrumbItem>
<BreadcrumbItem href="#src">src</BreadcrumbItem>
<BreadcrumbItem startIcon={<FileIcon />}>Button.tsx</BreadcrumbItem>
</Breadcrumb>
);
}size
import { Breadcrumb, BreadcrumbItem } from 'neba';
export default function BreadcrumbSizes() {
return (
<div className="flex flex-col gap-3">
{(['xs', 'sm', 'md', 'lg', 'xl'] as const).map((size) => (
<Breadcrumb key={size} size={size} label={size}>
<BreadcrumbItem href="#home">Home</BreadcrumbItem>
<BreadcrumbItem href="#projects">Projects</BreadcrumbItem>
<BreadcrumbItem>{size}</BreadcrumbItem>
</Breadcrumb>
))}
</div>
);
}structuredData
검색 결과 아래에 경로가 표시되려면 마크업만으로는 부족하고 구조화 데이터가 필요합니다. structuredData를 켜면 schema.org의 BreadcrumbList가 <script type="application/ld+json">으로 함께 나갑니다. baseUrl은 상대 href를 절대 URL로 만드는 기준입니다. 검색엔진은 절대 URL을 원합니다.
maxItems로 접힌 단계도 모두 들어갑니다. 무엇을 접을지는 줄에 자리가 얼마나 있느냐의 문제이고, 경로는 어느 쪽이든 같은 경로이기 때문입니다. href가 없는 단계는 item 없이 나가는데, 마지막 단계가 대개 그렇습니다.
기본값은 꺼짐입니다. 한 페이지에 BreadcrumbList는 하나여야 하고, 이미 SEO 레이어에서 직접 내보내는 앱이 많습니다.
import { Breadcrumb, BreadcrumbItem } from 'neba';
/**
* The preview looks exactly like any other trail, which is the point: what
* `structuredData` adds is a `<script type="application/ld+json">` beside the
* markup, drawn for nobody and read by a crawler. Open the inspector to see it.
*/
export default function BreadcrumbStructuredData() {
return (
<Breadcrumb structuredData baseUrl="https://neba.cdget.com">
<BreadcrumbItem href="/">Home</BreadcrumbItem>
<BreadcrumbItem href="/components/">Components</BreadcrumbItem>
<BreadcrumbItem href="/components/display/">Display</BreadcrumbItem>
<BreadcrumbItem>Breadcrumb</BreadcrumbItem>
</Breadcrumb>
);
}접근성
- 트레일은
label이 이름을 주는nav이며(기본값Breadcrumb), 그 안에 순서 있는 리스트가 들어갑니다. - 현재 단계에는
aria-current="page"가 붙고, 한 트레일에서 이것을 가지는 단계는 언제나 하나뿐입니다. - 구분자는
aria-hidden이므로 screen reader는 단계만 읽고 사이의 기호는 읽지 않습니다. …는expandLabel이 이름을 주는 실제 버튼입니다.expandable={false}이면 표시일 뿐이며 reader에게는 감춰집니다.- nav 이름과
…버튼의 이름을locale이 정합니다.label과expandLabel로 직접 쓸 수도 있습니다. structuredData는 접근성과는 무관합니다. 크롤러가 읽는 것이고, 화면에는 아무것도 그리지 않습니다.