Skip to content

WorkspaceSwitcher

진행 중 (WorkspaceSwitcher component — work in progress)

  • LNB 헤더 영역(.ds-lnb__header)에 자리 잡는 워크스페이스 전환 드롭다운. 한 곳에서 정의된 워크스페이스 카탈로그를 모든 앱이 공유.
  • 트리거 자체는 LNB 다크 톤 (글래스 tints) 사용, 드롭다운 패널은 흰 배경 사용.
  • 토큰 fix 완료. 단, font-weight, transition 120/150ms, border-radius 7px, 300px width, 1px hairline 정책 결정 대기 — 검토 필요 사항 참고.
규칙 (강제) · 코딩 에이전트용 — 사람은 접힌 채로 두어도 됩니다

WorkspaceSwitcher 강제 규칙. 상세는 아래 본문.

⚠️ 신규 화면에 이 드롭다운을 새로 도입하지 마세요. 실제 사용처는 general-affairs·management-dashboard 2곳뿐이고, admin은 쓰지 않습니다(admin은 클릭 불가 .ws-brand를 렌더). 이 페이지의 "사용처" 본문이 admin을 포함한다고 적어놨다면 그건 오래된 서술이에요.

호스트 마크업 — 인라인 스타일이 필수입니다

html
<div class="ds-lnb__header" style="padding: 0; min-height: 0;"
     x-data="workspaceSwitcher({ current: 'general-affairs' })">
  <!-- include workspace-switcher.html -->
</div>
  • style="padding: 0; min-height: 0;"를 빼먹지 마세요. design-system.css.ds-lnb__header 블록은 자체로 padding: 8px 10px + min-height: 36px를 갖는데, 트리거도 자기 padding·min-height(44px)를 갖고 있어서 이중 패딩으로 헤더가 틀어집니다.
  • ⚠️ 본문의 "호출 패턴" 코드 스니펫에는 이 인라인 스타일이 빠져 있어요. 스니펫을 그대로 복사하지 말고 위 형태를 쓰세요. (실제 호출처 2곳과 파셜 docstring은 전부 이 스타일을 갖고 있습니다.)

config

  • current는 카탈로그 id와 정확히 일치해야 합니다: admin | hr-system | management-dashboard | team-manager | general-affairs | inventory. 틀리면 조용히 실패해요 — 라벨이 "Hereby"로 폴백되고 어떤 항목도 is-current가 안 됩니다.
  • 카탈로그(WorkspaceSwitcher.tsconst ITEMS: WorkspaceItem[])가 단일 출처입니다. 워크스페이스 추가 = 거기 엔트리 추가. .ws-item 마크업을 손으로 짓지 마세요.
  • workspace-switcher.htmlx-for 래퍼 <div style="display: contents">를 실제 요소로 바꾸지 마세요. Alpine v3에서 중첩 x-for가 깨져서 평평한 entries 구조로 일부러 우회한 거예요 — 바꾸면 드롭다운 flex 레이아웃이 깨집니다.
  • disabled 항목은 속성 3개가 다 필요해요: is-disabled 클래스 + aria-disabled="true" + tabindex="-1".

⚠️ 트랩 ① 트리거 radius는 8px 선언이지만 실제로는 4px입니다.ws-trigger<button>인데 전역 4px !important 강제(design-system.cssbutton:not([class*="bg-"]):not(.ds-tab-item)… 규칙)의 :not() 제외 명단에 없습니다. border-radius: var(--radius-8)(8px)를 선언해도 4px로 렌더돼요. 선언을 고쳐도 소용없습니다. (.ws-item<a>, .ws-item-icon<span>이라 강제를 안 받아요. 트리거만 걸립니다.)

⚠️ 트랩 ② .ws-brandx-data를 "죽은 코드"라며 지우지 마세요 admin의 .ws-brand는 클릭이 안 되지만 여전히 workspaceSwitcher x-data가 필요합니다iconFor('logo-hereby-medical')로 로고를 그리거든요. 지우면 로고가 사라집니다.

  • 참고: admin의 .ws-brand 라벨은 카탈로그 제목이 아니라 $store.hereby.currentUser?.organizationName에서 옵니다. 같은 .ws-trigger-name 클래스라고 데이터 출처가 같다고 가정하지 마세요.

복제본 없음 — 앱 main.css 어디에도 .ws-* 재정의가 없습니다. 정본만 고치면 돼요.

상태: 워크스페이스(admin) 미사용 (2026-06~)

총무·경영 대시보드가 워크스페이스로 병합되면서 admin에서는 앱 전환 드롭다운이 불필요해져 제거되었고, 자리에는 비클릭 정적 브랜드(.ws-brand — 그라데이션 로고 + "Hereby Workspace" 텍스트, 버튼 크롬·클릭 없음)가 들어갔다.

  • 이 드롭다운 컴포넌트는 현재 총무(4005)·경영 대시보드(4006) 등 단독 접속 화면에서만 사용 중
  • 단독 화면들도 추후 정리 대상(후속 검토) — 신규 화면에 이 드롭다운을 새로 도입하지 말 것
  • ws-brandws-trigger와 같은 배치(높이 44·gap 8)에 배경·테두리·커서만 없는 변형이며, 접힘 시 로고만 남는다

라이브 미리보기

아래는 실제로 클릭되는 라이브 미리보기입니다 — 프로덕션과 동일한 .ws-* 클래스로 렌더됩니다. 트리거를 눌러 드롭다운을 열고, 항목을 클릭해 현재 워크스페이스를 전환해 보세요. (LNB 다크 헤더 톤 안에 트리거가 놓인 모습)

현재 워크스페이스: Workspace 설정 · 드롭다운 닫힘— 트리거를 눌러 열고 항목을 클릭해 전환해 보세요

⚠️ docs 환경 차이 (실제 앱 아님): 위 데모 항목의 파란 밑줄은 docs(VitePress)가 본문 안의 <a> 링크에 자동으로 입히는 스타일입니다. 실제 컴포넌트(workspace-switcher.html)도 항목을 <a>로 쓰지만, 앱에는 .vp-doc 링크 스타일이 없어 밑줄이 생기지 않습니다. → CSS로 억제하면 불필요한 override(강제)가 생기므로, 강제 대신 이 차이를 텍스트로만 명시합니다.

Dropdown 구성

  • 폭 300px, 흰 배경, shadow-xl, radius 10
  • 그룹별 라벨 (10px upper, neutral-400)
  • 현재 워크스페이스 (ic-workspace): orange-50 배경, 아이콘 박스 orange-600 + 흰 아이콘, 체크 표시
  • 일반 항목: 흰 배경, 아이콘 박스 orange-50 + orange-600 아이콘 (ic-chart-up, ic-clipboard)
  • 그룹 사이 .ws-divider (1px hairline)
  • disabled (ic-cube): cursor not-allowed, 텍스트·아이콘 흐림, "Coming soon" 배지

단일 출처

파트파일
Alpine 컴포넌트packages/components/src/components/WorkspaceSwitcher.ts
HTML partialpackages/components/html/workspace-switcher.html
스타일packages/static/styles/design-system.css (.ws-*)

구조

  • trigger — LNB 헤더 안에서 워크스페이스 이름·설명·chevron 노출. dark 톤 (--ds-glass-04/08/10).
  • dropdown — 300px width, 흰 배경, 그룹(group-label)별로 묶인 워크스페이스 카드 리스트.
  • item — 아이콘 + 제목 + 설명 + (현재면) 체크 마크. orange 50/600/700 톤으로 현재 표시.
  • disabled itemis-disabled 또는 aria-disabled="true" — hover 무효, 흐릿한 색.
  • collapsed sidebar에서: trigger의 텍스트·chevron 숨김, 드롭다운은 LNB 우측으로 펼침.

호출 패턴

html
<div class="ds-lnb__header" x-data="workspaceSwitcher({ current: 'admin' })">
  <!-- include "../../packages/components/html/workspace-switcher.html" -->
</div>

워크스페이스 카탈로그는 컴포넌트 내부에 정의 (단일 출처). 호출 시엔 current (현재 워크스페이스 id) 만 전달.

워크스페이스 카탈로그

WorkspaceSwitcher.ts 내부 ITEMS 배열에서 한 곳 관리.

그룹id제목설명상태
조직 운영adminWorkspace 설정직원·팀·역할표시
조직 운영hr-system급여관리월급 명세·수당·공제hidden
분석 & 스케쥴management-dashboard경영 대시보드인건비·청구·인력 분석표시
분석 & 스케쥴team-manager내부 결재휴가·초과근무·승인hidden
업무 & 자원general-affairs총무교육·정기업무·공지표시
업무 & 자원inventory재고관리물품·유통기한·환불disabled

hidden: true → DOM에 남지만 hidden 속성으로 시각적 제외 (페이지 통합 전이라 잠시 숨김 처리) disabled: true → 표시되지만 클릭 불가, 흐린 색

API

workspaceSwitcher(opts) Alpine.data factory. 옵션은 단순:

Prop타입설명
currentstring현재 워크스페이스 id ('admin' 등) — 카탈로그의 id와 일치해야 함

타입

Type필드
WorkspaceItemid, group, title, desc, path, devPort, icon, hidden?, disabled?, badge?

States

State표현
trigger defaultbackground: var(--ds-glass-04), border var(--ds-glass-08)
trigger hoverbackground: var(--ds-glass-08)
trigger openbackground: var(--ds-glass-10), border var(--ds-glass-18)
item defaultcolor: var(--color-neutral-900), transparent bg
item hoverbackground: var(--color-neutral-50)
item currentbackground: var(--color-orange-50), title var(--color-orange-700), icon bg var(--color-orange-600) + 흰 아이콘
item disabledcursor: not-allowed, 텍스트 var(--color-label-disable), 아이콘 흐림

사용된 토큰 (fix 후)

카테고리토큰
Spacing--ds-spacing-{4, 6, 8, 10, 16, 30, 44}
Color--color-neutral-{50, 100, 400, 500, 900}, --color-orange-{50, 500, 600, 700}, --color-line-normal, --color-label-disable, --color-label-muted
Glass tints--ds-glass-{04, 08, 10, 18, 55, 70}
Brand--ds-bw-white
Radius--radius-8, --ds-radius-sm, --ds-radius-lg, --ds-radius-label-md
Typography--text-b3 (chevron 16px), --text-b5 (title/check 13px), --text-c1 (desc 11px), --text-c2 (label/group/badge 10px)
Shadow--ds-shadow-xl (dropdown)
Motion--duration-fast (item hover transition)

검토 필요 사항

토큰 체계가 아직 커버하지 못해 하드코딩으로 남겨둔 항목:

  • 🟡 width: 300px (dropdown) — spacing 토큰 max 256px 초과. component-level layout 토큰 필요 (--ds-layout-dropdown-width 등)
  • 🟡 border-radius: 7px (item-icon) — radius 토큰에 7px 없음 (6/8 사이). 6 또는 8로 정렬 vs 컴포넌트 전용 유지
  • 🟡 font-weight: 600 (trigger-name, group-label, item-title, item-badge) — weight 토큰 미정의
  • 🟡 transition 120ms / 0.15s (trigger / chevron) — motion scale 부족
  • 🟡 padding 3px 6px (item-badge) — 2px step 미정렬. 배지 인셋 정책 결정 필요
  • 🟡 1px hairline (ws-divider) — spacing 최소 2px. hairline 토큰 정책
  • 🟡 margin-top: 1px (item-desc) — 시각 정렬용 microvalue. 토큰화하지 않음 (디자이너 의도적 nudge)
  • 🟢 글래스 톤은 정확 매칭만 사용 — 추가 step 필요 없음

사용처

.ds-lnb__header를 가진 모든 앱이 동일하게 사용:

각 앱은 current 만 다르게 전달 (해당 워크스페이스의 id).

목표 & 로드맵

항목현재목표
단일 워크스페이스 카탈로그✅ ITEMS in WorkspaceSwitcher.ts유지
Spacing 토큰 매핑✅ 완료유지
Color 토큰 매핑✅ 글래스 + 시맨틱유지
Radius 토큰 매핑✅ sm/lg/label-md7px 정렬 결정
Font-size 토큰 매핑✅ b3/b5/c1/c2유지
Font-weight 토큰화🔴 미정의typography weight scale 도입 후
Motion 토큰 확장🟡 fast/normal만120ms / 150ms 등 추가
Layout 토큰 (dropdown width 등)🔴 미정의--ds-layout-* 도입
hidden 항목 정리🟡 hr-system, team-manager hidden페이지 통합 시점에 노출
컴포넌트 가이드라인🔴 미작성group 분류 규칙, disabled 사용 시점 등

변경 이력

날짜내용
2026-06-12workspace(admin)에서 드롭다운 제거 — 페이지 병합(총무·경영)으로 전환 기능 불요. 정적 브랜드 변형 .ws-brand(비클릭, 로고+텍스트) 신설·적용. 단독 화면(총무·경영 등)은 스위처 유지
2026-05-28토큰 audit + fix: spacing ~10곳, radius 3곳, font-size 5곳, transition 1곳 → DS 토큰 매핑. 시각 변화 ≈ 0
이전단일 워크스페이스 카탈로그 컴포넌트로 통합 — 모든 앱이 동일한 스위처 공유