LNB 사이드바 (LnbSidebar)
좌측 내비게이션의 펼침·접힘과 두 가지 사용 패턴을 확인하고, 사용 기준과 호출 방법을 찾아보세요.
Alpine 컴포넌트 · Web · 진행 중(WIP) · 문서 포맷 시범 적용
Interactive playground
.ds-lnb* 클래스로 렌더해요 — 메뉴를 눌러 활성 전환을, 아코디언 부모를 눌러 하위 메뉴를 열어 보세요. 접힘 레일에서는 메뉴에 마우스를 올리면 플라이아웃과 툴팁이 뜹니다.id 없는 아이템은 onSelect 를 발생시키지 않습니다 — 자식 토글 전용이에요. <!-- 루트는 --expanded / --collapsed 중 정확히 하나를 항상 달아야 한다.
폭(208px / 68px)은 그 두 클래스에만 정의돼 있고 .ds-lnb 자체엔 없다 -->
<aside class="ds-lnb ds-lnb--expanded" x-data="lnbSidebar({
getConfig: () => LNB_CONFIG, // { sections: [{ label?, items }] }
getActive: () => activeMenu,
onSelect: (id) => navigate(id),
isCollapsed: () => lnbCollapsed,
onToggleCollapse: () => {
if (window.innerWidth < 768) return; // 모바일 가드 — 없으면 드로어가 레일로 렌더된다
lnbCollapsed = lnbPrefCollapsed = !lnbCollapsed;
},
})">
<!-- include "../../packages/components/html/lnb-sidebar.html" -->
</aside>펼침 — .ds-lnb--expanded · 208px
접힘 레일 — .ds-lnb--collapsed · 68px (메뉴에 hover 하면 플라이아웃)
패널 모달 내비게이션 — LNB 가 아니라 별도 컴포넌트
선택 섹션: 연장근무 유형— 밝은 배경 · 플랫(단일 depth) · active는 bg-white. 다크 레일(.ds-lnb)과 달리 페이지가 아니라 모달 우측 현황을 전환합니다.
--expanded / --collapsed 두 클래스에만 정의돼 있습니다 — .ds-lnb 자체에는 폭이 없어서, 둘 중 하나가 빠지면 폭이 사라집니다. 반응형 3단(<768 드로어 · 768–1279 강제 레일 · ≥1280 사용자 설정)은 컴포넌트가 아니라 앱이 구현합니다 — 여기서는 재현되지 않아요. 맨 아래 브랜드 마크도 partial 이 아니라 앱이 넣는 것이고, 접히면 락업 대신 심볼로 바뀝니다. 실제 앱은 메뉴가 세로로 다 안 들어가면 이 마크를 숨기는데(숨김 판정), 그 판정도 앱 몫이라 이 데모는 높이와 상관없이 늘 보여 줍니다. 종류(LNB · 패널 모달 내비) · 접힘 레일 · 접기 버튼을 바꾸면 실제 .ds-lnb* 클래스로 렌더된 사이드바와 호출 코드가 함께 바뀝니다. 대표 상태 세 가지는 「모아보기」 탭에 있습니다.
규칙 (강제) · 코딩 에이전트용
LNB 강제 규칙. 상세·미리보기는 아래 본문.
마크업 / 구성
- 템플릿은 빌드타임 include(
<!-- include "../../packages/components/html/lnb-sidebar.html" -->)로 넣습니다..ds-lnb__menu마크업을 손으로 짓지 마세요. - 루트
<aside class="ds-lnb">는ds-lnb--expanded/ds-lnb--collapsed중 정확히 하나를 항상 달고 있어야 합니다 — 폭(208px / 68px)은 그 두 클래스 블록에만 정의돼 있어요(design-system.css의.ds-lnb--expanded/.ds-lnb--collapsed)..ds-lnb자체엔 폭이 없습니다. - 메뉴는
LnbConfig = { sections: [{ label?, items }] }config로만 구성합니다(LnbSidebar.ts의LnbConfig/LnbSection/LnbItem타입). id없는 아이템은onSelect를 절대 발생시키지 않습니다 — 자식 토글 전용이에요(handleItemClick()). 아코디언 부모 그룹이 그렇습니다.- 렌더는 평평한
entries리스트 +display:contents래퍼입니다(get entries()). Alpine 3.14에서 중첩x-for가 깨져서 일부러 이렇게 한 거예요(파일 헤더 주석에 이유 있음) — 중첩x-for로 "정리"하지 마세요. - 아이콘은
getIcon레지스트리에서 옵니다(iconFor()). 렌더 박스는 18px(design-system.css의.ds-lnb__menu-icon).
반응형 3단 (⚠️ 컴포넌트가 아니라 앱이 구현합니다)
- 기준 앱은 admin(
dashboard.html의syncLnbViewport()): <768 드로어 / 768–1279 강제 레일(68px) / ≥1280 사용자 설정 복원. - 상태 플래그 3개:
lnbCollapsed·lnbDrawerOpen·lnbPrefCollapsed. 접기 버튼은lnbCollapsed와lnbPrefCollapsed를 둘 다 써야 해요 — 안 그러면 리사이즈 때 PC 설정이 날아갑니다. - 새 breakpoint를 만들지 마세요 —
md(768) ·xl(1280)만 씁니다. breakpoints 참고.
하단 브랜드 마크 .ds-lnb__brandmark (⚠️ 이것도 앱이 구현합니다)
- LNB 맨 아래에
hereby를 깔아 두는 자리입니다(2026-08, PR #591). 헤더 심볼 자리를 병원 로고가 가져가도 브랜드가 화면에서 사라지지 않게 하는 것이라, 로고를 등록하지 않은 조직에도 뜹니다. - CSS 는 공용(
design-system.css)이지만 마크업을 넣는 앱은 admin 하나입니다 —lnb-sidebar.htmlpartial 안이 아니라 호스트의<aside>마지막 자식으로 둡니다. - 자산은 영문 outline 락업(
logo-lockup-hereby-medical-outline-on-dark)이고, 접힘 레일(안쪽 폭 44px)에서는 같은 outline 심볼(logo-hereby-medical-outline-on-dark)로 바꿔 낍니다. 높이 18px 는 브랜드 규칙의 락업 최소 높이 16px 바로 위입니다 — 그 아래로 내리지 말고, 더 작아야 하면 크기를 낮추는 게 아니라 워드마크를 떼고 심볼만 쓰세요. - 장식이므로
aria-hidden="true"를 달고, CSS 가pointer-events: none으로 클릭·포커스에서 뺍니다. 링크로 만들지 마세요. - ⚠️ 숨김 판정은 반드시 「보이는 레이아웃」에서 재세요. 메뉴가 세로로 다 안 들어가면 마크를 숨겨 자리를 돌려주는데(admin
dashboard.html의syncLnbBrandmark()), 숨긴 채로 재면scrollHeight가clientHeight로 잘려 「다시 보여도 된다」는 조건이 영원히 성립하지 않습니다 — 한 번 숨은 마크가 안 돌아옵니다.is-hidden을 먼저 벗기고 재세요. 레이아웃이 아직 안 잡힌 순간(clientHeight === 0)에는 판단을 미룹니다(보이는 쪽이 안전).
⚠️ 트랩 — PR #430에서 실제로 터졌고, 되돌리면 재발합니다
- 드로어 닫기는
onSelect안에서만 하세요..ds-lnb__top(또는.ds-lnb) 컨테이너에@click="lnbDrawerOpen = false"를 걸면 안쪽 탭에도 발화해서,id없는 부모 그룹(예: "문서 결재")을 누르는 순간 하위 메뉴가 열리기도 전에 드로어가 닫힙니다. 커밋c49f31fe가 정확히 이걸 제거했어요 — 다시 넣지 마세요. onToggleCollapse에는 모바일 가드가 필수입니다:if (window.innerWidth < 768) return;. 없으면 드로어가 열린 채lnbCollapsed=true가 되어, 다시 연 드로어가 68px 데스크톱 레일로 렌더됩니다.- ⚠️
apps/general-affairs·apps/management-dashboard에는 이 가드가 없습니다. 다만 먼저 이식이 필요한지부터 확인하세요 — 두 앱은?embedded=1이면 LNB를 통째로 숨깁니다(embeddedMode). admin의embedRegistry가 두 앱을 품게 되면(현재는 team-manager·hr-system만 임베드) 그 LNB는 죽은 껍데기가 되므로 반응형 이식은 헛수고입니다. 두 앱이 독립 실행으로 계속 남고 + 모바일을 지원해야 할 때에만 admin의 반응형 LNB 4종 세트(뷰포트 동기화 · 모바일 가드 ·lnbPrefCollapsed보존 · chevron 숨김)를 통째로 이식하세요. 가드만 따로 넣지 마세요 — 드로어 없는 앱에서는 접기 버튼만 죽습니다.
- ⚠️
- 모바일에서 접기 chevron(
.ds-lnb__toggle-inline)은 숨겨야 합니다. 드로어는 항상 "펼침"이라, chevron이 보이면 사용자가 드로어를 레일로 접어버릴 수 있어요. onToggleCollapse를 주면isCollapsed도 같이 주세요(LnbSidebar.ts의isCollapsed?: () => booleanprop). 없으면 DOM 탐침으로 폴백해서 Alpine 한 틱 늦게 반응합니다.openFlyout()첫 줄의ev.stopPropagation()을 지우지 마세요 — 연 그 클릭이@click.outside로 바로 닫아버립니다.- 앱 CSS는
@import뒤라 소스 순서로 이미 이깁니다 —!important쓰지 마세요. - 다크 모드(2026-10-19) — LNB 는 늘 어두운 면이라 메뉴 · 하위 메뉴 · 화살표 · 접기 버튼의 흰 글자 · 선 · 선택 틴트는 정적 흰 잉크
--ds-ink-white-*(테마 불변)를 쓴다.--ds-glass-*는 모바일 유리 토큰이라 다크에서 어두워져 메뉴 글자가 사라졌다 — LNB 안에 쓰지 않는다. 접힘 플라이아웃 면은--color-bg-elevated, 접기 토글 면은--color-bg-container.
진행 중 (LnbSidebar component — work in progress)
- 좌측 네비게이션(LNB) 메뉴 컴포넌트. config 기반으로 섹션·메뉴·서브메뉴를 렌더링.
- 다크 배경(neutral-900), 글래스 톤 (
--ds-glass-*) 위주의 색 토큰 사용. - 토큰 fix 완료. 단, transition 120ms / 150ms, font-weight, 일부 비표준 rgba, 1px hairline 정책 결정 대기 — 검토 필요 사항 참고.
구조
- expanded 폭: 208px / collapsed 폭: 68px
- header — 브랜드/스위처 + (workspace 패턴) 접기 토글 우측 배치
- section —
section-head(라벨 + 구분선) + 메뉴 묶음 - menu — 단일 메뉴 (id 클릭) 또는 부모 메뉴 (children 토글)
- submenu — 부모 expand 시 노출 (들여쓰기 34px)
- bottom (profile + collapse) — 레거시 변형. workspace(admin)는 프로필을 PageHeader 아바타로, 토글을 헤더로 이동해 이 영역을 제거 (2026-06). 다른 앱 단독 화면은 유지
- brandmark — LNB 맨 아래
hereby브랜드 마크(.ds-lnb__brandmark, 2026-08). 위bottom과 다른 것입니다 — 프로필·토글이 있던 자리가 아니라 그 아래 장식 슬롯이고, 클릭 대상이 아닙니다. 지금 렌더하는 앱은 admin 하나 (아래 참고) - 메뉴 영역 스크롤바는 표시하지 않음 (
scrollbar-width: none) — 스크롤 동작은 유지
하단 브랜드 마크
LNB 맨 아래에 hereby 를 깔아 두는 장식 슬롯입니다(2026-08, PR #591). 헤더의 심볼 자리를 병원 로고가 가져갈 수 있게 되면서, 브랜드가 화면에서 아주 사라지지 않도록 만든 자리예요 — 그래서 로고를 등록하지 않은 조직에도 똑같이 뜹니다.
| 상태 | 자산 | 크기 |
|---|---|---|
| 펼침 (208px) | logo-lockup-hereby-medical-outline-on-dark (영문 outline 락업) | 높이 18px, 폭은 비율대로 |
| 접힘 레일 (68px) | logo-hereby-medical-outline-on-dark (같은 outline 심볼) | 높이 18px |
레일에서 자산이 바뀌는 것은 안쪽 폭이 44px 뿐이기 때문입니다. 락업은 비율 고정(865×130)이라 44px에 넣으면 높이가 규격 아래로 떨어져요.
18px 아래로 내리지 마세요
브랜드 규칙의 락업 최소 높이가 16px 이고 18px는 그 바로 위입니다. 더 작아야 하는 자리라면 크기를 낮추는 게 아니라 워드마크를 떼고 심볼만 씁니다. (이 하한은 2026-08-25에 24px → 16px로 내려간 값이고, 이 마크를 18px로 쓰기 위한 변경이었습니다.)
장식이라 클릭 대상이 아닙니다. 마크업에 aria-hidden="true"를 달고, CSS가 pointer-events: none으로 포커스·클릭에서 뺍니다(불투명도는 --ds-opacity-30). 링크로 만들지 마세요.
마크업은 앱이 넣습니다
CSS는 공용(design-system.css)이지만 lnb-sidebar.html partial 안에는 없습니다. 호스트가 <aside>의 마지막 자식으로 직접 둡니다 — 지금 렌더하는 앱은 admin 하나입니다(총무·경영은 ?embedded=1로 자기 LNB를 숨기므로 표기가 갈리지 않습니다).
<div class="ds-lnb__brandmark" aria-hidden="true"
x-html="$icon(lnbCollapsed ? 'logo-hereby-medical-outline-on-dark'
: 'logo-lockup-hereby-medical-outline-on-dark')"></div>숨김 판정 — 보이는 상태에서 재세요
메뉴가 세로로 다 들어가지 않는 화면(짧은 뷰포트·확대)에서는 마크를 숨겨 메뉴에 자리를 돌려줍니다. 판정도 앱 몫이고(admin dashboard.html의 syncLnbBrandmark()), 순서가 load-bearing입니다.
숨긴 채로 재면 마크가 영영 안 돌아옵니다
is-hidden은 display: none이라, 숨은 상태에서 재면 scrollHeight가 clientHeight로 잘려 「다시 보여도 된다」는 조건이 절대 성립하지 않습니다. 반드시 is-hidden을 먼저 벗기고 잰 뒤 다시 판정하세요. 레이아웃이 아직 안 잡힌 순간(clientHeight === 0)에는 판단을 미룹니다 — 보이는 쪽이 안전합니다.
메뉴 높이는 창 크기뿐 아니라 하위 메뉴 아코디언과 펼침/접힘으로도 바뀝니다. 그래서 리사이즈만 보지 말고 .ds-lnb__top 서브트리의 변화(x-show가 건드리는 style)까지 함께 봐야 합니다. 마크는 그 서브트리 밖이라 우리가 거는 is-hidden이 옵저버를 다시 깨우지는 않습니다.
패널 모달 내비게이션 (in-modal LNB)
패널 모달(.ds-modal--panel, 예: 근태 「유형·기준 통합 관리」)의 좌측 메뉴는 LNB 패턴을 모달 안으로 가져온 경량 변형입니다. 페이지 상단 Playground → 패널 모달 내비게이션에서 직접 조작할 수 있습니다.
다크 레일 컴포넌트(.ds-lnb, lnbSidebar())를 그대로 쓰는 게 아니라 밝은 배경의 인-모달 섹션 스위처입니다 — 하지만 "config로 항목을 정의하고, 정체성 아이콘을 붙이고, 하나만 active" 라는 LNB의 핵심 원칙은 동일합니다.
LNB와 무엇이 같고 다른가
| 항목 | 앱 LNB (.ds-lnb) | 패널 모달 좌측 메뉴 |
|---|---|---|
| 컴포넌트 | lnbSidebar() Alpine factory | 모달 내 인라인 마크업(type-manager.html) |
| 배경 | 다크(neutral-900) + 글래스 톤 | 밝음(bg-neutral-50) + 우측 1px 라인 |
| 항목 정의 | LnbConfig.sections[].items | typeManagerMenu({ id, label, icon }[]) |
| 아이콘 | 24px 아웃라인(ds-lnb__menu-icon) | 16px(.type-manager__nav-icon) |
| active 표현 | is-active (글래스 강조) | bg-white·neutral-900·600 |
| 계층 | section/부모/서브메뉴 · 접힘 레일 | 단일 depth(플랫), 접힘 없음 |
| 선택 결과 | 페이지/탭 전환 | 우측 콘텐츠(현황 표) 전환 |
공유 규칙 — 아이콘 일치: 패널 모달 좌측 메뉴의 각 항목 아이콘은 아이콘 사용 규칙과 같은 원칙을 따릅니다 — 그 섹션의 출처 페이지가 LNB·헤더에서 쓰던 정체성 아이콘을 그대로 재사용합니다(근무 설정=슬라이더, 연장근무=시계플러스, 외근=맵핀, 연차 부여 기준=달력체크, 휴가 유형=달력). 같은 화면을 가리키는 LNB·헤더·패널 메뉴의 아이콘은 항상 일치해야 합니다.
바로가기 — 좌측 메뉴가 팝업 안에서 어떻게 보이는지, 패널 전체 라이브 예시·규격은 → 모달 → 패널 모달
아이콘 사용 규칙
- 메뉴 아이콘은
@hereby/componentsicon registry(packages/components/src/icons.ts)의 24px 아웃라인 세트(스트로크 1.5)만 사용 - 레지스트리에 없는 이름은 조용히 사라집니다 —
getIcon()이 빈 문자열을 돌려주어 그 항목만 아이콘 없이 그려지고, 에러도 로그도 남지 않습니다.pnpm check:icons는.ts/.html/.vue만 훑으므로 문서·기획서에서 복사해 온 이름은 반드시 아이콘 갤러리에서 확인하고 씁니다 - 같은 LNB 안에서 아이콘 중복 금지 — 메뉴 식별이 목적이므로 항목마다 구분되는 통상적 의미의 아이콘 사용. 의미가 같은 메뉴(예: 섹션이 다른 두 "설정")만 동일 아이콘 허용
- 페이지 헤더 아이콘과 일치 — 같은 화면을 가리키는 LNB 항목·헤더 타이틀·embed 레지스트리의 아이콘은 동일해야 한다
- 2026-06 확장: 명찰(역할)·서식문서·표(시트)·시계(근태)·슬라이더(설정류)·달력시계(스케줄러)·달력체크(연차)·시계플러스(연장근무)·막대차트(현황)·태그(유형)·맵핀(외근)·게이지(대시보드)·목록체크(업무) 13종 추가
사용처
- admin (apps/admin/src/views/dashboard.html) — 4-group 계층 LNB
- general-affairs (apps/general-affairs/src/views/dashboard.html)
- management-dashboard (apps/management-dashboard/src/views/dashboard.html)
- 패널 모달 좌측 메뉴 (apps/admin/src/views/modals/type-manager.html) — LNB 패턴의 인-모달 변형 → 패널 모달 내비게이션
- 기타 LNB 사용 앱
States
| State | 표현 |
|---|---|
| default | color: var(--ds-glass-70) (희미한 흰색) |
| hover | background: rgba(255,255,255,0.06) (정확 토큰 부재), color: var(--ds-bw-white) |
| active | background: var(--ds-glass-10), color: var(--ds-bw-white) |
| child-active (collapsed) | 활성 하위를 가진 부모 아이콘에 active와 동일 강조 (is-child-active) — 하위가 숨겨지는 접힘 레일 전용 |
| disabled | color: var(--ds-glass-30), pointer-events: none |
| expanded parent | chevron 180° 회전 |
| collapsed sidebar | width 68px, menu-label · badge · section-label 숨김 |
접힘(collapsed) 레일 동작
접힘 레일은 라벨이 숨겨지므로 두 가지 보조 UI가 자동으로 동작한다 (별도 옵션 없음):
플라이아웃 — .ds-lnb__flyout
- 하위 메뉴가 있는 부모를 클릭하면 아이콘 우측에 하위 메뉴 그룹 표시
- 흰 바탕(
--ds-bw-white) + 1px 라인(--color-line-normal) +--radius-8+--shadow-popover, 행은 아이콘 + 타이틀(+배지) - 클릭한 버튼 좌표 기준 fixed 배치 — 레일의
overflow/스크롤에 잘리지 않음 (z-60) - 항목 선택·바깥 클릭·같은 부모 재클릭으로 닫힘. disabled 항목(준비중)은 흐림 + 클릭 불가
커스텀 툴팁 — .ds-lnb__tip
- 접힘 레일에서 메뉴 호버 시 아이콘 우측에 라벨 표시 (
neutral-900바탕 +--ds-bw-white텍스트 +--radius-4, z-70) - 네이티브
title미사용 — 네이티브 툴팁은 떠 있는 동안 코드로 지울 수 없어 플라이아웃을 가린다. 접근성은aria-label로 유지 - 클릭 즉시 숨김. 플라이아웃이 열려 있을 때 억제는 "열린 메뉴 자신"으로 한정 — 다른 메뉴의 툴팁은 그룹 위(z-70 > 60)로 표시
두 가지 사용 패턴
1. 메뉴만 사용 (가장 일반적)
<div x-data="lnbSidebar({
config: lnbConfig,
isActive: (id) => activeTab === id,
onSelect: (id) => switchTab(id),
})">
<!-- include "../../packages/components/html/lnb-sidebar.html" -->
</div>lnbConfig 예시:
const lnbConfig: LnbConfig = {
sections: [
{
label: "조직",
items: [
{ id: "organization", label: "조직관리", icon: "ic-organization" },
{ id: "employees", label: "직원관리", icon: "ic-employee", badge: "5" },
{ id: "roles", label: "역할관리", icon: "ic-id-card" },
],
},
{
label: "근태/급여",
items: [
{
label: "급여",
icon: "ic-money",
defaultExpanded: true,
children: [
{ id: "payroll", label: "급여대장", icon: "ic-money" },
{ id: "allowance", label: "수당 설정", icon: "ic-cash" },
],
},
{ id: "leaves", label: "휴가 관리", icon: "ic-calendar-check" },
],
},
],
};2. 전체 LNB Shell — workspace(admin) 최신 패턴
브랜드(정적, 비클릭) + 접기 토글이 헤더에 함께 배치되고, 프로필·토글이 있던 하단 영역(.ds-lnb__bottom)은 없다. 프로필은 PageHeader 아바타로 이동. 대신 맨 아래에 브랜드 마크 가 온다(2026-08).
<aside class="ds-lnb" :class="lnbCollapsed ? 'ds-lnb--collapsed' : 'ds-lnb--expanded'">
<div class="ds-lnb__header" x-data="workspaceSwitcher({ current: 'admin' })">
<!-- 심볼 자리 + 텍스트. 병원 로고를 등록한 조직은 심볼 자리를 로고가 가져간다
(같은 24px 정사각, .ws-brand__org-logo — object-fit: contain 이라 안 잘린다).
미등록이면 패밀리 심볼. 텍스트는 $store.hereby.brand + 조직명 두 줄. -->
<div class="ws-brand">…</div>
<button class="ds-lnb__toggle-inline" @click="lnbCollapsed = !lnbCollapsed">…</button>
</div>
<div class="ds-lnb__top" x-data="lnbSidebar({...})">
<!-- include ".../lnb-sidebar.html" -->
</div>
<!-- 하단 브랜드 마크 — partial 밖, aside 의 마지막 자식. 접히면 심볼로 바뀐다 -->
<div class="ds-lnb__brandmark" aria-hidden="true"
x-html="$icon(lnbCollapsed ? 'logo-hereby-medical-outline-on-dark'
: 'logo-lockup-hereby-medical-outline-on-dark')"></div>
</aside>마크의 숨김 판정도 셸이 합니다 — 조건과 순서는 숨김 판정에 있습니다.
(레거시) 스위처 + 하단 profile/collapse 변형
총무·경영 대시보드 등 단독 화면이 아직 쓰는 기존 변형 — 헤더에 워크스페이스 스위처(드롭다운), 하단 .ds-lnb__bottom에 프로필 + 접기 토글.
API
lnbSidebar(opts: LnbSidebarOptions) Alpine.data factory.
| Prop | 타입 | 설명 |
|---|---|---|
config | LnbConfig | { sections: LnbSection[] } — 섹션·메뉴 트리 |
isActive | (id: string) => boolean | 현재 활성 메뉴 판단 |
onSelect | (id: string) => void | 메뉴 클릭 핸들러 |
타입
| Type | 필드 |
|---|---|
LnbItem | id?, label, icon, badge?: string | number | LnbBadge, disabled?, children?, defaultExpanded? |
LnbBadge | text: string | number, tone?, line?, shape?(기본 round), size?(기본 xs) |
LnbSection | label?, items: LnbItem[] |
LnbConfig | sections: LnbSection[] |
id없는 item은 클릭해도 select 안 됨 (children 토글만)defaultExpanded→ 부모 메뉴는 기본으로 펼쳐짐(미지정=펼침). 접힌 상태로 시작하려면defaultExpanded: false를 지정badge→ 메뉴 우측 칩. 배지는 DS.ds-label컴포넌트로 렌더된다(전용 스타일 X).- 문자열/숫자(예:
badge: "5") → 알림 카운트 필(primary·round). 문서 결재 대기 건수 등에 사용 - 객체(
LnbBadge) → DS 라벨 변형을 직접 지정:tone(색)·line(테두리)·shape(square/round)·size(xs/sm/md) - 다크 레일 주의: LNB 배경이 어두우므로(neutral-900) 준비중 라벨 등엔 다크 서피스 tone(
gray-dark·light-gray-dark)을 써야 글자가 읽힌다. 예) 총무 준비 메뉴 ={ text: "준비중", tone: "light-gray-dark", size: "xs", shape: "square" }(solid). 밝은 배경용gray/light-gray는 글자가 어두워 다크 레일에서 안 보임
- 문자열/숫자(예:
사용된 토큰 (fix 후)
| 카테고리 | 토큰 |
|---|---|
| Spacing | --ds-spacing-{2, 4, 6, 8, 10, 12, 16, 18, 20, 22, 24, 32, 36, 68, 208, 220} 등 |
| Color (dark) | --color-neutral-{25, 50, 100, 700, 900}, --color-red-500, --color-orange-{50, 500} |
| Glass tints | --ds-glass-{08, 10, 18, 30, 55, 60, 70} |
| Brand | --ds-bw-white |
| Radius | --radius-4, --radius-8, --ds-radius-sm, --radius-full |
| Opacity | --ds-opacity-60 (submenu bullet), --ds-opacity-40 (flyout disabled) |
| Typography | --text-b4, --text-b5, --text-b6 (tip), --text-c1, --text-c2 |
| Shadow | --ds-shadow-xs (collapsed toggle), --shadow-blur4 (profile menu), --shadow-popover (flyout) |
| Motion | --duration-normal (sidebar width transition) |
| Flyout/Tip color | --ds-bw-white(바탕), --color-line-normal(라인), --color-neutral-{50, 75, 500, 900} |
단일 출처
| 파트 | 파일 |
|---|---|
| Alpine 컴포넌트 | packages/components/src/components/LnbSidebar.ts |
| HTML partial | packages/components/html/lnb-sidebar.html |
| 스타일 | packages/static/styles/design-system.css (.ds-lnb*) |
검토 필요 사항
토큰 체계가 아직 커버하지 못해 하드코딩으로 남겨둔 항목. 디자이너 결정 후 토큰화 진행:
- 🟡 rgba(255, 255, 255, 0.06) (4곳: border, hover backgrounds) —
--ds-glass-04(0.04) /--ds-glass-08(0.08) 사이. 0.06 토큰 추가 vs 0.08로 정렬 결정 필요 - 🟡 rgba(255, 255, 255, 0.9) (1곳: profile-name) —
--ds-glass-70초과. 0.9 토큰 추가 필요 - 🟡 rgba(255, 255, 255, 0.45) (1곳: section-label) —
--ds-glass-30/55사이. 0.45 토큰 또는 정렬 - 🟡 rgba(143, 143, 143, 0.5) (1곳: collapse toggle border) — neutral-400 + 0.5 alpha. 단일 토큰 부재
- 🟡 font-weight: 400/500/600/700 — 모두 하드코딩. weight 토큰 미정의
- 🟡 transition 120ms / 150ms (~8곳) — motion scale 부족
- 🟡 padding-left 13px / 34px (menu / submenu) — 2px step 미정렬. 메뉴 들여쓰기 정책 검토
- ✅ badge — 전용
.ds-lnb__badge인셋 스타일 제거 완료. 배지는 이제 DS.ds-label(크기·모양·색)로 렌더되고,.ds-lnb__badge는 접힘 레일에서 배지를 숨기는 훅으로만 남음 - 🟡 right -10px / -23px (collapse toggle 음수 offset) — half-pixel positioning 정책
- 🟡 1px hairline (3곳: section-line, profile-menu-divider) — spacing 최소가 2px. hairline 정책 결정
- 🟡 avatar linear-gradient — 브랜드 그라데이션, 단일 토큰 부재. 컴포넌트 전용으로 유지 가능
목표 & 로드맵
| 항목 | 현재 | 목표 |
|---|---|---|
| Config-driven 렌더링 | ✅ section/item/submenu 트리 | 유지 |
| Spacing 토큰 매핑 | ✅ 완료 | 유지 |
| Color hex → 토큰 | ✅ 완료 (fallback 정합 포함) | 유지 |
| Glass tint 매핑 | ✅ 정확 매칭만 (08/10/18/30/55/60/70) | 0.06 / 0.45 / 0.9 추가 |
| Shadow 매핑 | ✅ blur4 / xs | 유지 |
| Font-weight 토큰화 | 🔴 미정의 | typography 페이지 weight scale 도입 후 적용 |
| Motion 토큰 확장 | 🟡 fast/normal만 | 120ms / 150ms 등 세분화 |
| 2px step 외 spacing | 🟡 13/34/-10/-23 (메뉴/툴글) | step 정렬 또는 layout offset 토큰 도입 |
| Hairline 토큰 정책 | 🔴 미정의 | border-width / spacing 둘 중 결정 |
| 컴포넌트 가이드라인 | 🔴 미작성 | section 그룹 사용 시점, badge 사용 규칙 등 |
변경 이력
| 날짜 | 내용 |
|---|---|
| 2026-07-02 | 배지를 DS .ds-label로 렌더: 전용 .ds-lnb__badge 시각 스타일 제거, LnbItem.badge가 string | number(카운트 필) 또는 LnbBadge 객체(tone·line·shape·size 변형) 허용. 다크 레일용 tone(gray-dark·light-gray-dark) 지원. 총무 준비중 메뉴 = light-gray-dark solid 배지 + disabled. 라이브 미리보기·API 표 갱신 |
| 2026-06-12 | 접힘 레일 UX: 플라이아웃(.ds-lnb__flyout)·커스텀 툴팁(.ds-lnb__tip, 네이티브 title 대체)·is-child-active 강조 추가. workspace 패턴: 접기 토글 헤더(브랜드 우측) 이동·하단 profile 제거(헤더 아바타로)·ws-brand 정적 브랜드. 레일 스크롤바 숨김. 아이콘 13종 추가 + 중복 제거 리맵 |
| 2026-05-28 | 토큰 audit + fix: spacing ~50곳, radius 7곳, font-size 8곳, color hex 5곳, glass tint 5곳, color fallback 3곳, shadow 2곳, opacity 1곳, transition 3곳 → DS 토큰 매핑. 시각 변화 ≈ 0 |
| 이전 | config-driven LnbSidebar 컴포넌트 + 4-group 계층 admin 적용 |