Breadcrumb
현재 위치의 계층 경로를 보여주고 상위로 돌아가게 합니다. 3단계 이상 깊은 상세 화면에 씁니다.
1. Default
nav > ol. 마지막 항목은 링크 없이 aria-current=page 이고, 라벨은 span 으로 감쌉니다 — 220px 를 넘으면 말줄임.
HTML
<nav aria-label="현재 위치">
<ol class="breadcrumb">
<li><a href="#">정책</a></li>
<li><a href="#">USB 제어</a></li>
<li aria-current="page"><span>외부 저장장치 차단</span></li>
</ol>
</nav>CSS · .breadcrumb
/* JS Design System v0.6 · .breadcrumb · 토큰(var(--*))은 assets/style.css :root 참고 */
.breadcrumb{display:flex;align-items:center;gap:6px;font-size:14px;list-style:none;padding:0;margin:0;flex-wrap:wrap}
.breadcrumb li{display:flex;align-items:center;gap:6px;color:var(--text-tertiary);margin:0}
.breadcrumb li+li::before{content:"";width:12px;height:12px;flex:none;background:var(--text-disabled);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M17 5l-10 14%22 /%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M17 5l-10 14%22 /%3E%3C/svg%3E") center/contain no-repeat}
.breadcrumb a{color:inherit;text-decoration:none}
.breadcrumb a:hover{color:var(--text-primary);text-decoration:underline}
.breadcrumb li[aria-current]{color:var(--text-primary);font-weight:600}
.breadcrumb .more{padding:0 4px;color:var(--text-tertiary);cursor:pointer;border:0;background:none;font:inherit}
.breadcrumb .more:hover{color:var(--text-primary)}
.breadcrumb .more svg{width:14px;height:14px;display:block}
.breadcrumb li>a:not(.icon),.breadcrumb li>span{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.breadcrumb li>a.icon,.breadcrumb .more{position:relative}
.breadcrumb li>a.icon::after,.breadcrumb .more::after{content:"";position:absolute;top:50%;left:50%;width:100%;height:24px;min-width:24px;transform:translate(-50%,-50%)}React · @jiran/ds-react
import { Breadcrumb, BreadcrumbItem } from "@jiran/ds-react";
export default function Example() {
return (
<Breadcrumb>
<BreadcrumbItem href="#">정책</BreadcrumbItem>
<BreadcrumbItem href="#">USB 제어</BreadcrumbItem>
<BreadcrumbItem current>외부 저장장치 차단</BreadcrumbItem>
</Breadcrumb>
);
}2. Collapsed
가운데가 2개 이상일 때(5단계 이상) 접습니다. 첫 항목과 끝 2개는 남기고 가운데만 … 로 묶으며, 누르면 펼쳐집니다.
HTML
<nav aria-label="현재 위치">
<ol class="breadcrumb">
<li><a href="#" class="icon" aria-label="홈"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12l-2 0l9 -9l9 9l-2 0"/><path d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7"/><path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6"/></svg></a></li>
<li><button type="button" class="more" aria-label="상위 경로 펼치기" data-items="정책|USB 제어"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M11 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/><path d="M18 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/></svg></button></li>
<li><a href="#">PC-2041</a></li>
<li aria-current="page"><span>이벤트 #48213</span></li>
</ol>
</nav>CSS · .breadcrumb
/* JS Design System v0.6 · .breadcrumb · 토큰(var(--*))은 assets/style.css :root 참고 */
.breadcrumb{display:flex;align-items:center;gap:6px;font-size:14px;list-style:none;padding:0;margin:0;flex-wrap:wrap}
.breadcrumb li{display:flex;align-items:center;gap:6px;color:var(--text-tertiary);margin:0}
.breadcrumb li+li::before{content:"";width:12px;height:12px;flex:none;background:var(--text-disabled);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M17 5l-10 14%22 /%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M17 5l-10 14%22 /%3E%3C/svg%3E") center/contain no-repeat}
.breadcrumb a{color:inherit;text-decoration:none}
.breadcrumb a:hover{color:var(--text-primary);text-decoration:underline}
.breadcrumb li[aria-current]{color:var(--text-primary);font-weight:600}
.breadcrumb .more{padding:0 4px;color:var(--text-tertiary);cursor:pointer;border:0;background:none;font:inherit}
.breadcrumb .more:hover{color:var(--text-primary)}
.breadcrumb .more svg{width:14px;height:14px;display:block}
.breadcrumb li>a:not(.icon),.breadcrumb li>span{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.breadcrumb li>a.icon,.breadcrumb .more{position:relative}
.breadcrumb li>a.icon::after,.breadcrumb .more::after{content:"";position:absolute;top:50%;left:50%;width:100%;height:24px;min-width:24px;transform:translate(-50%,-50%)}React · @jiran/ds-react
import { useEffect, useRef, useState } from "react";
import { Breadcrumb, BreadcrumbItem, BreadcrumbMore, Icon } from "@jiran/ds-react";
const HIDDEN = ["정책", "USB 제어"];
export default function Example() {
const [open, setOpen] = useState(false);
const nav = useRef<HTMLElement>(null);
/* … 버튼이 사라지므로 펼친 첫 링크로 포커스를 옮긴다 — 안 옮기면 키보드 사용자가 위치를 잃는다 */
useEffect(() => {
if (open) nav.current?.querySelector<HTMLAnchorElement>("li:nth-child(2) a")?.focus();
}, [open]);
return (
<Breadcrumb ref={nav}>
<BreadcrumbItem href="#" linkProps={{ className: "icon", "aria-label": "홈" }}><Icon name="home" /></BreadcrumbItem>
{open
? HIDDEN.map((t) => <BreadcrumbItem key={t} href="#">{t}</BreadcrumbItem>)
: <BreadcrumbMore onClick={() => setOpen(true)} />}
<BreadcrumbItem href="#">PC-2041</BreadcrumbItem>
<BreadcrumbItem current>이벤트 #48213</BreadcrumbItem>
</Breadcrumb>
);
}Props
HTML 에서는 클래스와 속성으로, React(@jiran/ds-react)에서는 같은 이름의 prop(variant · size …)으로 변형·상태를 지정합니다.
| Prop / Class | Type · Values | Default | Description |
|---|---|---|---|
| .breadcrumb | ol | — | nav[aria-label] 안의 ol. React Breadcrumb 의 label 이 그 aria-label |
| li a | link | — | 상위 경로 링크(React BreadcrumbItem href). 220px 를 넘으면 말줄임 — 전체 문구는 title 로 함께 |
| li[aria-current=page] > span | current | — | 현재 페이지. 링크 없이 Semibold. 라벨을 span 으로 감싸야 말줄임이 걸립니다(li 가 flex) — React current 가 자동으로 감쌉니다 |
| a.icon | boolean | false | 홈 아이콘처럼 아이콘만 있는 링크. aria-label 필수 — 없으면 이름 없는 링크로 읽힙니다. 클릭 영역은 24×24 |
| .more | button | — | 접힌 경로 펼치기(React BreadcrumbMore). 클릭 영역 24×24 |
| .more[data-items] | attribute | — | 필수 — 접은 경로를 | 로 이어 둡니다("정책|USB 제어"). 누르면 그 자리에 링크로 펼치고 첫 링크로 포커스를 옮기며 ds:expand 를 발행합니다. 비어 있으면 아무 일도 하지 않습니다 — ds.js 담당. React 는 BreadcrumbMore 의 onClick 으로 직접 처리 |
| .breadcrumb li a svg | element | — | 링크 앞 아이콘. 홈 아이콘 18 · .more 아이콘 14 |
Usage
React: @jiran/ds-react 패키지를 설치하고 style.css 를 한 번 import 하면 위 React 탭 코드를 그대로 쓸 수 있습니다(React Package). HTML: assets/style.css 를 연결하고(일부만 필요하면 아래 컴포넌트 CSS 복사), 동작이 필요하면 assets/ds.js 를 함께 넣습니다 — 이 페이지의 프리뷰가 그 스크립트로 동작합니다. 색은 전부 토큰(var(--accent) 등)을 참조하므로 --accent-* 만 제품 컬러로 바꾸면 됩니다.
React @jiran/ds-react
npm i @jiran/ds-react
// main.tsx — 앱 진입점에서 한 번
import "@jiran/ds-react/style.css";
// 컴포넌트
import { Breadcrumb, BreadcrumbItem, BreadcrumbMore } from "@jiran/ds-react";HTML
<!-- 1) 토큰 + 전체 컴포넌트 스타일 한 번에 연결 -->
<link rel="stylesheet" href="assets/style.css">
<!-- 동작(드롭다운·탭·달력·표 정렬/선택·토스트 등) — 순수 JS 한 파일. React 제품은 불필요 -->
<script src="assets/ds.js" defer></script>
<!-- 2) 또는 아래 "Component CSS" 만 프로젝트 CSS 에 붙이고,
:root 토큰은 Foundations › Design Token 에서 복사 -->Component CSS .breadcrumb
/* JS Design System v0.6 · .breadcrumb · 토큰(var(--*))은 assets/style.css :root 참고 */
.breadcrumb{display:flex;align-items:center;gap:6px;font-size:14px;list-style:none;padding:0;margin:0;flex-wrap:wrap}
.breadcrumb li{display:flex;align-items:center;gap:6px;color:var(--text-tertiary);margin:0}
.breadcrumb li+li::before{content:"";width:12px;height:12px;flex:none;background:var(--text-disabled);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M17 5l-10 14%22 /%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2224%22 height=%2224%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22black%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22 %3E%3Cpath d=%22M17 5l-10 14%22 /%3E%3C/svg%3E") center/contain no-repeat}
.breadcrumb a{color:inherit;text-decoration:none}
.breadcrumb a:hover{color:var(--text-primary);text-decoration:underline}
.breadcrumb li[aria-current]{color:var(--text-primary);font-weight:600}
.breadcrumb .more{padding:0 4px;color:var(--text-tertiary);cursor:pointer;border:0;background:none;font:inherit}
.breadcrumb .more:hover{color:var(--text-primary)}
.breadcrumb .more svg{width:14px;height:14px;display:block}
.breadcrumb li>a:not(.icon),.breadcrumb li>span{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.breadcrumb li>a.icon,.breadcrumb .more{position:relative}
.breadcrumb li>a.icon::after,.breadcrumb .more::after{content:"";position:absolute;top:50%;left:50%;width:100%;height:24px;min-width:24px;transform:translate(-50%,-50%)}