Navigation 콘솔 좌측의 1차 내비게이션입니다. 그룹 라벨로 메뉴를 묶고 현재 위치를 .on 으로 표시합니다. 폭 240, 접으면 64.
1. Default 그룹 라벨(.group) + 링크. 아이콘 18px, 건수는 .count. 하위 항목은 .sub.
HTML
< nav class = "sidenav" aria-label = "주 메뉴" style = "border:1px solid var(--border-default);border-radius:var(--radius-lg)" >
< a href = "#" class = "on" aria-current = "page" > < 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 > < span > 대시보드</ span > </ a >
< a href = "#" > < 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 = "M9 6l11 0" /> < path d = "M9 12l11 0" /> < path d = "M9 18l11 0" /> < path d = "M5 6l0 .01" /> < path d = "M5 12l0 .01" /> < path d = "M5 18l0 .01" /> </ svg > < span > 이벤트</ span > < span class = "count" > 12</ span > </ a >
< div class = "group" > 보호</ div >
< a href = "#" > < 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 = "M12 3a12 12 0 0 0 8.5 3a12 12 0 0 1 -8.5 15a12 12 0 0 1 -8.5 -15a12 12 0 0 0 8.5 -3" /> </ svg > < span > 정책</ span > </ a >
< a href = "#" class = "sub" > USB 제어</ a >
< a href = "#" class = "sub" > 네트워크</ a >
< a href = "#" > < 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 = "M3 7a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3v-2" /> < path d = "M3 15a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3l0 -2" /> < path d = "M7 8l0 .01" /> < path d = "M7 16l0 .01" /> </ svg > < span > 에이전트</ span > </ a >
< div class = "group" > 관리</ div >
< a href = "#" > < 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 = "M8 7a4 4 0 1 0 8 0a4 4 0 0 0 -8 0" /> < path d = "M6 21v-2a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v2" /> </ svg > < span > 사용자</ span > </ a >
< a href = "#" > < 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 = "M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065" /> < path d = "M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0" /> </ svg > < span > 설정</ span > </ a >
< div class = "foot" > v2.4.1 · 라이선스 정상</ div >
</ nav > CSS · .sidenav, .count CSS 복사
/* JS Design System v0.6 · .sidenav, .count · 토큰(var(--*))은 assets/style.css :root 참고 */
.tabs .tab .count { font-size : 12px ; font-weight : 700 ; padding : 1px 6px ; border-radius : var(--radius-full) ; background : var(--bg-panel) ; color : var(--text-secondary) ; min-width : 0 ; height : auto }
.tabs .tab.on .count { background : var(--accent-subtle) ; color : var(--accent-text) }
.sidenav { width : 240px ; padding : 12px ; background : var(--bg-surface) ; border-right : 1px solid var(--border-default) ; display : flex ; flex-direction : column ; gap : 2px ; font-size : 14px ; min-height : 320px }
.sidenav .group { margin : 12px 12px 4px ; font-size : 12px ; letter-spacing : .06em ; text-transform : uppercase ; color : var(--text-tertiary) ; font-weight : 600 }
.sidenav a { display : flex ; align-items : center ; font-weight : 500 ; gap : 10px ; padding : 8px 12px ; border-radius : var(--radius-md) ; text-decoration : none ; color : var(--text-primary) ; white-space : nowrap }
.sidenav a:hover { background : var(--bg-subtle) }
.sidenav a.on { background : var(--accent-subtle) ; color : var(--accent-text) ; font-weight : 600 }
.sidenav a svg { width : 18px ; height : 18px ; flex : none ; color : var(--text-tertiary) }
.sidenav a.on svg { color : var(--accent-text) }
.sidenav a .count { margin-left : auto }
.sidenav a.sub { padding-left : 40px ; font-size : 14px ; color : var(--text-secondary) }
.sidenav.collapsed { width : 64px ; padding : 12px 10px }
.sidenav.collapsed a { justify-content : center ; padding : 10px 0 }
.sidenav.collapsed a span,.sidenav.collapsed .group,.sidenav.collapsed a .count { display : none }
.sidenav .foot { margin-top : auto ; padding : 12px ; font-size : 12px ; color : var(--text-tertiary) }
.count { display : inline-grid ; place-items : center ; min-width : 20px ; height : 20px ; padding : 0 6px ; border-radius : var(--radius-full) ; background : var(--danger) ; color : var(--danger-on) ; font-size : 12px ; font-weight : 700 ; line-height : 1 ; }
.count.neutral { background : var(--bg-panel) ; color : var(--text-secondary) }
.count.accent { background : var(--accent) ; color : var(--accent-on) }
.count.dot { min-width : 8px ; width : 8px ; height : 8px ; padding : 0 }
.with-count .count { position : absolute ; top : -6px ; right : -6px }
React · @jiran/ds-react
import { SideNav, SideNavGroup, SideNavItem } from "@jiran/ds-react" ;
export default function Example() {
return (
< SideNav footer ="v2.4.1 · 라이선스 정상" style ={{ border: "1px solid var(--border-default)" , borderRadius: "var(--radius-lg)" }}>
< SideNavItem href ="#" icon ="home" active> 대시보드</ SideNavItem >
< SideNavItem href ="#" icon ="list" count ={12}> 이벤트</ SideNavItem >
< SideNavGroup > 보호</ SideNavGroup >
< SideNavItem href ="#" icon ="shield" > 정책</ SideNavItem >
< SideNavItem href ="#" sub> USB 제어</ SideNavItem >
< SideNavItem href ="#" sub> 네트워크</ SideNavItem >
< SideNavItem href ="#" icon ="server" > 에이전트</ SideNavItem >
< SideNavGroup > 관리</ SideNavGroup >
< SideNavItem href ="#" icon ="user" > 사용자</ SideNavItem >
< SideNavItem href ="#" icon ="settings" > 설정</ SideNavItem >
</ SideNav >
);
}
2. Collapsed 1280 이하 또는 사용자가 접었을 때. 아이콘만, 라벨은 툴팁으로.
HTML
< nav class = "sidenav collapsed" aria-label = "주 메뉴" style = "border:1px solid var(--border-default);border-radius:var(--radius-lg)" >
< a href = "#" class = "on" aria-current = "page" title = "대시보드" > < 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 > < span > 대시보드</ span > </ a >
< a href = "#" title = "이벤트" > < 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 = "M9 6l11 0" /> < path d = "M9 12l11 0" /> < path d = "M9 18l11 0" /> < path d = "M5 6l0 .01" /> < path d = "M5 12l0 .01" /> < path d = "M5 18l0 .01" /> </ svg > < span > 이벤트</ span > </ a >
< a href = "#" title = "정책" > < 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 = "M12 3a12 12 0 0 0 8.5 3a12 12 0 0 1 -8.5 15a12 12 0 0 1 -8.5 -15a12 12 0 0 0 8.5 -3" /> </ svg > < span > 정책</ span > </ a >
< a href = "#" title = "에이전트" > < 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 = "M3 7a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3v-2" /> < path d = "M3 15a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3l0 -2" /> < path d = "M7 8l0 .01" /> < path d = "M7 16l0 .01" /> </ svg > < span > 에이전트</ span > </ a >
< a href = "#" title = "설정" > < 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 = "M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065" /> < path d = "M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0" /> </ svg > < span > 설정</ span > </ a >
</ nav > CSS · .sidenav, .count CSS 복사
/* JS Design System v0.6 · .sidenav, .count · 토큰(var(--*))은 assets/style.css :root 참고 */
.tabs .tab .count { font-size : 12px ; font-weight : 700 ; padding : 1px 6px ; border-radius : var(--radius-full) ; background : var(--bg-panel) ; color : var(--text-secondary) ; min-width : 0 ; height : auto }
.tabs .tab.on .count { background : var(--accent-subtle) ; color : var(--accent-text) }
.sidenav { width : 240px ; padding : 12px ; background : var(--bg-surface) ; border-right : 1px solid var(--border-default) ; display : flex ; flex-direction : column ; gap : 2px ; font-size : 14px ; min-height : 320px }
.sidenav .group { margin : 12px 12px 4px ; font-size : 12px ; letter-spacing : .06em ; text-transform : uppercase ; color : var(--text-tertiary) ; font-weight : 600 }
.sidenav a { display : flex ; align-items : center ; font-weight : 500 ; gap : 10px ; padding : 8px 12px ; border-radius : var(--radius-md) ; text-decoration : none ; color : var(--text-primary) ; white-space : nowrap }
.sidenav a:hover { background : var(--bg-subtle) }
.sidenav a.on { background : var(--accent-subtle) ; color : var(--accent-text) ; font-weight : 600 }
.sidenav a svg { width : 18px ; height : 18px ; flex : none ; color : var(--text-tertiary) }
.sidenav a.on svg { color : var(--accent-text) }
.sidenav a .count { margin-left : auto }
.sidenav a.sub { padding-left : 40px ; font-size : 14px ; color : var(--text-secondary) }
.sidenav.collapsed { width : 64px ; padding : 12px 10px }
.sidenav.collapsed a { justify-content : center ; padding : 10px 0 }
.sidenav.collapsed a span,.sidenav.collapsed .group,.sidenav.collapsed a .count { display : none }
.sidenav .foot { margin-top : auto ; padding : 12px ; font-size : 12px ; color : var(--text-tertiary) }
.count { display : inline-grid ; place-items : center ; min-width : 20px ; height : 20px ; padding : 0 6px ; border-radius : var(--radius-full) ; background : var(--danger) ; color : var(--danger-on) ; font-size : 12px ; font-weight : 700 ; line-height : 1 ; }
.count.neutral { background : var(--bg-panel) ; color : var(--text-secondary) }
.count.accent { background : var(--accent) ; color : var(--accent-on) }
.count.dot { min-width : 8px ; width : 8px ; height : 8px ; padding : 0 }
.with-count .count { position : absolute ; top : -6px ; right : -6px }
React · @jiran/ds-react
import { SideNav, SideNavItem } from "@jiran/ds-react" ;
export default function Example() {
return (
< SideNav collapsed style ={{ border: "1px solid var(--border-default)" , borderRadius: "var(--radius-lg)" }}>
< SideNavItem href ="#" icon ="home" active> 대시보드</ SideNavItem >
< SideNavItem href ="#" icon ="list" > 이벤트</ SideNavItem >
< SideNavItem href ="#" icon ="shield" > 정책</ SideNavItem >
< SideNavItem href ="#" icon ="server" > 에이전트</ SideNavItem >
< SideNavItem href ="#" icon ="settings" > 설정</ SideNavItem >
</ SideNav >
);
}
Props HTML 에서는 클래스와 속성으로, React(@jiran/ds-react)에서는 같은 이름의 prop(variant · size …)으로 변형·상태를 지정합니다.
Prop / Class Type · Values Default Description .sidenav nav—폭 240, 세로 링크 목록 .group div—그룹 라벨(uppercase caption) a.on current—현재 메뉴(accent subtle) a.sub child—하위 항목(들여쓰기) .collapsed booleanfalse폭 64, 아이콘만 .count element—미확인 건수
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 { SideNav, SideNavGroup, SideNavItem } 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 .sidenav, .count CSS 복사 /* JS Design System v0.6 · .sidenav, .count · 토큰(var(--*))은 assets/style.css :root 참고 */
.tabs .tab .count { font-size : 12px ; font-weight : 700 ; padding : 1px 6px ; border-radius : var(--radius-full) ; background : var(--bg-panel) ; color : var(--text-secondary) ; min-width : 0 ; height : auto }
.tabs .tab.on .count { background : var(--accent-subtle) ; color : var(--accent-text) }
.sidenav { width : 240px ; padding : 12px ; background : var(--bg-surface) ; border-right : 1px solid var(--border-default) ; display : flex ; flex-direction : column ; gap : 2px ; font-size : 14px ; min-height : 320px }
.sidenav .group { margin : 12px 12px 4px ; font-size : 12px ; letter-spacing : .06em ; text-transform : uppercase ; color : var(--text-tertiary) ; font-weight : 600 }
.sidenav a { display : flex ; align-items : center ; font-weight : 500 ; gap : 10px ; padding : 8px 12px ; border-radius : var(--radius-md) ; text-decoration : none ; color : var(--text-primary) ; white-space : nowrap }
.sidenav a:hover { background : var(--bg-subtle) }
.sidenav a.on { background : var(--accent-subtle) ; color : var(--accent-text) ; font-weight : 600 }
.sidenav a svg { width : 18px ; height : 18px ; flex : none ; color : var(--text-tertiary) }
.sidenav a.on svg { color : var(--accent-text) }
.sidenav a .count { margin-left : auto }
.sidenav a.sub { padding-left : 40px ; font-size : 14px ; color : var(--text-secondary) }
.sidenav.collapsed { width : 64px ; padding : 12px 10px }
.sidenav.collapsed a { justify-content : center ; padding : 10px 0 }
.sidenav.collapsed a span,.sidenav.collapsed .group,.sidenav.collapsed a .count { display : none }
.sidenav .foot { margin-top : auto ; padding : 12px ; font-size : 12px ; color : var(--text-tertiary) }
.count { display : inline-grid ; place-items : center ; min-width : 20px ; height : 20px ; padding : 0 6px ; border-radius : var(--radius-full) ; background : var(--danger) ; color : var(--danger-on) ; font-size : 12px ; font-weight : 700 ; line-height : 1 ; }
.count.neutral { background : var(--bg-panel) ; color : var(--text-secondary) }
.count.accent { background : var(--accent) ; color : var(--accent-on) }
.count.dot { min-width : 8px ; width : 8px ; height : 8px ; padding : 0 }
.with-count .count { position : absolute ; top : -6px ; right : -6px }