변경 이력
운영 규칙
디자인 시스템(토큰 · 컴포넌트 · 패턴 문서)이 변경되면 같은 작업에서 이 페이지에 항목을 추가합니다. 문서만 고치고 이력을 비워두지 않습니다.
2026-09-04 — 떠 있는 어시스턴트도 ESC 로 닫힘 (design/ai-assistant-dock)
변경
- 떠 있는 어시스턴트(좁은 화면)가 ESC 로 닫힙니다. 그때는 본문 위에 얹힌 팝업이라 ESC 가 자연스럽습니다. 도크는 그대로 둡니다 — 본문을 밀어내고 자리를 차지하는 상시 크롬이라, 모달을 띄운 채로도 쓰라고 만든 것을 키 하나로 접으면 전제가 깨집니다
- 실측: 떠 있는 AI(z65) + 모달(50) 상태에서 ESC 1회 → 어시스턴트, 2회 → 모달. 도크에서는 ESC 를 눌러도 유지됩니다
2026-09-04 — 캡처가 어시스턴트·시트 레일을 빼던 것 (design/ai-assistant-dock)
수정
- 어시스턴트를 열어 둔 채 신고하면 그 패널이 그림에서 빠졌습니다(사용자 리포트). 캡처 필터가
.ds-fab-layer를 통째로 걸러 채팅·어시스턴트 패널과 시트 워크북 레일까지 지웠습니다 — 어시스턴트가 문제일 때가 신고가 가장 필요한 순간인데 정작 증거가 사라진 것입니다. 이제 빼는 것은 신고 UI 자신과 확인 다이얼로그 둘뿐입니다 - 도크가 열려 있을 때 오른쪽을 잘라내던 크롭도 제거했습니다. 패널이 필터에서 빠지던 시절 빈 흰 칸을 없애려던 보정인데, 이제는 그 크롭이 어시스턴트를 잘라냅니다
2026-09-04 — 좁은 화면 자동 접힘 · 떠 있는 창은 하나만 (design/ai-assistant-dock)
변경
- 1280 밑으로 내려가는 순간 어시스턴트가 접힙니다. 떠 있는 창으로 바꿔 두면 그 폭에서 채팅과 80% 겹쳐 뒤쪽을 못 읽습니다 — 열어 둔 채 두는 것보다 접는 편이 낫습니다
- 떠 있는 상태에서는 채팅과 어시스턴트가 한 번에 하나만 열립니다(UI 겹침 방어). 여는 쪽이 신호를 쏘고 반대쪽이 접습니다 → FAB 배치 계약
함정
- 경계를 넘는 순간에만 접어야 합니다 — 좁은 화면에서 사용자가 직접 연 떠 있는 창까지 리사이즈마다 닫으면 못 씁니다
- 도크에서는 상호 배타를 걸지 않습니다 — 채팅이 도크 왼쪽으로 비켜서 겹치지 않으므로 둘 다 열려 있어도 됩니다(실측 겹침 0). 무조건 하나만 열리게 하면 도크의 이점이 사라집니다
2026-09-04 — 도크 폭이 창을 줄였다 넓히면 안 돌아오던 것 (design/ai-assistant-dock)
수정
- 창을 줄이면 도크 폭이 그대로 굳어 다시 넓혀도 안 돌아왔습니다. 800 으로 쓰던 상태에서 창을 900px 로 줄였다 1920 으로 되돌리면 360 으로 남았습니다(실측). 창이 좁아질 때 고른 폭 자체를 덮어쓰고 있었기 때문입니다 — 이제 표시만 물리고(
dockRenderWidth) 고른 값(dockWidth)은 그대로 둡니다. 확인: 800 → 680 → 384 → 680 → 800 - 드래그 기준도 「지금 그려진 폭」으로 바꿨습니다. 고른 폭이 상한에 물려 있는 상태에서 고른 값을 기준으로 잡으면 손잡이를 잡는 순간 폭이 튑니다
2026-09-04 — ESC 는 가장 위에 뜬 팝업부터 (design/ai-assistant-dock)
변경
- ESC 가 「화면에서 가장 위에 있는 것」 하나만 닫습니다. 예전에는 팝업마다 자기
@keydown.escape.window를 달아 먼저 등록된 리스너가 먼저 돌았는데, 그 순서는 Alpine 이x-data를 만나는 순서(DOM 순서)라 늦게 뜬 팝업과 무관했습니다 — 모달 위에 신고 팝업을 띄우고 ESC 를 누르면 밑에 깔린 모달이 닫혔습니다(사용자 리포트).esc-guard.ts에 공용 디스패처를 두고 신고 모달·채팅 패널을 등록했습니다. 실측: 모달+신고 상태에서 ESC 1회 → 신고만, 2회 → 모달 - 새 팝업은
registerEscLayer(getEl, close)로 등록하고 마크업의 ESC 리스너는 뺍니다 → FAB 배치 계약
함정
- 디스패처는 window 캡처 단계에 걸어야 합니다. 버블 단계면 다른 팝업의 리스너와 다시 순서 싸움이 됩니다
- 「가장 위」는 시간이 아니라 z-index 로 고릅니다 — 나중에 뜰수록 높은 층에 서므로 결과가 같고, 등록을 빠뜨린 팝업이 있어도 무너지지 않습니다. 레거시 모달이 더 위면 비켜서서 그 모달의 자기 리스너가 처리합니다
2026-09-04 — 신고 모달을 팝업 위로 · 어시스턴트 글자 정합 (design/ai-assistant-dock)
수정
- 신고 모달이 다른 팝업 아래로 들어가던 것을 고쳤습니다(사용자 리포트). 신고는 화면이 이상할 때 부르는 것이라 그 화면이 이미 모달이거나 모달 위 모달(60)인 경우가 흔한데, 기본 백드롭(50)이라 그 아래에 깔려 「눌렀는데 아무 일도 안 난다」로 보였습니다.
.ds-modal-backdrop--report(--ds-z-modal-report68)를 신설했습니다 — 컨펌(70)보다는 아래라 신고 모달 안에서 띄우는 확인 팝업이 자기를 덮습니다 - 어시스턴트 답변의 문단만 16px 로 뜨던 것을 고쳤습니다 — 공유 DS 의 unlayered
p규칙이 말풍선의text-b5(13px)를 이겨서, 사용자 말풍선(13px)과 나란히 놓였을 때 어시스턴트만 커 보였습니다..ai-md p에font-size: inherit을 넣었습니다
변경
- 응답 피드백(👍/👎)을 감췄습니다.
ai.feedback로 저장은 되는데 읽는 곳이 한 군데도 없어 사용자가 누른 값이 아무 데도 쓰이지 않습니다. 쓰는 곳이 생기면 마크업을 되살리면 됩니다
2026-09-04 — 딤 위 신고 버튼 · 어시스턴트에서 신고 진입점 제거 (design/ai-assistant-dock)
수정
- 버그 리포트 캡처에 팝업이 담기지 않던 것을 고쳤습니다 — 아래 함정 참고
추가
- 팝업이 떠 있을 때만 뜨는 신고 버튼
.ds-report-fab(흰 원 + 어두운 글리프) — 층은 어시스턴트와 같은 65(--ds-z-report-fab)라 겹쳐 뜬 모달(60) 위에서도 눌립니다 — 팝업이 뜨면 헤더의 「불편·기능 신고」가 딤에 가려 못 누르는데, 정작 팝업이 문제일 때가 신고가 가장 필요한 순간입니다. 딤 위(--ds-z-report-fab55)에 뜨고, 결정을 묻는 팝업(컨펌 70·권한 75)보다는 아래입니다 → FAB 배치 계약
변경
- 어시스턴트 상단바의 「버그 리포트 하기」를 뺐습니다. 신고 진입점은 헤더의 상시 진입(확성기)과 오류 다이얼로그의 「리포트 보내기」, 그리고 위 플로팅 버튼이 맡습니다 — 대화창에 같은 입구를 하나 더 두면 어디로 들어가야 하는지가 흐려집니다
함정
offsetParent로 「모달이 떠 있나」를 재면 안 됩니다. 백드롭은position: fixed라 항상 null 이라, 그 판정을 쓴 채팅 ESC 가드가 통째로 죽어 있었습니다(2026-09-04 실측) — 모달 위에서 ESC 를 누르면 모달 대신 채팅이 닫혔습니다. computeddisplay로 봅니다- 신고 버튼을 55 에 두면 겹쳐 뜬 모달(60)에 깔립니다 — admin 「근무 설정」 패널 위 「휴가/연차 유형」 같은 흔한 조합입니다(사용자 리포트). 어시스턴트와 같은 65 로 올렸습니다
- 캡처가 팝업을 통째로 빼고 있었습니다(2026-09-04 사용자 리포트). 필터가 클래스만 보고
.ds-modal-backdrop을 전부 걸러, 사용자가 신고하려는 그 팝업이 그림에서 사라졌습니다 — 팝업이 문제일 때가 신고가 가장 필요한 순간인데 정작 증거가 빠진 것입니다. 이제 확인 다이얼로그(--top)만 빼고 일반 팝업은 담습니다. 리포트 모달 자신은data-cs-report-host로 이미 걸러집니다 <button>으로 두면 원이 안 됩니다. 전역 blanket 규칙이 모든<button>의 radius 를 4px 로 강제해 모난 사각으로 렌더됩니다(실측) — 채팅·AI FAB 과 같이<div role="button">을 씁니다- 헤더 안 버튼만 위로 올릴 수는 없습니다. 헤더 래퍼의
z-index(그리고position: sticky)가 쌓임 맥락을 만들어 그 안의z-index는 바깥과 겨루지 못합니다 — 자식에position: fixed를 줘도 같습니다. 줄 전체를 올리면 팝업 중에 상단바만 밝게 남아 어색해서, 화면 최상위에 따로 그리는 플로팅 버튼으로 갔습니다 - FAB 줄과 같은 높이에 두면 안 됩니다. AI FAB 과 좌표가 정확히 겹치는데(실측 둘 다
x=1532) AI FAB 은 딤 위(65)라 이 버튼(55)을 덮어 클릭이 그쪽으로 갑니다 — 눈에는 안 보이는 고장입니다. 한 칸 위(bottom: 104px)에 세웁니다 - 보임/숨김은 CSS
:has()가 정합니다. MutationObserver 는 admin(2만 노드)에서 x-show 토글마다 돌아 버튼 하나 값어치를 넘습니다
2026-09-04 — 대화 말풍선 b5로 축소 · 채팅 ESC 닫기 (design/ai-assistant-dock)
변경
- 채팅·어시스턴트 말풍선 본문을
text-b3(16px) →text-b5(13px) 로 내렸습니다(실측 확인). 두 대화창이 한 화면에 나란히 뜨므로 같은 값이어야 합니다. 여백도 함께 좁혔습니다 — 어시스턴트px-3 py-2→px-2.5 py-1.5, 채팅px-3.5 py-2→px-3 py-1.5, 메시지 간격gap-2→gap-1.5 - 어시스턴트 마크다운(
.ai-md)의 크기 위계를 다시 잡았습니다 — 제목 b2(18)→b4(14), code b5(13)→b6(12), 문단·목록·표 여백도 한 단계씩. 본문만 내리면 제목이 본문보다 커 보이고 code 가 본문과 같아져 위계가 뭉갭니다 - ESC 로 채팅 패널이 접힙니다. 어시스턴트는 그대로 안 닫힙니다 — 상시 크롬이라 ESC 의 대상이 아닙니다 → FAB 배치 계약
함정
- 말풍선 크기는 한쪽만 바꾸면 안 됩니다. 채팅과 어시스턴트가 나란히 떠서 글자가 다르면 바로 눈에 띕니다. 그리고 어시스턴트는 마크다운 스케일이 본문에 물려 있어 두 파일을 같이 봐야 합니다
- ESC 처리를
stopImmediatePropagation()에 기대지 마세요. 모달도 같은 window 리스너를 쓰는데 등록 순서는 Alpine 이x-data를 만나는 순서라 예측할 수 없습니다 — 화면마다 결과가 달라집니다. 지금 무엇이 떠 있는지로 판정해야 하고, 숨은 백드롭이 DOM 에 여러 개 있으므로 존재가 아니라 표시 여부를 봅니다
2026-09-04 — 우하단 FAB 정리 + 어시스턴트 사이드 패널(도크) (design/ai-assistant-dock)
변경
- AI FAB 과 채팅 FAB 의 자리를 맞바꿨습니다 — AI 가 바깥(가장자리 24px), 채팅이 안쪽(80px) 입니다(사용자 결정). 색도 갈립니다: AI = BI 패밀리 그라디언트(
.ds-fab--brand), 채팅 = 단색 다크(bg-neutral-900). 예전에는 채팅이bg-orange-400, AI 가bg-neutral-900이었습니다 - 가로 자리를 마크업에서 걷어냈습니다.
right-[calc(80px+…)]같은 임의값 대신.ds-fab--ai/.ds-fab--chat만 붙이고design-system.css가 슬롯을 고릅니다 — 채팅은 AI FAB 이 실제로 DOM 에 있을 때만 안쪽으로 물러납니다(body:has(.ds-fab--ai)). AI 는 admin 한 곳에만, 그것도 조직 AI 가 켜져 있을 때만 그려지므로 못 박으면 나머지 화면에서 가장자리가 빕니다 → FAB 배치 계약
추가
- 어시스턴트가 화면 오른쪽에 붙는 도크로 열립니다 — 덮지 않고 본문을 밀어냅니다(
body.ds-ai-dock-open의padding-right). 폭은 좌측 핸들 드래그로360 ~ min(800, 화면폭 − 720), 키보드(←/→·Home)로도 조절됩니다. 태블릿 이하(<1280)에서는 예전의 떠 있는 패널로 자동 폴백합니다 → 도크 규칙 - 어시스턴트만 모달 위에 섭니다(
--ds-z-fab-ai65). 상시 크롬이라 모달을 띄운 채 물을 수 있어야 하는데 44 에서는 딤 아래로 내려가 통째로 못 썼습니다. 위 칸은 컨펌 다이얼로그(70)·권한 팝업(75)이 가져갑니다 - AI FAB 의 새 답변 표시
.ds-fab__dot— 개수를 세지 않으므로 숫자 배지가 아니라 점 하나입니다. 흰 링은 장식이 아니라 필수입니다(그라디언트 면에서 빨간 점이 묻힙니다) - iframe 안에서는 두 FAB 을 그리지 않습니다(
.ds-embedded) — admin 이 품는 앱들도 자기 화면에서 FAB 을 그려 한 화면에 두 벌 떴습니다
함정
- 채팅·어시스턴트 패널의 앞뒤를 z-index 로 바꾸려 하지 마세요. 어시스턴트는 모달(50) 위, 채팅은 모달 아래여야 해서 두 요구가 동시에 성립하지 않습니다 — 빈 z 칸을 어떻게 배치해도 풀리지 않습니다. 겹치지 않게 만드는 것이 해법이고, 도크가 그 일을 합니다(겹침 0 실측)
--ds-z-fab토큰을 올려서 해결하지 마세요. 채팅 FAB·패널과 시트 워크북의 수식 범위 바가 함께 씁니다 — 올리면 2026-08 에 고친 「FAB 이 모달의 확정 버튼을 덮는다」가 돌아옵니다. 예외는 요소 단위(더블 클래스)로만 줍니다- 도크로 전환해도 AI FAB 은 DOM 에 남습니다(
x-show).body:has(.ds-fab--ai)가 계속 매치해 그냥 두면 채팅이 안쪽에 선 채 바깥 자리만 빕니다 —body.ds-ai-dock-open규칙이 특이도로 되돌립니다 - 드래그 중 iframe 의 포인터 이벤트를 죽여야 합니다. admin 은 iframe 을 여럿 품는데, 커서가 그 위로 가면 부모가 이벤트를 못 받아 드래그가 그 자리에 굳습니다
2026-08-26 — 안내 아이콘 한 쌍으로 통합 + 이름 규칙 명문화 (design/icon-check-update-docs)
수정
- 획을
stroke가 아니라 「채운 면」으로 그린 아이콘 15개가 굵기 정규화를 피해 갔습니다. 실측하니organization1.20px ·workspace·team·signature1.25px ·money·setting1.30px 로 표준(1.67px @20px)의 0.72~0.78배 였어요 — 화면에서 혼자 얇아 보이던 이유입니다.stroke-width가 아예 없으니 8/25 굵기 개정이 닿지 않았습니다 money·organization·workspace를 stroke 기반 표준으로 다시 그렸습니다(0.99~1.05x 로 수렴)setting은gear와 중복이었고,gear쪽이 이미 표준 굵기였습니다 —setting이름을 유지한 채 도형을gear것으로 바꾸고ic-gear는 삭제했습니다(호출 4곳 이동).ic-setting-fill도 같은 실루엣의 solid 로 교체ic-chevron-down만 혼자 채운 면이라 나머지 세 방향보다 두꺼웠습니다 — stroke 기반으로 바꿔 네 방향이 같아졌습니다ic-dots-grid는 이름과 다른 그림이었습니다 — 실제로는 키보드(M11 14h2가 스페이스바)였어요. 이름대로 3×3 점 격자로 다시 그렸습니다ic-party의 폭죽 파티클이 깨져 보였습니다 — 점 좌표를 정리했습니다ic-refresh-fill을 삭제했습니다(호출 1곳 →ic-refresh)team·signature도 같은 원인으로 얇아 다시 그렸습니다 —signature는 테두리 안 흘림이 획 2에서 뭉개져 필기선 + 밑줄 형태로 바꿨습니다- 점(dot) 크기를 통일했습니다 —
ic-dots-grid는 이름과 달리 키보드였고, 3×3 격자로 다시 그리면서 점을ic-dots-horizontal과 같은 구성(반지름 .75 원 + stroke 2 = 지름 3.5)으로 맞췄습니다.ic-grip도 지름 2.8 → 3.5 - line 을 다시 그리면
-fill짝도 같이 그려야 합니다 —organization과workspace는 line 만 바꿔서 한동안 짝이 서로 다른 그림이었어요. 둘 다 새 구성에 맞춰 solid 를 다시 만들었습니다 ic-terminal(1.83x)은 예외입니다 — 짝 없는 solid 글리프라 두꺼운 게 맞습니다.ic-file-xlsx(0.72x)는 안에 «XLSX» 글자가 든 배지형이라 성격이 달라 그대로 뒀습니다- fill 변형 7종을 추가했습니다 —
ic-bell-fill·ic-camera-fill·ic-cap-fill·ic-employee-fill·ic-folder-fill·ic-star-fill·ic-tag-fill. line↔fill 쌍이 18 → 25쌍이 됐어요 - 자동 생성은 열린 선 디테일을 지워버립니다 —
ic-bell-fill의 추와ic-cap-fill의 술이 사라졌었습니다. 둘 다 열린 선(M10 21h4,M21 7v6)이라 채우면 넓이가 0이 돼요. line 의 획 굵기까지 반영한 범위로 채운 알약을 직접 그려 넣었습니다(예: 추 =M10 20h4a1 1 0 0 1 0 2h-4a1 1 0 0 1 0-2z→ line 의M10 21h4+ stroke 2 와 같은 x 9~15 · y 20~22) ic-question-circle-fill을 새로 추가했습니다 — line 만 있고 fill 이 없던 쌍입니다. line 과 채움 비율이 같도록 viewBox 를 계산해서("0.4 0.4 19.2 19.2") 넣어 1.00x 로 맞췄습니다ic-navigation을 지우고ic-map-pin으로 통합했습니다. 종이비행기 모양이었는데, 하필 쓰이는 곳도 스케줄러 「제출」 버튼 한 곳뿐이라rotate-90으로 돌려 "보내기" 아이콘처럼 쓰고 있었어요 — 이름과 그림과 용도가 셋 다 어긋나 있었습니다. 「제출」 버튼은 원래 그 뜻인ic-send로 바꾸고(회전도 불필요해졌습니다), 지도 위치는 이미 있던ic-map-pin이 맡습니다. 「한 의미에 한 쌍만」 규칙대로 같은 뜻의 두 번째 이름을 만들지 않았어요ic-map-pin-fill을 새로 추가했습니다 — line 만 있던 쌍입니다. line 의 획 바깥선과 같은 실루엣으로 그리고 안쪽 원은fill-rule="evenodd"로 파냈습니다(1.03x — 허용 범위 8% 이내)- 알림 아이콘을 심각도 3종으로 정리했습니다 — 오류는 원+
!(ic-exclamation-circle) · 경고는 삼각형+!(ic-warning) · 안내는 원+i(ic-info-circle). 색만으로 구분하면 빨간 상자 안에 안내 기호가 들어가 톤이 어긋나요 → 알림 아이콘 - 중복이던
ic-info를ic-info-circle로 합쳤습니다(호출 64곳 이동). 두 원(exclamation·info)은 같은 원(r=9)에 막대와 점만 상하 반전한 형제로 다시 그려 한 가족으로 읽힙니다 - 오류 공지 29곳이 안내 기호(ⓘ)를 쓰고 있던 걸 바로잡았습니다 — 로그인·회원가입 실패, 초대 오류, 연차 정책 불일치 등. 같은 자리에서 원래도 어긋나 있던 2곳(develop 시점부터
ic-info)도 함께 교정했습니다 ic-info-circle-fill의i를 다시 그렸습니다. line 쪽i와 글자 모양이 아예 달랐어요(글자가 눌리고 아래에 이상한 발이 붙어 비대칭). viewBox 도 20 vs 24 로 달라 나란히 놓으면 fill 이 더 커 보였습니다. 이제 line 과 같은 24 좌표계에서 같은 위치·굵기로 그린 뒤fill-rule="evenodd"로 안쪽을 파냅니다ic-lightbulb이 찌그러져 있었습니다. 유리부 호의 현 길이가 11.54 인데 반지름이 6(지름 12)이라 거의 한계치였고,large-arc=1과 겹쳐 원 둘레의 59% 를 그리는 기형 호가 됐습니다 — 좌상단이 움푹 들어가고 좌우가 어긋나 보였어요. 반지름 5.6 의 정상 호로 다시 그렸습니다
추가
이름 규칙을 문서에 명문화했습니다 —
ic-{의미}-{형태}-{채움}순서이고 채움 자리는fill일 때만 씁니다(line 은 접미사 없음). 한 의미에 한 쌍만 두고, 쌍은 같은 글자·같은 좌표계로 그립니다line↔fill 의 크기가 어긋난 3쌍을 맞췄습니다 —
ic-warning(fill 이 17% 작음) ·ic-lock(17% 큼) ·ic-check-circle(13% 큼).d좌표는 건드리지 않고 viewBox 만 조정해 고쳤습니다(예:ic-check-circle-fill"0 0 16 16"→"-1.5 -1.5 18 18"). 15쌍 전체를 실측해 1.00x 로 수렴한 것을 확인했습니다
앞선 기록 정정
- "viewBox 가 어긋난 쌍 5개" 는 잘못된 진단이었습니다. 실측해 보니 viewBox 숫자와 실제 보이는 크기는 별개였어요 —
ic-menu(24↔16)·ic-refresh(24↔20)는 viewBox 가 달라도 채움 비율이 같아 멀쩡했고, 반대로 viewBox 가 같은ic-lock(24↔24)이 17% 어긋나 있었습니다. 기준을 "글리프가 박스를 채우는 비율" 로 바꿔 문서에 명시했습니다 - "fill 안쪽 마크가 얇다" 도 과잉 진단이었습니다. 15쌍을 크게 놓고 보니 문제가 아니었어요 — 파낸 마크는 검은 바탕 위 흰 영역이라 같은 폭이어도 더 두껍게 보여서(irradiation), 얇게 그린 것이 오히려 올바른 광학 보정입니다. line 굵기에 맞춰 키우지 마세요
ic-message(0.91x)·ic-user(0.92x)는 정상 범위로 두었습니다 — solid 는 원래 조금 작게 그립니다
2026-08-25 — 락업 최소 높이 24 → 16px, LNB 브랜드 자리 재배치 (design/lnb-hospital-logo)
변경
- 락업 최소 높이를 24px → 16px 로 내렸습니다 → 브랜드 · 크기와 보호 영역. 「그보다 작으면 워드마크를 떼고 심볼만」이라는 판단 자체는 그대로이고, 그 경계선만 옮겼습니다. 심볼 단독에 하한이 없다는 것도 종전과 같습니다
- 데모의 높이 슬라이더 하한도 16 → 10 으로 내렸습니다. 규칙 하한과 슬라이더 하한이 둘 다 16 이면 「최소 크기 미만」 경고를 한 번도 볼 수 없게 되기 때문입니다 — 규칙은 보여줄 수 있어야 규칙입니다
추가
- LNB 하단 브랜드 마크(
.ds-lnb__brandmark) — 영문 outline 락업(다크 배경용 흰색 단색) · 높이 18px · 불투명도 30% · 가운데 정렬 · 비클릭. 접힘 레일(안쪽 폭 44px)에서는 락업 대신 같은 outline 심볼로 바뀌고, 메뉴가 세로로 다 들어가지 않는 화면에서는 숨겨 메뉴에 자리를 돌려줍니다(세로가 짧은 노트북에서 안 보이는 것이 정상입니다). CSS 는 공용이지만 지금 렌더하는 앱은 admin 하나입니다 - outline 자산 2종을 레지스트리에 등록했습니다 —
logo-lockup-hereby-medical-outline-on-dark·logo-hereby-medical-outline-on-dark. outline 은 색이 배경에서 갈리므로 이름에 배경이 붙습니다
수정
- LNB 헤더의 「hereby 심볼 × 병원 로고」 코브랜딩을 걷어냈습니다. 208px LNB 에서 브랜드 텍스트가 쓸 수 있는 폭이 29px 까지 밀려 「hereby Me…」로 잘렸어요(
.ws-brand안쪽 136 − 심볼 24 − gap 8 − 코브랜딩 블록 75). 이제 로고를 등록한 조직은 심볼 자리를 로고가 대체하고(같은 24px 정사각),hereby는 위의 하단 브랜드 마크가 맡습니다 - 「LNB 헤더에 락업을 못 쓰는 이유」가 규칙 위반에서 디자인 판단으로 바뀌었습니다. 132px 폭에 넣으면 높이가 19.8px 인데, 하한이 24 이던 때는 위반이었고 16 인 지금은 규칙상 가능합니다. 그래도 화면은 심볼 + 텍스트를 유지합니다 — 승격하려면 시안을 먼저 받으세요
2026-08-25 — 획 굵기 규격 16px 기준 재정의 + 아이콘 갤러리 신설 (design/icon-check-update-docs)
추가
- 아이콘 라이브러리 페이지를 채웠습니다 — 그동안 "준비 중" 스텁이라, 등록된 아이콘 162개를 훑어볼 방법이 레포에 없었습니다(
packages/static/icons/*.svg폴더를 Finder로 여는 게 유일한 우회로였어요). 이름 검색 · 12~24px 미리보기 크기 토글 · 클릭하면 이름 복사 - 목록을 손으로 적지 않습니다 —
packages/components/src/icons.ts를 빌드 시점에 그대로 불러와(getIcon/iconNames) 렌더하므로, 아이콘을 새로 등록하면 이 페이지도 다음 배포에서 자동으로 같이 늘어납니다 - 이 페이지 자체가 회귀 확인 도구입니다 — 크기 토글로 전 아이콘을 12~24px 구간에서 눈으로 훑을 수 있습니다. 아래 두 결함 다 이 갤러리를 보다가 찾았어요
수정
- 획 굵기 규격을 "16px 표시에서 1.33px" 로 다시 정하고 획 기반 아이콘 123개를 전부 맞췄습니다. 굵기가 5가지로 갈려 있었어요 — 1.00px(88개) · 1.12px(3) · 1.20px(7) · 1.33px(24) · 1.50px(1). 원인은 하나입니다: Heroicons v1 outline 을 그대로 붙여넣은 것(그쪽 기본이
stroke-width="2", 우리 규격은 1.5였음).ic-refresh는 path 까지 원본과 바이트 단위로 같았습니다 - 왜 "적은 숫자" 가 아니라 "16px 환산" 이 기준인가 — 아이콘은 대부분
w-4(16px)로 쓰는데 굵기는 viewBox 좌표계 값이라, 적은 숫자와 보이는 굵기가 다릅니다. 실제 굵기는stroke-width ÷ viewBox가 정해요. 옛 규격(24÷1.5)은 24px 표시를 전제한 값이라 실사용 16px 에서는 1.0px 헤어라인으로 렌더되고 있었습니다. 새 규격 24 viewBox · stroke 2(= 16px 에서 1.33px)는 Heroicons·Lucide 와 같은 값이고, 이미 24개가 여기 있었습니다. viewBox 가 24 가 아니면viewBox ÷ 12로 환산합니다(16 → 1.3333, 32 → 2.6667) → 스타일 규칙 ic-close는 24 좌표계로 되돌리고 글리프만 키웠습니다. 이 아이콘만 16 좌표계 특례였는데, 그러면 굵기 규격에서 혼자 벗어납니다. 24 박스 안에서 X 가 박스의 50% 밖에 안 차지해 작아 보이던 게 진짜 원인이었어요 — 62.5%(15 유닛)로 키워 16px 에서 X 폭 8px → 10px, 굵기는 새 표준 1.33px 입니다. X 를 쓰는 481곳이 여기 해당합니다- 적용은
stroke-width숫자만 바꿨습니다(ic-close제외) — path 좌표를 건드리지 않아 도형이 깨질 위험이 없습니다.ic-spinner는 굵은 링이 목적이라 유일한 예외로 남겼습니다(2.67px) ic-badge-check도형이 찌그러져 있었습니다. path 끝의 호(arc) 4개가 통째로 빠져 도형이 닫히지 않았고, 시작점까지 대각선으로 뚝 끊겨 이어졌습니다 — 씰(seal) 한쪽이 잘려나간 모양이었어요. 2026-08-10 레지스트리 최초 등록 때부터 그랬습니다. 출처(Heroicons) 원본과 대조해 복원했고, 끝점 좌표 합이 (0,0)으로 닫히는지 계산으로 확인했습니다- docs 데모 5개가 아이콘을 손으로 베껴 쓰던 것을 레지스트리 직접 참조로 바꿨습니다 —
DemoLnbSidebar·DemoWorkspaceSwitcher·DemoButtons·DemoEmployeeTable·DemoDataTable. 사본이라 등록본이 바뀌어도 문서만 옛 그림으로 남아 실제로 벌어져 있었습니다:org는 2×2 격자(실제는 조직도 글리프) · 총무의ic-clipboard는 빈 사각형+줄 3개(실제는 클립+체크리스트) · 「수정」은 획 2(실제 1.5) · 「삭제」는ic-trash와 아예 다른 모양
재발 방지
- 데모는 이제 이름만 적습니다 —
getIcon('ic-…')로 읽으므로 등록본이 바뀌면 문서가 자동으로 따라옵니다. 새 데모에서도 SVG 를 손으로 그리지 마세요(DemoLnbSidebar에는 "icons copied from icons.ts" 라는 사실과 다른 주석까지 붙어 있어 드리프트를 더 늦게 발견했습니다) - 예외는 둘뿐입니다 — ① 브랜드/BI 자산: 그라디언트
id가 문서 전역이라 한 페이지에 둘이 들어가면 충돌하므로 데모 전용 id 를 쓴 사본을 유지합니다 ②:d런타임 바인딩(표 정렬 화살표): 레지스트리 대상이 아닙니다
남은 것
- fill 이 없는 아이콘이 아직 100여 개 있습니다. 이 중 화살표·chevron·정렬선처럼 면이 없는 것 22개는 대상이 아니고(채워도 그릴 게 없습니다), 나머지가 후보입니다
- 일괄 자동 생성은 안 됩니다 — 시도해 보고 접었습니다. 획을 면으로 바꾸는 걸
fill-rule="evenodd"로 자동화해 17개를 돌렸더니 5개만 쓸 만했어요.ic-clipboard-check·ic-calendar-check는 조각만 남고,ic-approval은 검은 사각형,ic-search는 손잡이가 사라진 원이 됐습니다 — 내부 디테일이 별도<path>인 아이콘은 하나로 합치는 순간 형태가 무너집니다 - 그래서 나머지는 한 개씩 손으로 그려야 합니다. 서두르면 결함이 그대로 들어갑니다 — 이번에 발견된
ic-badge-check(도형이 안 닫힘)와ic-info-circle-fill(눌린i)이 정확히 그렇게 들어온 것들이었어요 - 자동 생성분에는 크기 보정이 필수입니다. 획의 중심선을 쓰기 때문에 일관되게 0.89~0.91x 로 작게 나옵니다(좌우로 획 절반씩 손실). 이번 7종도 viewBox 를 계산해 1.00x 로 맞췄습니다
packages/static/icons/*.svg파생본 99개를pnpm icons:build로 함께 갱신했습니다 — 등록본을 고칠 때 이 명령을 빼먹으면pnpm icons:check가 잡습니다- 채움(fill) 전용 38개는 손대지 않았습니다 — 획이 없어 굵기 축 밖입니다. 다만 이들도 Heroicons v1 solid 계열로 보여, 나중에 아웃라인과 나란히 놓았을 때 무게감이 맞는지는 따로 볼 여지가 있습니다
2026-08-13 — 로딩 스피너 문서 정본화 + 확장 축 개방 (design/update-loading-components)
추가
- Spinner 페이지를 신설했습니다 — 스피너는 이미 4곳에서 돌고 있었지만 규칙이 코드 주석에만 있어, "언제 링이고 언제 아이콘인가"를 판단할 근거가 문서에 없었습니다
- 종류는 두 개뿐이고, 자리가 정합니다 — 영역이 아직 아무것도 못 그리면 div 링
.ds-spinner, 컨트롤 하나만 바쁘면 SVG 아이콘ic-spinner+animate-spin. 판단이 애매하면 "이 스피너를 지우면 그 자리에 뭐가 보이나" 를 물어보세요 — 아무것도 안 보이면 링입니다 - 영역 로딩이 아이콘이 아니라 div 링인 이유(되돌리지 마세요): SVG
transform애니메이션은 메인 스레드가 막히면 합성 레이어로 못 올라가 프레임이 튑니다. 영역 로딩은 정의상 메인 스레드가 바쁜 순간이라 정확히 그때 끊깁니다 — 시트 로딩에서 "뚝뚝 끊김"으로 실제 리포트된 현상이에요. div 테두리 회전 +will-change는 GPU 합성이 안정적입니다 - 확장 축 5개를 변수로 열었습니다 —
--ds-spinner-size(2rem) ·--ds-spinner-thickness(3px) ·--ds-spinner-track(neutral-200) ·--ds-spinner-indicator(orange-500) ·--ds-spinner-badge-size(4rem). 기본값이 종전과 같아 화면 변화는 없습니다. 새 종류는.ds-spinner--<이름>으로 변수만 덮습니다 —width/border/animation을 다시 선언하면 GPU 합성과 reduced-motion 대응을 새 종류가 못 물려받아요 → 확장 절차 - 스피너가 아닌 로딩 표시도 같은 페이지에 지도로 정리했습니다 — 진행률을 아는 경우(N/M)는 스피너가 아니라 카운트 배너(시트 「추가 행 로딩 중… 500 / 1000」), 패널 안 짧은 목록은 텍스트 한 줄. 스켈레톤과 진행률 바는 DS에 없습니다 → 스피너가 아닌 것
수정
- 인라인
style로 복제돼 있던 링 1곳을 공용 클래스로 되돌렸습니다 — 시트 [설정 완료] 처리 중 화면(setup-complete.html). 겉보기는 같았지만 사본이라 ①--ds-spinner-*변수를 안 따라오고 ②prefers-reduced-motion에서 혼자만 1s 로 빠르게 돌고 있었습니다(다른 스피너는 2.4s). 모달 · 화이트 딤 문서의 예제 코드도 같은 복제본이라 함께 고쳤습니다 — 문서가 잘못된 패턴을 퍼뜨리고 있던 셈입니다 - 흰 딤 위에서는 받침(
.ds-spinner-badge)을 씌우지 않습니다 — 흰 바탕에 흰 원이 겹쳐 테두리만 보입니다. 받침은 링이 콘텐츠 위에 겹쳐 뜰 때만
재발 방지
pnpm check:spinners를 신설했습니다 (pre-commit 차단). 회전(animate-spin)과 원형 테두리를 한 줄에서 조합한 손으로 그린 링을 추가된 줄만 보고 막습니다. 이번에 정리한 인라인 style 형뿐 아니라 Tailwind 유틸 조합 형(border-2 border-orange-500 border-t-transparent rounded-full animate-spin)도 같이 잡습니다 — 두 철자가 실제로 존재했습니다. 아이콘 스피너(ds-ico/$icon(가 같은 줄)는 정상 사용법이라 대상이 아니고, 로딩이 아닌 회전 원은ds-spinner-exempt: <사유>로 통과시킵니다. 판정기는 7개 케이스(차단 2 · 통과 5)로 확인했습니다- 다섯 축을
@property { inherits: false }로 등록했습니다. CSS 변수는 기본이 상속이라, 패널 하나에--ds-spinner-size를 걸면 그 안에 든 부팅·임베드 스피너까지 따라 줄어듭니다. 이제 조상에 건 값은 내려가지 않고 변형 클래스나 요소 자신의 선언만 먹습니다 — 의도한 확장 방식 그대로입니다.@property를 모르는 브라우저는 블록을 무시할 뿐 렌더는 동일합니다
남은 것
ic-spinner를 영역 로딩에 쓰는 자리 11곳 — admin 스케줄러 4 · team-manager 3 · inventory · general-affairs · module-manager 대시보드 ·system-admin/loading.html(부팅 화면인데 아이콘을 씀)- 손으로 그린 링 3곳 —
employee-app/modals/work-settings.html·work-type-change.html·team-manager/dashboard.html. 두께 2px 라 공용 링(3px)과 미세하게 다르고 reduced-motion 에서 혼자 빠르게 돕니다 - 둘 다 마이그레이션 대상이지 위반이 아닙니다 — 일괄 스윕하지 말고 그 파일을 고칠 때 함께 바꾸세요. 전 앱이
design-system.css를 로드하므로 치환은<span class="ds-spinner"></span>한 줄입니다 - 복제본 없음 확인 — 앱
main.css어디에도.ds-spinner재정의가 없어 정본만 고치면 전 앱에 반영됩니다(모달·페이지네이션 같은 copy trap 이 없는 드문 경우)
2026-08-10 — 처리 중(로딩) 전용 화이트 딤 백드롭 (design/sheet-setup-usage-mode)
추가
- 백드롭에
--white변형을 넣었습니다 —.ds-modal-backdrop.ds-modal-backdrop--white, 딤 색만var(--ds-glass-70)로 바뀌고 나머지(fixed inset-0 · 가운데 정렬 · z · 스크롤 잠금)는 기본과 같습니다 → 화이트 딤 - 어두운 딤과 역할이 다릅니다. 어두운 딤은 "화면을 덮었으니 저기에 집중하라"는 신호지만, 흰 딤은 "같은 화면이 잠깐 멈춤" 으로 읽힙니다. 화면 전환 없이 몇 초 잠그고 스피너 + 한 줄 안내만 얹는 자리에 씁니다. 사용자 확인을 받는 다이얼로그에는 쓰지 않습니다 — 흰 딤 위에 선택 버튼을 얹으면 멈춤 신호가 선택 화면으로 바뀝니다
- 선택자를 더블 클래스로 둔 이유: 앱
main.css들이.ds-modal-backdrop단일 클래스 복사본을 갖고 있어, 특이도가 낮으면 앱에서 조용히 덮입니다. 더블 클래스(0,2,0)는!important없이 항상 이깁니다 - 첫 사용처는 급여 시트 [설정 완료] 처리 중입니다(UP-008 설정/사용 모드) — 확정 카드를 숨기고 같은 백드롭 안에서 흰 딤 + 스피너로 교체합니다
2026-08-07 — 컨펌 다이얼로그가 열릴 때 확인 버튼에 포커스 (design/sheet-uiux-fx)
변경
- 다이얼로그(알림·확인·선택형)가 열리면 확인 버튼이 포커스를 가집니다 — Enter 가 곧 확인이 되고(네이티브
confirm()관례), Tab 으로 취소/대안 버튼에 옮겨 Enter·Space 로 누를 수 있습니다. prompt 는 지금처럼 입력칸이 포커스를 가집니다 → 키보드 - 배경: 포커스가 다이얼로그를 연 쪽 입력창(급여 시트 수식바)에 남아, 수식을 Enter 로 확정한 뒤 범위 선택 다이얼로그에서 Enter 를 또 쳐도 아무 일도 일어나지 않았습니다("Enter 를 쳐도 수식 작성이 안 끝난다" 리포트). 이제 Enter 두 번이면 수식 확정 → 범위 확정까지 끝납니다
2026-08-06 — 채팅·AI FAB 비킴 변수 --ds-fab-dodge (design/sheet-uiux-fx)
추가
- 오른쪽 드로어가 열릴 때 FAB 를 왼쪽으로 비켜 세우는 계약을 넣었습니다 — 채팅/AI FAB 와 패널의
right가calc(<기본값> + var(--ds-fab-dodge, 0px))로 바뀌었습니다. 변수 기본값이 0px 라 세우지 않는 앱은 아무 변화가 없습니다 → FAB 배치 계약 - 첫 사용처는 급여 시트 워크북입니다: 오른쪽 레일 드로어(셀 상세·메모 / 열 설정 / 급여 반영 / 행 정보, 320px)가 열리면
body.sheet-rail-open이 320px 를 세우고, admin 임베드에서는hereby:tm:railpostMessage 로 부모 문서의 FAB 도 함께 밀립니다 - 배경: FAB 는
position:fixed라 드로어가 그리드를 밀어내도 그대로 드로어 위에 얹혀 하단 버튼(드로어 푸터)을 가렸습니다. 드로어 푸터 버튼들이 좌측 정렬로 도망가 있던 이유가 이것이었고, 비킴 도입으로 취소=왼쪽·저장=오른쪽 정렬이 가능해졌습니다
2026-08-05 — 컨펌 다이얼로그에 선택형(askChoice) 추가 (design/sheet-uiux-fx)
추가
- 선택지가 둘인 확인
askChoice를 넣었습니다 —"confirm"(오른쪽) ·"alt"(가운데) ·"cancel"(Esc). 물음이 「할까/말까」가 아니라 「어느 쪽이냐」 일 때 씁니다. 첫 사용처는 급여 시트에서 셀에 넣은 수식의 적용 범위(「이 셀만」 / 「전체 열에 적용」)입니다 → 선택형 - 네 번째 모달 종류가 아닙니다 — 카드·제목·여백·타이포는 컨펌 다이얼로그와 완전히 같고 푸터 버튼만 갈립니다. 모달 3종 분류는 그대로입니다
- 취소 버튼을 두지 않았습니다. 선택지 둘만 세우고 되돌아 나가는 길은 Esc 가 집니다. 셋째 버튼을 두면 무엇이 기본인지 흐려지고, 두 선택지 중 하나를 「취소」 자리에 앉히면 사용자가 무엇을 고른 건지 흐려집니다
- 호스트가 없으면
"cancel"입니다.askConfirm은 같은 상황에서 통과(true)시키지만 — 띄울 수 없는 확인창이 동작을 조용히 취소하면 안 되므로 — 선택형에는 통과시킬 기본값이 없습니다. 임의로 고르면 사용자가 고르지 않은 쪽이 실행됩니다
2026-08-04 — 급여 시트에도 가로 스크롤바 + 썸을 반투명으로 (design/sheet-uiux-fx)
변경
- 가로 스크롤바 제외 목록에서 급여 시트를 뺐습니다 — 이제
.sched-grid(근무표)만 제외입니다. 시트야말로 열이 수십 개라 가로로 넘치는 대표 화면이고, 이 기능을 만든 이유("윈도우에는 표를 가로로 스크롤할 입력 수단이 사실상 없다")가 가장 아프게 걸리는 곳이었습니다 → 가로 스크롤바 - 시트에는 아래쪽 바만 붙입니다. 시트의 스크롤 박스는
flex-1로 화면 높이에 맞춰져 있어 아래 바가 항상 보이고, 위 바는 sticky 열 헤더를 10px 가려 스프레드시트에서는 손해가 큽니다. 마크업 4곳에data-ds-xscroll="bottom"을 뿌리는 대신table-xscroll.ts의BOTTOM_ONLY_SELECTOR한 곳에서 정합니다 - 시트에서는 바가 표를 덮지 않고 자기 자리(10px)를 차지합니다 — 새 변형
.ds-xscroll--inflow(래퍼에data-ds-xscroll-inflow,.sheet-scroll은 기본 적용). 다른 표는 아래에 여백이 있어 겹쳐도 되지만, 시트는 스크롤 박스가 화면 끝까지 차서 맨 아래가 데이터 행(또는 sticky 합계 행) 이라 겹치면 그 줄이 잘려 보였습니다. 흐름 안에 두면flex-1인 박스가 그만큼 줄고 시트가 바 위쪽에 온전히 들어옵니다 - 표와 바 사이에 분리선(
border-top: 1px solid var(--color-line-normal))을 넣었습니다 — 시트 툴바·오른쪽 레일과 같은 선입니다. 겹치는 바에는 넣지 않습니다(표 위에 얹혀 있어 행 사이를 가로지르는 줄로 읽힘).border-box라 테두리가 트랙 안쪽을 먹으므로 바 높이만 10 → 11px로 키워 트랙은 그대로 10px입니다 - 썸을 평상시 반투명(
--ds-opacity-50)으로 낮췄습니다 — 이 바는 표 위에 상시 겹쳐 그려지므로, 진하면 데이터를 읽는 내내 눈에 걸립니다. hover·드래그 중에는neutral-400+ 불투명으로 또렷해져 조작 피드백은 그대로입니다 - 호스트 단위 제외 상수(
EXCLUDED_HOST_SELECTOR)는 없앴습니다 — 끄는 방법은data-ds-xscroll="off"하나로 통일합니다
2026-07-31 — 파비콘 캐시 무효화 + 문서 사이트 파비콘은 마스터 심볼 (design/BI-update)
수정
- 파비콘을
fill본으로 바꿨는데 탭에는 예전 아이콘이 그대로 남았습니다. 브라우저가 파비콘을 URL 기준으로 따로 캐시하기 때문이고, 강력 새로고침으로도 잘 지워지지 않습니다. 10개 앱의href에?v=2를 붙여 다시 받게 했습니다 — 앞으로 파비콘 파일을 바꾸면 이 값을 같이 올려야 합니다(값이 갈리면 앱마다 다른 시점의 아이콘이 남습니다) - 서빙되는 파일 자체는 정상이었습니다 — 16px로 렌더해 십자가 흰색으로 채워지는 것을 확인했습니다. 파일이 아니라 캐시가 원인이었다는 뜻입니다
추가
- 문서 사이트(docs)에 파비콘을 붙였습니다 — 마스터 심볼(스우시)입니다. 종전에는 파비콘이 아예 없어 브라우저 기본 아이콘이 떴습니다. 이 사이트의 주체는 특정 패밀리가 아니라 hereby 제품군 전체라 십자·사람·펜을 쓰지 않습니다
- 자산은 예외적으로
docs/public/favicon.svg에 둡니다 — 문서 사이트는@hereby/static을 소비하지 않아/static미들웨어가 없고 VitePress의public/이 유일한 경로입니다.href에는 base를 붙였습니다(버전 문서는/1.2.3/하위 경로라 루트 절대경로면 404)
2026-07-31 — BI 심볼 내부를 fill/outline 두 케이스로 등록 + 전 사용처 fill 전환 (design/BI-update)
추가
- 심볼을 그리는 방식을 둘로 등록하고 기본형을
fill로 정했습니다 —fill(그라디언트 면 + 안쪽 흰색) ·outline(그라디언트 없이 워드마크 색을 그대로 따르는 단색, 안쪽은 채우지 않음).outline은 선(stroke)이 아닙니다 — 면은 그대로이고 색이 단색으로 바뀌는 것입니다 → 심볼을 그리는 두 방식 - 심볼만이든 락업이든 기본은
fill입니다. 다크 배경 락업에서 워드는light로 뒤집히는데 심볼의 십자만 검게 남던 것이 이 규칙을 못 박은 계기입니다 — 워드마크에는 톤 규칙이 있어도 심볼 안쪽에는 규칙이 없었습니다 → 기본형 outline은 브랜드 컬러를 쓰지 않습니다 — 오렌지 단색도 그라디언트도 아니고, 워드마크와 같은 색(밝은 배경#1B1B1B· 어두운 배경 흰색) 하나입니다. 내보내기에는linearGradient가 아예 들어가지 않습니다outline은 컬러 없는 워드마크와만 조합합니다 — 서비스명 톤dark·light만 되고primary(오렌지)와는 금지입니다. 심볼이 워드마크 색을 따라가므로 워드에 컬러가 있으면 심볼까지 그 컬러가 되어 정의 자체가 깨집니다 → 조합 규칙outline을 자리별로 규정하지 않았습니다 — 「어느 화면은 outline」 같은 규칙은 두지 않습니다. 기본은 언제나fill이고outline은 데모에서 받아 쓰는 케이스입니다- 데모에 「심볼 내부」 선택기를 넣었습니다(기본값
fill, 심볼을 끄면 사라지고 마스터엔 없습니다).outline을 고르면primary버튼이 잠기고,primary상태에서outline을 고르면 그 배경의 중립 톤으로 물러납니다 — 배경 × 톤 금지 조합과 같은 처리입니다 - 내보내기 파일명 맨 뒤에 축이 붙습니다 —
hereby-bi-medical-en-dark-on-white-fill.svg·hereby-bi-medical-symbol-fill.svg. 기본형도 생략하지 않습니다: 생략하면 예전에 받아 둔outline파일과 같은 이름이 되어 조용히 덮어써집니다.outline은 심볼만이어도 배경이 붙습니다(…-symbol-outline-on-white.svg) — 색이 배경에서 갈리기 때문입니다
변경
- 심볼이 박힌 자리를 전부
fill본으로 교체했습니다(12곳) —icons.ts의 심볼 3(logo-hereby-medical·-sign·-works)과 락업 2종 안의 심볼, 파비콘 2(9개 앱이 참조), 랜딩 인라인 3(nav.html·footer.html·데모의 Sign), docs 데모 2(DemoLnbSidebar·DemoWorkspaceSwitcher). PNG 앱 아이콘은 유지합니다 fill본은 새로 그리지 않고 등록된 정본에서 파생합니다 —컨테이너+컨테이너 ⊕ outline 원본(fill-rule="evenodd"대칭차 = 정확히 안쪽 도형) 두 줄입니다. 디자인 재요청이 없고, 원본이 바뀌면fill본이 따라옵니다.clipPath·mask는 쓰지 않습니다(자산은 어디서 열어도 같아야 합니다) → 파생식- 컨테이너를 얻는 방법이 심볼마다 다릅니다 — Medical은 첫 서브패스가 이미 라운드 사각형, Sign은 펜이 외곽선에 통째로 파여 서브패스가 1개라 상자가 같은 Medical 컨테이너를 재사용, Works는 몸통도 외곽선에 파여 있어 그 구간만 Medical 컨테이너 하단으로 되돌립니다
- Works의 상단 노치는
fill본에서도 비어 있습니다 — 노치는 안쪽 도형이 아니라 컨테이너 실루엣의 일부라, 흰색이 되는 것은 사람(머리+몸통)뿐입니다. 위 파생식이 이것까지 자동으로 지킵니다 - 마스터엔 이 축이 없습니다 — 스우시 하나라 컨테이너도 안쪽 도형도 없습니다(뚫린 데가 없으니 채울 것도 없습니다). 마스터 데모에는 선택기가 나타나지 않습니다
2026-07-29 — 라벨에 굵기 축 추가 + 채팅 위젯 미확인 배지 (design/review-and-apply-improvements)
추가
- 라벨 굵기를 크기와 분리했습니다 —
.ds-fw-regular/medium/semibold/bold하나를 얹으면 크기는 그대로 두고 굵기만 바뀝니다(기본 medium). 버튼이 쓰던 모디파이어를 그대로 공유하고, 각 크기 변형이 읽는--ds-label-weight변수만 세팅하므로 새!important는 0입니다 → 라벨 → 굵기 - 라이브 미리보기에 굵기 토글을 넣어 4단계를 실제 CSS로 비교할 수 있게 했습니다
변경
- 채팅 위젯(FAB)의 반투명을 걷어냈습니다. 닫힌 상태가
bg-orange-500/50+text-white/80이라 시트 위에 띄우면 뒤 숫자가 그대로 비쳐 버튼인지 얼룩인지 구분이 안 됐습니다 — 기본은 불투명orange-400+ 흰 아이콘, hover 에서 primary(orange-500) 로 한 단계 진해집니다. 상시 노출되는 버튼이라 기본은 primary 보다 한 톤 낮추고 강조는 hover 가 집니다 - 미확인 배지를 DS 라벨로 교체했습니다 — 손으로 조합한
min-w-4 h-4 rounded-full bg-red-500 text-[9px]대신.ds-label--xs --round --solid --red+.ds-fw-bold. 세는 대상은 확인 안 한 대화(채널 포함) + 공지 합계이고, 99 초과는+99로 줄입니다(예전99+)
수정
- admin 의
.admin-app .ds-label { font-weight: 500 }이 굵기 축을 조용히 삼켰습니다. 특이도(0,2,0)가 크기 변형(0,1,0)보다 높아 admin 안에서만.ds-fw-bold가 무시됐습니다 —var(--ds-label-weight, 500)으로 바꿔 admin 기본값(medium)은 유지하면서 축이 살아 있게 했습니다. 앱 override 가 변수를 안 읽으면 그 앱에서만 DS 축이 죽는다는 사례입니다
2026-07-29 — 정보성 영역은 notice 사용 강제 (design/review-and-apply-improvements)
금지 (재유입 차단)
- 안내·주의·경고·오류를 알리는 박스는 반드시 notice + 변형으로 만듭니다. 새 기능이든 기존 화면 수정이든 같습니다 —
bg-red-50 border border-red-200 text-red-600같은 유틸 조합 틴트 박스를 새로 만들지 않습니다. 제각각인 크기·간격·색을 한 규격으로 모으는 것이 이 컴포넌트의 존재 이유입니다 - 이모지로 아이콘을 대신하지 않습니다 —
⚠️·❗대신<svg class="ds-notice__icon">. 이모지는 OS·폰트마다 크기·색이 달라 변형 색과 어긋나고 16×16 규격도 못 맞춥니다 - notice가 아닌 것을 함께 적었습니다 — 상태 배지(
.ds-label) · 흐름을 막는 확인창(모달) · 표 행 하이라이트 · 박스 없는 한 줄 검증 문구 - 레거시는 마이그레이션 대상이지 위반이 아닙니다. 일괄 스윕하지 않고 고치고 있는 파일 안에서만 바꿉니다(모달·버튼과 같은 정책).
bg-*-50조합 310곳이 잡히지만 상당수는 알림 박스가 아니라 배지·행 하이라이트입니다
변경
- 로그인·계정 화면의 알림 박스 10곳을 notice로 전환했습니다 — 공용
signin-form(오류 3 · 인증코드 안내 1) ·forgot-password-form(성공·안내·오류) ·change-password-form(성공·오류) ·signup-form(오류). 공용 컴포넌트라 8개 앱 로그인 화면에 한 번에 반영됩니다. 인증코드 발송 안내는 orange 조합이었는데 notice 변형에 orange가 없어--info(yellow)로 갑니다 - admin 의 계정 화면 오류 6곳 + 스케줄러 경고 목록을 notice 로 전환했습니다 — 워크스페이스 선택(3) · 회원가입 · 이메일 인증 · 초대 수락은
--danger, 스케줄 경고 모달의 항목은--warning+ 아이콘(⚠️이모지를 규격 아이콘으로 교체). 같은 화면의 team-manager 복사본은 아직 남아 있습니다 - team-manager 의 안내 박스 6곳을 notice 로 전환했습니다 — 병동 설정의 조직 정책 안내·교육 정책 안내 2종(예전엔 orange/amber 로 색이 서로 달랐습니다) · 스케줄 경고 목록(
⚠️이모지 → 규격 아이콘) · 치료 인센티브의 항목 미등록 경고. admin 에는 이미 적용돼 있고 team-manager 복사본만 옛 상태로 남아 있던 드리프트였습니다 - 직원앱의 "승인 대기 중인 신청이 있어요"를
--warning, 모듈 매니저의 "API 연결 실패"를--danger로 바꿨습니다. 모듈 매니저 쪽은 박스 안에 있던 재시도 버튼을 notice 밖(아래)으로 뺐습니다 — notice 는 액션 슬롯이 없으니 버튼은 밖에 둡니다 - hr-system 은 급여 시트 셀 상세 패널의 "발송 완료" 한 곳만
--success로 바꿨습니다. 나머지 후보는 열어보니 성격이 달랐습니다 — 호봉/급여 미리보기는 값 카드, 엑셀 가져오기 결과는 버튼이 달린 결과 카드(코드 주석에 "not a plain ds-notice"로 이미 명시), 치료 유형 문구는 읽기 전용 필드의 보조 텍스트, "보호된 행이 없습니다"는 빈 상태입니다. 버튼을 품은 결과 카드는 DS 에 정의가 없습니다 — notice 는 아이콘+문구 전용이라 액션 슬롯이 없어서, 결과 카드 패턴 정의는 백로그로 둡니다 .ds-modal__desc는 정본에 없습니다 — admin·hr-systemmain.css두 곳에만 있어서, team-manager 에서 쓰면 스타일이 통째로 빠져 전역p { 16px }로 렌더됩니다(조용한 깨짐). 두 복사본은margin값도 어긋나 있습니다. 근무 불일치 보정 모달의 설명문은 그래서 이 클래스 대신text-b4 text-label-assistive로 넣었고, 함정은.claude/rules/frontend/design-system-architecture.md의 복제 표에 기록했습니다- 모두가 안내 박스인 건 아닙니다 — 자격 유형 폼의 "자격유형 · 기본 · 삭제 불가" 카드와 희망근무 상세의 날짜 목록은 색 틴트를 쓰지만 목록 항목이라 notice 로 바꾸지 않았습니다. 색이 들어간 박스라고 전부 notice 가 아니라, 흐름을 설명·경고하는 박스만 notice 입니다
- module-manager(4010)가
design-system.css를 아예 로드하지 않고 있었습니다 —main.css가@import "tailwindcss"뿐이라, 이 앱만 공용 로그인 폼의.ds-btn이 무스타일로 렌더되고 있었습니다(기존 결함). notice 전환 시 투명 박스가 될 자리라 정본@import한 줄을 추가해 다른 8개 앱과 같은 로드 체인으로 맞췄습니다 - 연차 정책의 "5인 미만 사업장" 경고를
--danger+ 아이콘으로 전환했습니다(admin 유형·기준 관리). 손으로 조합한 red 박스(bg-red-50+text-b5 font-semibold text-red-600+ ⚠️ 이모지)를 DS 규격(패딩 10/12 · radius-4 · 12px · 아이콘 red-500)으로 맞췄고, 본문은<span class="flex-1">로 감쌌습니다 — admin의 unlayeredp { font-size: 16px }가 notice 의 12px를 이기기 때문에 notice 안에서는<p>를 쓰지 않습니다
2026-07-29 — 신규 모달은 무조건 DS 3종 (design/modal-apply-feedback-DS)
금지 (재유입 차단)
- 신규 기능에서 모달을 만들 때 DS 3종을 벗어나면 커밋이 막힙니다 —
pnpm check:modals(pre-commit blocking). 잡는 것: 백드롭 직접 조합(fixed inset-0+ 배경) ·bg-black/*·bg-[var(--ds-dim…)]· 새.ds-mini-modal· 모달 카드에 직접 준 패딩 ·.ds-modal__title에 얹은 색 - 추가된 줄만 봅니다. 레거시가 72곳(백드롭 25 · 딤 24 · mini-modal 17 · 패딩 6) 있어 전체를 막으면 아무 커밋도 못 합니다 — 목적은 과거 청산이 아니라 새 위반 차단입니다. 현황은
pnpm check:modals:audit - 정당한 예외는 사유를 적어 통과시킵니다 —
ds-modal-exempt: <사유>(같은 줄 또는 윗줄). 컨텍스트 메뉴·피커·라이트박스·드로어 딤처럼 문서가 인정하는 예외용이고, 사유 없는 예외는 두지 않습니다 - 문서에 새 모달을 만들 때 절을 만들어 3종 복붙 시작점과 막히는 목록을 함께 뒀습니다
변경
- 컨펌 팝업을 특징별로 나누지 않습니다. 파괴적(삭제) 확인도 카드·제목·여백·타이포가 완전히 같고, 차이는 푸터 버튼이 집니다 —
tone: "danger"는 이제 확인 버튼 색만 바꿉니다(제목 red 제거). 같은 컴포넌트가 두 종류처럼 보이던 것을 없앴습니다
수정
- 문서 데모의 다이얼로그 상단 여백이 실제와 달랐습니다 — VitePress 의
.vp-doc h3(margin-top:32px, 특이도 0,1,1)가.ds-modal__title(0,1,0)의margin:0을 이겨서 카드 위 여백이 28 이 아니라 60 으로 보였습니다. 실제 앱은 28 이었으니 문서 쪽만 어긋난 드리프트입니다.ds-tailwind-shim.css에서 모달 카드 안 헤딩의 본문 크롬을 무력화해 고쳤고(특이도 0,2,1), 새 모달 데모에도 자동 적용됩니다
2026-07-29 — 모달 3종 확정 + 컨펌 다이얼로그 여백 28 · 타이틀 「확인」 (design/modal-apply-feedback-DS)
변경
- 모달의 종류를 셋으로 확정하고 이름을 붙였습니다 — 컨펌 다이얼로그(confirmation dialog,
.ds-dialog) · 일반 모달(modal,.ds-modal) · 패널 모달(panel modal,.ds-modal--panel). 셋은 백드롭·닫기·스크롤잠금·카드 내부 구조가 전부 같고 바깥 여백과 크기만 다릅니다. 네 번째 종류는 만들지 않습니다 → 모달은 3종입니다 - 컨펌 다이얼로그 여백을 상하좌우 28로 일괄 조정했습니다(일반 모달 상44/좌우40/하36). 문장 한두 줄 + 버튼뿐인 카드에 40을 주면 내용 대비 여백이 과해 카드가 비어 보였습니다. 안쪽 간격(타이틀↔본문 24 · 본문↔푸터 32)과 타이포는 일반 모달과 그대로 동일합니다 — 달라진 건 바깥 여백 하나뿐입니다
- 여백 차이는 마크업이 아니라 CSS가 집니다 —
.ds-dialog .ds-modal__*가 값을 갖습니다. 선택자 특이도(0,2,0)가 앱main.css에 복제된.ds-modal__header(0,1,0)보다 높아 소스 순서와 무관하게 이깁니다(복사본은 admin·hr-system·management-dashboard 세 곳) - 컨펌 다이얼로그 타이틀을 on/off 축으로 만들고 기본값을 「확인」으로 통일했습니다. 예전엔 종류마다 기본 제목이 달랐습니다(alert 「안내」/「오류」, confirm 「확인」, prompt 「입력」) — 같은 컴포넌트인데 화면마다 제목이 갈려 서로 다른 것처럼 보였습니다. 이제 종류 불문 「확인」, 끄려면
title: ""/title: false(헤더가 통째로 빠지고.ds-dialog--no-title이 본문 위 여백을 대신 집니다), 바꾸려면 문자열 - 실패 신호는 제목이 아니라 톤이 집니다 —
showError의 제목도 「확인」이고,tone: "danger"가 제목을 red로, 확인 버튼을lightor로 바꿉니다 - 타이틀을 비워 부르던 급여 시트 4곳(
rows.ts)의title: ""를 지워 기본 타이틀 「확인」이 적용되게 했습니다
메모
.ds-mini-modal(저장확인·위저드 확인)은 이 3종 밖의 레거시입니다. 역할은 컨펌 다이얼로그와 같은데 규격이 다릅니다(패딩 24 · gap 32 · 타이틀 14) —.ds-dialog로 흡수하는 것이 목표이고 새 코드에서는 쓰지 않습니다. 일괄 스윕은 하지 않았습니다
2026-07-29 — 네이티브 얼럿 폐지 + 확인/알림 다이얼로그를 폼 모달 규격으로 (design/modal-apply-feedback-DS)
추가
- 공용 다이얼로그를 하나 만들었습니다 —
packages/components/src/dialog.ts+html/ds-dialog.html.askConfirm/showAlert/showError/showPrompt네 함수로confirm()/alert()/prompt()를 전부 대신합니다. 문서: 모달 → 확인·알림·입력 다이얼로그 - 등록은 앱이 아니라 플러그인이 합니다 —
createAuthComponentsPlugin()이Alpine.store("dialog", …)를 대신 걸어, 앱이 빠뜨리면 조용히 no-op(alert)·무조건 통과(confirm)로 떨어지는 사고를 구조적으로 막습니다
변경
- 다이얼로그 타이포·간격을 폼 모달과 동일하게 맞췄습니다. 예전
.ds-mini-modal(패딩 24 · gap 32 · 타이틀 14/600 · 본문 13)은 같은 화면의 폼 모달(44/40/36 · 타이틀 20/600 · 본문 14)과 눈에 띄게 달랐습니다. 이제 다이얼로그가.ds-modal__header/__title/__body/__footer를 그대로 씁니다 — 수치를 복제하지 않으므로 폼 모달이 바뀌면 다이얼로그도 따라갑니다. 타이틀↔본문 24px 는 헤더의padding-bottom이 만듭니다 - 네이티브
alert()/confirm()/prompt()를 전부 걷어냈습니다 — 231곳(TS) + 13곳(HTML). 실패 경로는showError(제목 「오류」 + red), 나머지는showAlert(제목 「안내」)로 나눴고,confirm()은await askConfirm()으로 바꾸며 호출부를 async 로 승격했습니다. 파괴적 확인(삭제·해체·초기화·revoke)은tone: "danger"→ 확인 버튼lightor - 중복 구현 3개를 공용 하나로 접었습니다 — hr-system 시트의
dialog슬라이스 +dialog-bridge+input-prompt, employee-app 의confirm스토어, Messenger 의 자체 폴백. 슬라이스는 얇은 위임만 남기고(this.askConfirm(...)170여 개 호출부 무변경) 상태·템플릿·브리지는 삭제했습니다
금지 (재유입 차단)
- 네이티브 시스템 다이얼로그는 이제 금지입니다 — 예외 없음. 걷어내는 것만으로는 다시 들어옵니다. 문서 + 기계적 차단 두 겹으로 막았습니다
- Biome
lint/suspicious/noAlert를error로 켰습니다 —.ts의alert(·window.alert(·globalThis.prompt(를pnpm lint:check에서 잡습니다 pnpm check:dialogs(pre-commit blocking) 를 새로 뒀습니다 — Biome 이 못 잡는 캐스트형(window as any).confirm(· 인덱스형window["alert"](·.html전부를 담당합니다. 색 검사와 달리|| true가 없어 걸리면 커밋이 막힙니다- 두 겹인 이유는 캐스트형이 실제로 9곳 새어 나갔기 때문입니다. 소스 grep 은 통과했고 빌드 산출물을 스캔해서야 발견했습니다(team-manager·admin 스케줄러). Biome 은 그 형태를 전역 참조로 보지 않습니다
- 예외는 다이얼로그 자체 구현(
dialog.ts·ds-dialog.html)뿐이며 스크립트ALLOW목록에 명시돼 있습니다
- Biome
메모
showAlert는 일부러 await 할 수 없습니다 — 대부분catch에서 부르는데 거기서 await 하면 뒤따르는finally정리가 사용자 클릭까지 멈춰 섭니다- e2e의
page.on("dialog", …)는 이제 아무것도 잡지 못합니다. 조용한 no-op 이 되어 「얼럿이 안 떴다」 검증을 항상 통과시키는 가짜 초록을 만들었습니다 — 시트 스펙 5개를 새 헬퍼helpers/ds-dialog.ts로 옮겼습니다. 다이얼로그를 띄우는 동작은 사용자의 답을 기다리므로await하지 말고 먼저 시작해 둔 뒤 확인을 누르고 기다립니다 ds-dialog.html루트의x-data는 지우면 안 됩니다. 이 파일은 앱index.html최상위(어떤x-data스코프에도 없는 자리)에 include 되는데, Alpine 은x-data루트에서만 트리를 초기화합니다. 없으면x-cloak이 남아 스토어는 열렸는데 화면엔 아무것도 안 뜨는 조용한 실패가 되고, 빌드·린트·타입체크는 전부 green 입니다 — 브라우저에서 실제로 띄워 봐야 잡힙니다ds-dialog.html주석에 include 지시문을 예시로 적으면 안 됩니다 — 빌드의html-include가 주석까지 훑어 자기 자신을 끼워 넣습니다(첫 빌드가 이걸로 깨졌습니다).ds-mini-modal은 그대로 남습니다 — 저장확인(close-confirm)·위저드 확인 등 네이티브 대체가 아닌 작은 팝업이 계속 씁니다. 이번 정렬 범위는 「네이티브 다이얼로그 대체」 하나입니다
2026-07-29 — 상태 툴팁은 「상태 + 행동」 2줄로 (design/modal-apply-feedback-DS)
변경
- hr-system 급여 시트의 반영 상태 칩 툴팁을 멀티라인 2줄로 바꿨습니다. 기존 문구는 「시트 값이 급여대장에 반영된 값과 다릅니다. 다시 반영하면 급여대장이 시트와 같아집니다」로, 다시 반영하는 방법이 문장 어디에도 없었습니다 — 상태만 알려 주고 행동은 사용자가 화면에서 찾아야 했습니다. 이제 1줄은 지금 상태, 2줄은 「오른쪽 「급여대장에 반영」 버튼을 누르면 …」처럼 누를 자리와 그 결과를 적습니다
- 못 누르는 상태에서는 먼저 풀어야 할 것을 앞세웁니다 — 작업 완료·보호·대상 미지정이면 버튼이 비활성이라 「버튼을 누르세요」가 막다른 안내가 됩니다. 판정은 반영 버튼과 같은
commitBlockedReason하나를 공유해 칩과 버튼의 안내가 어긋나지 않게 했습니다 - 급여대장 화면에도 안내를 넣었습니다 — 여기는 집계 열뿐이라 상태 칩도, 「급여대장에 반영」 버튼도 아예 없고 「소스 전체 반영」만 있습니다. 숫자가 여기 보이니 고칠 곳도 여기라고 읽혀 「반영 버튼이 안 보인다」로 되돌아오던 자리라, 그 버튼 툴팁 1줄째에 값이 소스 시트에서 온다는 사실을 먼저 적고 2줄째에 미반영 소스 개수와 「변경된 시트만 반영」을 적습니다
- 사이드바 상태 점을 색 있는 2단계로 바꿨습니다. 예전에는 미반영일 때만 점이 떠서, 점이 없는 상태가 「작업 완료」인지 「반영까지 끝난 작업 중」인지 구분되지 않았습니다. 이제 작업 완료가 아닌 시트는 전부 점을 달고 색이 반영 여부를 말합니다 —
primary=반영 필요 ·neutral-300=반영 완료. 라벨도 색의 짝을 읽어 줍니다(「작업 중 / 반영 필요」·「작업 중 / 반영 완료」) - 라벨은 상태 이름까지만, 방법은 적지 않습니다. 어떻게 반영하는지는 시트를 열면 툴바 칩과 반영 버튼이 이미 안내하므로 목록에서 같은 말을 두 번 하지 않습니다. 「편집 중」이라는 옛 라벨은 「작업 중」으로 정정했습니다: 시트 상태는 「완료 / 잠김 / 작업 중」 3종인데 「편집 중」은 그중 어느 것도 아니었고, 급여대장에서는 아무도 편집하지 않았는데 소스가 밀려 켜지므로 사실과도 달랐습니다
- 반영 개념이 없는 시트는 둘째 줄을 뺍니다 — 반영 열도 없고 소스도 받지 않는 시트에 회색 점을 달고 「반영 완료」라고 쓰면 하지도 않은 반영을 했다고 말하게 됩니다. 목록 API가
commitState(needed/done/none)로 이 세 갈래를 내려 줍니다 - 3줄째는 마지막으로 값을 고친 시각입니다(「3시간 전 수정」). 여기서 쓰는 값은
lastChangeAt(행·셀의updatedAt최댓값)이지 시트의updatedAt이 아닙니다 — 셀 편집 경로는sheet_views레코드를 건드리지 않아서, 시트updatedAt을 쓰면 방금 고친 시트가 「3일 전 수정」으로 보입니다(사이드바 목록에 이미 떠 있는 「N분 전」 텍스트가 그 값이라 같은 줄에서 두 시각이 어긋날 수 있습니다). 목록 API가 판정용으로 이미 뽑던 값이라 쿼리는 늘지 않았습니다
메모
- 줄바꿈 말고는 아무것도 더 하지 않았습니다 —
title에\n이 생긴 것만으로tooltip.ts가 상세 도움말 버블로 자동 전환합니다(data-ds-tip은 원래 있던 placement 지정 그대로, 새 클래스 0). 아래 2026-07-27 「멀티라인 툴팁 범위 확장」이 실제 문구 개선으로 이어진 첫 사례입니다 - 문구는 마크업이 아니라 TS getter(
commitStatusTooltip)에 둡니다. 상태 4갈래 × 2줄을:title삼항식에 넣으면 읽을 수 없고, 버튼 툴팁(commitButtonTooltip)과 같은 자리에 있어야 둘이 같이 관리됩니다
2026-07-27 — 툴팁 밝은 톤(light) 추가 (design/recheck-applied-feedback)
추가
- 툴팁에 밝은 톤을 만들었습니다 — 지금까지 툴팁은 검은 알약 하나뿐이라, 흰 카드·패널 위에서는 필요 이상으로 무겁게 튀었습니다. 밝은 배경 +
--color-label-alternative글자 +--color-line-normal1px 라인의 반대 톤을 하나 두었습니다 - 켜는 법은 속성 하나입니다 — 대상에
data-ds-tip-tone="light". 조상에 걸어도 됩니다(closest로 판정) — 밝은 톤은 보통 트리거 하나가 아니라 패널·화면 단위 결정이라, 패널 루트에 한 번만 걸면 그 안의 툴팁이 전부 따라옵니다. 안쪽 마크업은 한 줄도 고치지 않습니다 - 색 톤은 「세 번째 종류」가 아니라 세 번째 축입니다. 종류는 여전히 한 줄·멀티라인 2가지, placement도 4개 그대로이고, 톤은 그 둘과 직교합니다(멀티라인 밝은 compact-center 같은 조합이 그냥 됩니다). 톤 값은
light하나뿐이며 늘리지 않습니다 — 기본이 어두움이라dark가 필요 없고, 버블 스타일이 갈라지기 시작하면 되돌리기 어렵습니다
변경
- 색을 변수 2개(
--ds-tip-bg·--ds-tip-fg)로 모읍니다. 버블 배경과 4개 placement의 화살표가 같은 변수를 읽으므로, 톤 모디파이어는 변수만 덮으면 되고 placement별로 색을 다시 적지 않습니다. 예전처럼 화살표마다 색이 하드코딩돼 있으면 톤을 하나 추가할 때 3곳을 조용히 빠뜨립니다. 어두운 톤의 값은 그대로라 기존 화면의 시각 변화는 0입니다
메모
- 밝은 톤에만 테두리가 있습니다 — 흰 버블은 흰 배경 위에서 경계를 잃기 때문입니다. 화살표에도 같은 라인을 그리는데, 45° 회전한 사각형이라 바깥을 향하는 두 변이 placement마다 다릅니다(아래 방향
bottom+right· 위 방향top+left· 왼쪽 방향left+bottom). 화살표 안쪽 절반이 버블 테두리 위에 겹쳐 그려지므로 화살표를 1px 바깥으로 밀어 두 선을 잇습니다 - 그림자는 밝은 톤만
--ds-shadow-md로 바꿨습니다 — 어두운 톤의 25% 검정 그림자는 밝은 버블 아래에서 탁하게 번집니다. 어두운 톤의 그림자 2종은 매칭 토큰이 없어 하드코딩 유지라는 기존 결정 그대로입니다 - 적용 화면은 아직 없습니다. 컴포넌트만 준비된 상태이고, 「밝은 패널 위에서 검은 알약이 과하다」고 판단된 화면부터 붙입니다
2026-07-27 — 멀티라인 툴팁 범위 확장 + 가로 위치 보정 (design/recheck-applied-feedback)
변경
- 툴팁의 종류를 2가지로 정리했습니다 — 「한 줄」과 「멀티라인 상세 도움말」. 어디에 뜨는지(placement)는 별개의 축이고, 어떤 종류인지는 라벨에 줄바꿈이 있느냐 하나로 갈립니다
- 멀티라인을 버튼과 여러 구간에서 쓸 수 있게 범위를 넓혔습니다. 그동안은
.icon-btn·select같은 정해진 셀렉터에 걸리거나data-ds-tip을 붙인 대상만 DS 툴팁이 떴고, 그 밖의 요소는 여러 줄title을 줘도 브라우저 기본 툴팁이 떴습니다. 이제 줄바꿈이 곧 opt-in입니다 — 평범한<button>, 표 셀, 배지, 구간 제목 어디든 여러 줄title만 주면 상세 도움말 버블이 뜹니다(기본 위치는 대상 위 가운데).data-ds-tip은 위치를 바꾸고 싶을 때만 씁니다 - 한 줄 툴팁의 규칙은 그대로입니다. 아무 요소에나 한 줄
title을 단다고 DS 툴팁이 되지는 않습니다 — 범위가 넓어진 것은 멀티라인뿐입니다 - hr-system 급여 시트의 수식 힌트
ƒ는 마크업을 한 줄도 고치지 않았는데 자동으로 DS 멀티라인 툴팁이 됐습니다(원래 개행 title을 갖고 있었지만 opt-in이 없어 브라우저 기본 툴팁으로 떴습니다). 확장의 효과가 바로 보이는 사례입니다
추가
- 가로 방향 위치 보정. 지금까지 flip은 위아래 전용이라, 화면 가장자리 대상에 320px 버블이 뜨면 잘렸습니다. 범위를 넓히면 가장자리 버튼이 흔해지므로 같이 넣었습니다 — 버블은 뷰포트 안으로 되돌리고(양옆 8px) 화살표만 대상 위에 남깁니다. 계산은 순수 함수(
clampTooltipX·clampTooltipLeft)로 분리해 단위 테스트 12개로 고정했습니다
수정
- 오른쪽 끝에서 버블이 스스로 좁아지며 세로로 길어지던 버그를 재현·수정했습니다.
position: fixed+width: auto는 shrink-to-fit 폭을 자기left기준 잔여 공간으로 계산합니다 — 화면 오른쪽 끝에 앵커되면 남은 공간이 좁으니 버블이 그만큼 찌그러졌습니다(297px → 157px로 재현)..ds-tooltip에width: max-content를 주어 폭을 위치와 분리했습니다. 가로 보정을 넣으며 실측하다 발견했고, 확장 이전에는 가장자리 사용 자체가 드물어 드러나지 않던 문제입니다 [data-placement="top"][data-flip="down"]블록이 똑같이 두 번 선언돼 있어(뒤엣것만 적용되는 죽은 코드) 앞의 사본을 지웠습니다. 화살표 동작을 고치려다 죽은 쪽을 고치기 십상인 자리입니다
2026-07-27 — 툴팁 문서 정정: placement 4개 + 멀티라인 사용처 전수 (design/recheck-applied-feedback)
추가
- 라이브 미리보기에서 멀티라인 툴팁을 직접 확인할 수 있습니다 — 기존 미리보기에는 top·right·compact 3개뿐이라, 방금 문서에 적은 멀티라인을 눈으로 볼 방법이 없었습니다. 새 줄에 ⓘ(top·멀티라인) ·
?배지(compact-center·멀티라인) · 한 줄 비교 3종을 놓았습니다 - 이 줄은 데모가 아니라 실물입니다. docs 사이트는 앱과 똑같이
setupTooltip()을 한 번 호출해 두고 있어서,data-ds-tip+ 여러 줄title마크업만 두면 프로덕션 코드가 그대로 처리합니다 — 미리보기에서 보이는.ds-tooltip--multiline전환이 실제 화면의 그것과 같은 경로입니다. docs 데모가 실제 컴포넌트와 어긋나는(재구현 드리프트) 문제를 이 지점에서는 구조적으로 없앴습니다 - 미리보기의 Vue 미러 쪽도 실제 코드에 맞췄습니다 —
.ds-tooltip--multiline자동 토글, top placement의 아래로-flip(문서에는 있는데 데모에만 빠져 있었습니다)
수정
- placement가 3개라고 적혀 있었지만 실제로는 4개입니다 —
compact-center(data-ds-tip="compact-center")는tooltip.ts와design-system.css양쪽에 이미 구현돼 있는데 문서에만 빠져 있었습니다. compact와 같은 작은 버블이지만 트리거 가운데에 정렬되고 아래 화살표가 붙습니다 —?도움말 배지처럼 좁은 트리거에서 좌측 정렬 버블이 어긋나 보이는 문제 때문에 만든 변형입니다. 자동 대상 목록에.ds-btn--icon도 빠져 있어 함께 채웠습니다
메모
- 멀티라인 툴팁(
.ds-tooltip--multiline)이 실제로 뜨는 곳을 코드에서 전수 확인해 표로 남겼습니다 — admin 근무 유형 모달의 ⓘ 2개(연차 차감 판정 · 공휴일 근무 유형), hr-system 시트 「열 설정」 모달의?배지 1개, 이렇게 3곳뿐입니다 - ⚠️ 아래 두 항목은 같은 날 범위 확장(맨 위 항목)으로 폐기됐습니다 — 지금은 개행 하나로 충분하고,
ƒ힌트도 DS 툴팁으로 승격됐습니다. 기록으로만 남깁니다 - 발동 조건은 「개행 title」 하나가 아니라 두 가지입니다 — ① 툴팁 대상일 것(자동 셀렉터 또는
data-ds-tip) ② 평가된title에 실제 개행이 있을 것. hr-system 시트의 수식 열ƒ힌트는 개행 title을 갖고 있지만data-ds-tip이 없어(그 파일에는data-ds-tip이 하나도 없습니다) 브라우저 기본 툴팁으로 뜹니다. 미전환 1건으로 문서에 적어 뒀습니다 - Alpine
:title="..."이 소스에서 여러 줄에 걸쳐 있는 것은 개행이 아닙니다. 이 기준으로 다시 세면 후보 4건은 전부 한 줄 툴팁입니다(스케줄러 희망근무 셀 · 연차 규칙 · 직원 삭제 · 시트 상단 버튼들) - 가로 방향 충돌 처리는 없습니다 — flip은 세로 전용이라, 화면 가장자리에 붙은 트리거에 320px 멀티라인 버블이 뜨면 잘립니다. 지금 사용처 2곳이 모두 가운데 뜨는 모달 안이라 표면화되지 않았을 뿐입니다(미리보기 stage를 가운데 정렬한 것도 같은 이유). 필요해지면 그때 좌우 clamp를 넣기로 하고 한계로 적어 뒀습니다
2026-07-21 — AppHeader 배경·구분선을 화면별로 켤 수 있게 (+ 메신저 화면 light 통일) (design/employee-app)
수정
- AppHeader의
solid·divider가boolean외에() => boolean도 받습니다. employee-app처럼 헤더 인스턴스 하나가 여러 섹션을 도는 셸에서는 boolean이 무용지물이었습니다 —x-data표현식은 마운트 시 한 번만 평가되므로 첫 화면의 값이 그대로 굳어, 섹션을 바꿔도 배경이 따라오지 않습니다. 함수로 넘기면appHeaderRootClassgetter가 매번 다시 읽어 화면별로 갈립니다. 기존 boolean 호출부는 그대로 동작합니다(옵션 타입만 넓힘) - 판단 기준을 문서에 적었습니다 — 본문 맨 위가 흰 면(서브탭 바·카드)으로 시작하면
solid로 이어 붙이고, 회색 셸이 그대로 이어지면 기본형(투명)을 둡니다
수정
- 헤더 탭의 활성 밑줄이 웹 탭보다 얇게 보이던 문제.
.ds-tab-underline의 기본 오프셋(md-12px/ ml-15px)은nav.ds-tab-nav의 하단 1px 구분선 위에 겹치라고 1px 더 내려간 값입니다. 헤더 탭 줄에는 그 선이 없고 대신overflow-y: clip이 걸려 있어서, 2px 밑줄의 아래쪽이 그대로 잘려 나갔습니다 — 브라우저에서 실측하니 밑줄이 아이템 바닥을 0.8px 넘어가 그만큼 깎여 2px가 ~1.2px로 렌더됐습니다..ds-app-header__tabs안에서만 1px씩 올려(-11px/-14px) 밑줄 전체가 아이템 박스 안에 들어오게 했습니다. 수정 후 재측정: 헤더-0.2px(= 완전히 안쪽), 웹 탭과 동일한 굵기 - 모바일 탭에서 hover·pressed 배경 틴트 제거. 터치 기기에서
:hover는 탭한 뒤에도 다음 탭을 누를 때까지 남습니다(iOS·Android 공통) — 그래서 선택하지도 않은 탭에 회색 박스가 붙어 있는 것처럼 보였습니다..ds-app-header__tabs안에서만 배경을 투명으로 고정했습니다(웹 TabNav는 그대로 틴트 사용). 선택 피드백은 글자색 + 하단 밑줄이 담당합니다.@media (hover: hover)로 가르지 않았습니다 — 그러면 데스크톱 브라우저로 앱을 여는 개발·검수 화면과 실기기가 갈립니다. 문서: AppHeader — 배경 틴트 없음, TabNav 상태 목록에도 예외 표기 - 헤더 우측 아이콘 버튼을 알림벨과 같은 톤으로.
getActions버튼(.ds-btn--icon --muted --lg)과 알림벨은 박스 36px · 글리프 24px까지는 같은데, 색이neutral-400(벨은 500)이고 레지스트리 아이콘 선이 1.5(벨의 path는 2)라 나란히 두면 액션만 옅고 가늘게 보였습니다..ds-app-header__actions안에서만 벨에 맞춰 색·선 굵기·hover(배경 원 없이 색만 진해짐)를 통일했습니다. 특성도로 이깁니다 —.ds-btn--icon.ds-btn--muted와.ds-btn--icon:hover:not(...)가 파일 뒤쪽에 있어 클래스 2개짜리로 쓰면 순서에서 집니다(새!important0). 실측 확인: 액션·벨 모두36×36박스 /24×24글리프 /rgb(94,94,94)/ path stroke2px - 확인 방법. 같은 마크업을
nav.ds-tab-nav와.ds-app-header__tabs두 곳에 넣고 앱 빌드 CSS(Tailwind 유틸 포함)를 물린 뒤getBoundingClientRect로 쟀습니다. ⚠️design-system.css만 걸고 재면relative·inline-flex유틸이 없어 밑줄의 기준 박스가.ds-tab-item으로 바뀌어 측정값이 통째로 달라집니다(처음에 이걸로 한 번 틀렸습니다)
추가
showBack옵션 —onBack이 있어도 false면 그 화면만 1depth로 렌더합니다. 앱 셸은 헤더 인스턴스가 하나인데 목록(1depth)과 상세(2depth)를 오가므로, "onBack이 있으면 2depth" 규칙만으로는 깊이를 바꿀 수 없었습니다(onBack유무는 마운트 시점에 굳습니다). 제목 강조(--depth2)도onBack유무가 아니라 실제 백버튼 렌더 여부를 따르도록 고쳤습니다 — 안 그러면 목록 화면에서 날짜만 화면 제목처럼 커집니다showBell·showAvatar도() => boolean허용 — 상세에서 벨·아바타를 끄려면solid와 같은 이유로 함수가 필요합니다onBellClick옵션 — 넘기면 공유 벨의 드롭다운을 렌더하지 않고 그 핸들러만 부릅니다. 모바일에서 알림 팝오버는 어차피 화면을 거의 다 덮어서, 직원 앱은 알림을 2depth 화면으로 엽니다. 미읽음 배지는 두 모드가 같은notificationBell스코프 안에 있어 카운트 폴링이 그대로 유지됩니다. 문서: AppHeader — 알림 벨- ⚠️ 셸이 연 depth의 뒤로가기를
app-depth-back이벤트로 돌리면 엉뚱한 섹션이 자기 goBack()을 실행합니다(직원 앱은 메신저가 그 이벤트를 듣습니다). 셸이 연 화면은 셸이 직접 닫습니다. 같은 이유로 메신저의x-effect가setDepth(null)로 모든 depth를 밀던 것도kind가messenger*일 때만 닫도록 좁혔습니다 — 안 그러면 알림 화면이 열리자마자 닫힙니다
- ⚠️ 셸이 연 depth의 뒤로가기를
- 문서에 헤더 하나가 목록과 상세를 겸할 때 절 추가 — 상태는 라우터 스토어에, 동작(뒤로가기)은 window 이벤트로 넘기라는 원칙 포함
ic-message-plus아이콘 신설 — 말풍선 + 안쪽 플러스("대화를 새로 만든다"). 말풍선 path는ic-message와 동일하고 중심에 6단위 십자만 얹었습니다AppHeader.test.ts신설(12케이스) — 함수형 옵션이 매번 다시 읽히는지, 깊이 판정과 제목 강조가 함께 움직이는지, 탭이 있으면 하단 8px이 붙지 않는지를 잠급니다
메모
- 직원 앱 메신저 탭(대화/연락처)을 본문 → 헤더로 이관(ml 48px). 본문에
nav.ds-tab-nav를 두던 구조라 모바일 상단 내비게이션은 헤더와 탭이 한 컴포넌트라는 규칙을 어기고 있었습니다(결재 탭은 이미 이관 완료). 함께 있던 「+ 그룹 대화」 버튼은 헤더 우측 아이콘 액션(ic-message-plus)으로 옮겼습니다. 그룹 선택 모드의 컨텍스트 바(취소 / n명 / 그룹 만들기)는 탭이 아니라 선택 상태 전용 툴바라 본문에 남고, 그동안 헤더 탭과 그룹 액션은 감춥니다 — 예전에도 탭 줄 자리를 이 바가 통째로 차지했습니다 - 그룹 대화 만들기(상대 고르기)도 2depth 화면으로. 「+」를 누르면 뷰는 연락처 그대로지만 헤더가 백버튼+「그룹 대화」로 바뀌고 하단 내비가 숨습니다. 본문 툴바의 「취소」 버튼은 제거 — 백버튼이 곧 취소이고(
goBack()이groupSelectMode를 가장 먼저 풉니다) 둘 다 두면 같은 동작이 한 화면에 두 번 나옵니다. 헤더가 없는 chat-widget에서는 그 버튼이 유일한 취소라 그대로 둡니다 messengerGroupSelect스토어 거울 제거. 그룹 선택이 depth로 승격되면서 탭·액션을 감추던 두 군데 가드가isDepth하나로 덮여 읽는 곳이 없어졌습니다 — 남겨두면 "왜 있지" 하는 죽은 상태가 됩니다- 헤더가 탭을 품으면 헤더 하단 8px은 붙지 않습니다(요청받아 확인).
(solid || divider) && !hasTabs조건이라 메신저(배경 solid + 탭)는--pad-bottom없이 탭 줄이 바로 본문에 이어집니다. 테스트 4케이스로 잠갔습니다 - 직원 앱: 메시지방·공지 상세를 2depth 페이지로 전환. 자체 헤더 줄 대신 공유 AppHeader가 백버튼·제목·액션(검색·멤버)을 그리고, 하단 내비는 숨습니다. 상세 상태는
router스토어의depth가 소유하고(헤더는 앱 셸 스코프, 상세는 섹션 스코프라 로컬로 공유 불가 —approvalTab과 같은 이유), 백버튼·헤더 액션은 window 이벤트(app-depth-back등)로 섹션에 전달합니다 - 공지 상세: 「확인 했어요」를 하단 고정 + 끝까지 읽어야 활성화. 버튼이 본문 스크롤 안에 있어 긴 공지는 끝까지 내려야 보였습니다 → 스크롤러 바깥 형제로 빼서 화면 하단에 고정하고(
position: fixed가 아니라 채팅 입력줄과 같은shrink-0패턴), 본문을 끝까지 본 뒤에만 활성화되도록 했습니다. 함정 둘: ① 내용이 짧아 스크롤이 없는 공지는scroll이벤트가 영원히 안 와서 버튼이 영영 비활성이 됩니다 → 열릴 때 한 번 직접 잽니다. ② 그 측정을 화면에 붙기 전에 하면clientHeight·scrollHeight가 둘 다 0이라 "끝까지 봤다"가 참이 됩니다 →clientHeight === 0이면 재지 않습니다. 두 함정 모두Messenger.test.ts(8케이스)로 잠갔습니다 - 공지 상세 메타를 DS 라벨로 교체(
--md24px/13px) · 날짜text-b4. 중요도·분류 칩이 유틸 조합(px-2 py-0.5 rounded+bg-…/text-…+text-[10px])으로 손수 그려져 있었습니다 →.ds-label --md --square+ 색 변형 클래스로 바꿨습니다(라벨은 색 변형으로만 칠하는 것이 강제 규칙).noticePriorityColor()도 Tailwind 유틸 대신ds-label--red/--primary/--gray/--light-gray를 반환합니다. 분류 칩은 중요도 칩과 바로 옆에 붙어 있어 같은 회색이면 구분이 안 되므로--line(테두리)로 갈랐습니다 - 공지 제목
text-h3(24px) · 본문text-b3(16px). 제목은<h2 class="text-base">였는데 16px이 아니라 h2 기본값으로 렌더되고 있었습니다 — 공유 DS의 unlayeredh1~h6규칙이 Tailwind 유틸을 이기고, 앱 가드는 DS 토큰 클래스만 덮기 때문입니다(공지 목록의<p class="text-xs">와 같은 함정) - ⚠️ 메신저 마크업은 데스크톱 chat-widget(7개 앱)과 공유입니다. 위젯에는 상단 헤더가 아예 없어서 자체 헤더 줄을 지우면 대화방에서 빠져나올 방법이 사라집니다 →
hostHeader옵션(기본 false)으로 employee-app에서만 숨깁니다. 또한 safe-area 하단 패딩을 갖고 있던 게 하단 내비라, 내비가 숨는 화면에서는 채팅 입력줄이.safe-bottom을 직접 가져야 홈 인디케이터에 깔리지 않습니다 - 죽은 헤더 교체 감지기 제거 —
.safe-top요소를 찾아 앱 헤더를 숨기려던MutationObserver(섹션 콘텐츠 전체를subtree+attributes로 관찰)가 남아 있었는데, 결과값sectionHeaderReplacementVisible을 읽는 템플릿이 하나도 없었습니다. 이번 2depth 구조가 그 의도를 제대로 대체하므로 함께 걷어냈습니다 - 직원 앱 메신저 화면을 전부 light(흰 면) 한 장으로 통일했습니다. 이전에는 투명 헤더(셸 회색) → 서브탭 바(neutral-25) → 대화 목록(neutral-50)으로 세 단계의 회색이 겹쳐 헤더 아래에 회색 띠가 남았습니다. 헤더는 메신저에서만
solid를 켜고,.messenger-subnav-bar는 neutral-25 → 흰색, 대화 검색·목록의bg-neutral-50과 메시지 검색 바의bg-gray-50은 걷어내 messenger-content.html 루트가bg-white한 곳에서 면을 소유합니다 - 공지 구간의 카드를 걷고 메시지 구간과 같은 눈금에 올렸습니다. 공지만 흰 카드(
mx-4+px-4+ 행px-2)로 묶여 있어 제목 열이 메시지 이름 열보다 안쪽으로 밀려 있었습니다 → 구간px-4 py-2· 행px-3 py-2.5 gap-3· 아이콘 40px로 맞췄습니다. 이 과정에서<p class="text-xs">가 12px이 아니라 16px로 렌더되던 것도 드러났습니다 — 공유 DS의 unlayeredp { font-size }가 Tailwind 유틸을 이기고, employee-appmain.css의:is(p,h1..h6).text-*가드는 DS 토큰 클래스만 덮기 때문입니다(text-xs·text-[10px]은 무방비).<span>/<div>+text-b4/text-b6로 바꿔 메시지 행과 같은 스케일이 됐습니다 - 행에서 테두리와 좌우 여백을 걷어 폭을 넓게 씁니다. 필수 대화방의
border border-orange-100을 없애고(배경 틴트만으로 구분) 행의px-3도 뺐습니다 — 라인이 사라지면 행을 안쪽으로 물릴 이유가 없습니다. 이제 좌우 여백은 구간의px-4(16px) 하나만 남아, 아바타·시각·읽지 않음 배지가 각각 12px씩 넓게 자리합니다. 공지·채널 행도 같은 규칙입니다 - 공지 목록의 중복 마크업 한 벌 제거.
slice(0, 3)과slice(3)이 완전히 같은 행 마크업 두 벌이라 한쪽만 고치면 「더보기」 이후 행만 조용히 갈라졌습니다 →slice(0, showAllNotices ? length : 3)한 벌로 합쳤습니다(35줄 감소). 미확인 공지 아이콘의 하드코딩fill="#ea580c"(구버전 오렌지)도fill-orange-500으로 교체 - 회색 배경을 걷으면
hover:bg-white가 같이 죽습니다. 채널·대화 행과 메시지 검색 결과가 회색 위에서 흰색으로 밝아지는 hover였는데, 바닥이 흰색이 되면서 아무 변화가 없어집니다 →hover:bg-neutral-50 active:bg-neutral-100으로 뒤집었습니다. 연락처 행은 원래부터 틴트 오버레이(.msg-row-select)라 영향이 없습니다
2026-07-20 — 모바일 앱 셸 크롬 2종 신설 (AppHeader · BottomNav) (design/employee-app)
추가
- AppHeader · BottomNav 신설. 그동안 모바일 상단 바와 하단 탭바는 DS에 정의가 아예 없었고(SoT에
@media0건 ·safe-area-inset0건), employee-appmain.css가.bottom-nav/.bottom-nav-item/.unread-badge/.safe-top으로 자체 보유하고 있었습니다. 정의를 공유 DS로 올려.ds-app-header*(7규칙) ·.ds-bottom-nav*(5규칙)로 세웠습니다. 현재 소비 앱은 employee-app 하나이지만, 모바일 프리미티브가 DS 밖에 있던 구조 자체를 해소하는 것이 목적입니다 - 설정 기반(config-driven) 구조. 탭 추가·삭제·순서 변경·배지·권한 숨김이 호출부 배열 수정만으로 끝납니다(
apps/employee-app/src/nav-config.ts). 템플릿과 컴포넌트는 손대지 않아요. 항목 옵션:icon/iconSvg(이중 주입) ·activeIcon/activeIconSvg·badge()·hidden()·disabled - 활성 상태 fill 아이콘. 선택된 탭은 색뿐 아니라 글리프가 outline → solid로 바뀝니다. 활성 변형은 선택 사항이라 지정하지 않은 항목은 기본 아이콘으로 폴백해요
- 배지 일반화. 기존
.unread-badge는 메신저 전용이었지만,badge()는 어느 탭에나 붙고0·null·""반환 시 자동으로 숨습니다(badgeMax초과분은"99+") - 셸 폭 토큰
--ds-app-shell-max-width(기본 430px) — 하드코딩 430px를 대체
수정
- AppHeader 기본형 높이 57 → 48px.
padding: 12px 16px 8px+ 흰 배경 +border-bottom: 1px이던 것을padding: … 16px 0으로 바꾸고, 배경·구분선을 기본형에서 걷어내 옵션으로 돌렸습니다. 헤더가 셸 배경과 이어지고, 아래 콘텐츠와의 간격은 콘텐츠가 소유합니다(헤더에 하단 패딩을 되돌리면 화면마다 간격이 이중으로 붙습니다) - 배경(
solid)·하단선(divider)은 서로 독립 축. 넷 중 아무 조합이나 가능하고 기본형은 둘 다 off입니다 —.ds-app-header--solid(흰 배경) /.ds-app-header--divider(하단 1px). 앱 CSS에서.ds-app-header에 배경을 덧칠하면 변형이 갈라지므로 옵션으로만 켜도록 문서에 못박았습니다 - 하단 여백 8px은 조건부로 되살아납니다. 헤더가 배경이나 선을 가져 하나의 "면"이 되면 콘텐츠와의 여백을 헤더가 직접 가져야 하므로
--pad-bottom(8px)이 붙습니다. 단 탭 줄이 있으면 붙지 않습니다 — 탭이 그 아래를 채우므로 더하면 사이가 벌어집니다. 조건은(solid || divider) && !hasTabs이고, 판정은 컴포넌트가 하고 CSS는 결과 클래스만 받습니다(:has()의존 회피). 결과 높이: 기본형 48 · 배경만 56 · 선 포함 57px — 초기 구현의 57px이 정확히divider+ 하단 8px 조합이었던 셈입니다 - AppHeader에 1depth / 2depth 도입.
onBack을 넘기면 2depth — 좌측에 백버튼(.ds-app-header__back, chevron-left 24px)이 붙고.ds-app-header--depth2가 루트에 걸려__leading이 보조 텍스트(b4·neutral-400)에서 화면 제목(b3·semibold·neutral-900)으로 바뀝니다. 별도depth옵션 대신 **"뒤로 갈 곳이 있으면 2depth"**로 판정해 호출부가 한 옵션만 넘기면 되게 했습니다. 백버튼+제목 묶음은.ds-app-header__lead가 담당 - 깊이별 시각 예시를 문서에 고정 렌더로 넣었습니다 —
DemoAppHeader에depth·tabs·bell·avatarprop을 추가해, 체크박스를 켜지 않고도 1depth / 2depth / 2depth+탭 줄을 나란히 비교할 수 있습니다(ASCII 다이어그램을 실제 렌더로 교체). 3depth 이상도 2depth와 동일 형태 — 깊이를 세지 않고 "뒤로 갈 곳이 있는가"로만 판정합니다 - 메신저 아이콘 점 3개 정렬. 말풍선 원의 중심은
x=11.25인데 점 3개는 Heroicons 원본 좌표(평균12.375)를 물려받아 1.125 오른쪽으로 치우쳐 있었습니다(말풍선만 다른 path로 교체되고 점이 따라오지 않은 흔적). 시작점M8.625→M7.5로 세 점 중심을7.125 · 11.25 · 15.375로 맞췄습니다 - 알림벨 글리프 확대를 클래스 훅으로 이관. 앱 CSS의
[x-data="notificationBell"] > .relative > button > svg는 공유 템플릿의 4단 중첩 구조에 의존해 마크업이 바뀌면 조용히 깨졌습니다 →.ds-app-header__bell로 대체 - messenger-content.html의
text-[9px]→text-c2(10px). 9px는 타이포그래피 스케일에 없는 값이고 employee-app 실렌더 최소값이었습니다.<span>이라 unlayeredp규칙에 눌리지 않고 실제로 9px로 렌더되던 자리예요
문서
- 내비게이션 섹션에 두 페이지 추가.
.ds-tabbar(상단 탭)와.ds-bottom-nav(하단 탭바)의 혼동,PageHeader(데스크톱)와AppHeader(모바일)의 혼동을 각 페이지의규칙 (강제)에 트랩으로 명시 - 내비게이션 사이드바를
Web/Mobile (App)2계층으로 재편. 카테고리에(App)을 명시한 이유는 반응형 모바일 웹과 구분하기 위해서입니다 — 좁아진 데스크톱 화면은 Web 컴포넌트 + 브레이크포인트 규칙이 담당하고, 이 그룹은 Capacitor 네이티브 앱 셸만 다룹니다 - 모바일 상단 내비게이션은 헤더와 탭이 한 컴포넌트. 작업 중간에
TabNav (Mobile)문서를 따로 만들었다가, 모바일에서는 헤더와 탭이 같은 것이라는 정리에 따라 그 문서를 없애고 AppHeader가 탭 줄까지 품도록 통합했습니다.getTabs를 넘기면 헤더 줄 아래.ds-app-header__tabs가 렌더되고, 생략하면 헤더만 남습니다. 웹처럼 본문에nav.ds-tab-nav를 두지 않습니다 - 탭 아이템 스타일은 웹 TabNav와
.ds-tab-item을 공유합니다(현재--ml48px 고정, 모바일 전용 사이즈 베리에이션은 미정). 활성 클래스도tab-active로 동일 — BottomNav의is-active와 다르니 주의 - 활성 밑줄이 세로 스크롤을 만드는 문제를 DS 안에서 막았습니다.
.ds-app-header__tabs가overflow-y: clip을 자체 보유하므로 앱 로컬 보정이 필요 없습니다. 웹은 반대로.tabnav-unclip으로 클립을 푸는 opt-in 보정을 쓰므로, 두 규칙을 서로 복사하면 그대로 버그입니다(양쪽 문서에 경고 유지)
메모
- 라이브 미리보기 신설 —
DemoAppHeader·DemoBottomNav. docs theme이 design-system.css 전체를 import하므로 데모가 실제.ds-*클래스로 렌더됩니다(시각은 프로덕션과 동일하고, 재구현되는 건 상태 로직뿐). BottomNav 데모는 활성 fill 아이콘을 토글해 outline과 직접 비교할 수 있고, AppHeader 데모는 슬롯(액션·벨·아바타)을 켜고 끄며 우측 고정 순서를 확인할 수 있습니다. 대신 각 페이지에 **「데모의 한계」**를 명시했습니다 — safe-area 패딩·네이티브 키보드 숨김·벨 드롭다운·오버플로 클립 조건은 브라우저에서 재현되지 않습니다 - employee-app
main.css76줄 감소 - 남은 모바일 프리미티브 부채(DS 정의 없음): 바텀시트 · 액션시트 · 토스트 · 스피너/스켈레톤 · 빈 상태 · 카드 · 아바타 · safe-area 유틸
2026-07-24 — 로그인 BI 규칙을 opt-in 으로 완화 (design/employee-app)
변경
- 「로그인은 텍스트 고정」을 「기본은 텍스트, 심볼은 앱이 켜서 쓴다」로 바꿨습니다. 2026-07-20에 하지 않기로 못박았던 항목인데, 직원 앱이 제품 판단으로 로그인 상단에 Works 심볼을 놓기로 하면서 규칙이 실제 화면을 막고 있었습니다. 금지 대신 확장 가능한 형태로 열었습니다 —
loginForm({ showBrandLogo: true })로 그 앱만 켜고 기본값은false라, 안 켠 앱 7개는 종전과 100% 동일한 텍스트 제목입니다. - 열되 경계는 남겼습니다. ① 로그인에 놓을 수 있는 건 심볼뿐이고 가입의 락업을 옮기는 것은 여전히 금지입니다(워드마크가 들어가면 바로 아래 부제의 서비스명이 두 번 적힙니다). ② 심볼은
$store.hereby.brandFamily를 따라가고 자산 키를 문자열로 박지 않습니다. ③ 켠 화면은 심볼 → 제목 → 부제 3단이고, 「로그인 제목 = 앱 이름」 규칙은 그대로입니다 — 바뀐 건 심볼 노출 여부뿐입니다. - 「앱 이름 자체가 패밀리 브랜드인 앱」을 규칙에 명시했습니다. 직원 앱의 제품명은
hereby Works(국문hereby 웍스)라 앱 이름과 브랜드가 같은 말입니다. 그동안 규칙이 admin(「관리자」 ≠hereby Medical)처럼 둘이 다른 경우만 상정하고 있어서, 직원 앱 로그인 제목이 규칙 위반처럼 읽혔습니다. 예외가 아니라 일치하는 경우임을 적어 오독을 막았습니다. 아울러nav.employee_app은 「직원 앱」이라는 일반 서술어라 제품명이 아니라는 점도 함께 적었습니다. - 같은 내용을
CLAUDE.md의 Brand/BI 한 줄 가드레일에도 반영했습니다(정본은brand.md).
수정
- 심볼을 켠 로그인 제목이 「브랜드 조판」대로 렌더되지 않던 문제. 제목 전체에
font-brand한 장만 걸려 있어서, 영문hereby는 Geist SemiBold 600으로 맞았지만 국문 서비스명웍스는 스택 폴백의 시스템 한글 서체 · weight 600 으로 떨어졌습니다(정본은 Pretendard Bold 700). 한 요소에는 서체·weight 를 하나씩밖에 못 주므로loginForm에titlePartsgetter 를 두어 한글 덩어리만 잘라 내고, 조각별로font-brand font-semibold/font-pretendard font-bold를 물렸습니다. 실측 확인:hereby=Geist600,웍스=Pretendard Variable700. 한 워드마크 안에서 두 폰트가 섞이는 것이 정상이라는 조판 규칙(브랜드)을 그대로 따른 것입니다- ⚠️
x-for템플릿 안팎에 공백·개행을 넣으면 텍스트 노드가 함께 복제돼 낱말 사이가 벌어집니다 — 한 줄로 붙여 씁니다 - ⚠️ 제목 크기는 반드시 DS 토큰 클래스(
text-h2등)로 줍니다.tokens.css의 unlayeredh1{font-size:32px}가@layer utilities의 Tailwind 유틸을 이겨서text-xl같은 클래스는 조용히 무시됩니다
- ⚠️
2026-07-24 — 모바일 화면 배율 라벨·디폴트 재정의 (design/employee-app)
변경
- 글자 크기 배율 3단계 라벨을 한 칸씩 내리고 디폴트를 가운데로 — 이전
기본(×1.0) / 크게(×1.2) / 매우 크게(×1.4), 디폴트 ×1.0 에서 →작게(×1.0) / 기본(×1.2) / 크게(×1.4), 디폴트기본(×1.2) 로 바꿨습니다. 웹은 정의 타이포(×1.0) 기준으로 개발하고, 모바일은 최초 진입부터 한 단계 키운 배율(×1.2)을 기본으로 주어 손안의 화면에서 읽기 편하게 시작합니다. CSSzoom값과data-font-scale키(base/lg/xl)는 그대로라 기존 저장값·CSS 마이그레이션이 없습니다(저장소의base=작게,lg=기본,xl=크게 로 라벨만 이동).
문서
- 타이포그래피 문서에 모바일 화면 배율 섹션 신설 — 작게/기본/크게를 실제
zoom으로 나란히 렌더한 세 배율 비교 미리보기와 구현 위치를 명시했습니다.
2026-07-24 — 표 가로 스크롤바 전역 적용 (design/apply-feedback-uiux-2)
추가
- 가로로 넘치는 모든 표(리스트)에 위·아래 가로 스크롤바를 자동으로 붙입니다 —
setupTableXScroll()(@hereby/components)을 각 앱main.ts에서 1회 호출하면 DOM을 훑어 붙이고, Alpine이 나중에 그리는 표·모달 안 표까지 MutationObserver로 따라갑니다. 적용: admin · team-manager · hr-system · general-affairs · management-dashboard · inventory. 화면 마크업은 한 줄도 고치지 않았습니다 - 출발점은 윈도우입니다. 윈도우에는 표를 가로로 스크롤할 입력 수단이 사실상 없습니다 — 휠은 세로 전용이고
Shift+휠은 대부분 모릅니다. 네이티브 가로 스크롤바는 스크롤 컨테이너 바닥에만 생기는데 표가 길면 그 바닥이 화면 밖이라, 「옆에 열이 더 있다」는 사실조차 보이지 않습니다. 그래서 위에도 하나 더 두고 둘 다 상시 노출입니다 - 레이아웃 높이는 0입니다 — 바(10px)는 같은 크기의 음수 마진을 받아 표 위에 겹쳐 그려집니다. 표가 아래로 밀리지 않습니다. 겹치는 자리가 sticky thead(z 30)·sticky 헤더 컬럼(z 31)이라
--ds-z-table-xscroll(36) 토큰을 새로 뒀습니다 - PC·태블릿(≥768px =
md)만. 모바일은 터치 스크롤이 기본이라 바를 띄울 이유가 없습니다. CSS 미디어쿼리와 JS 양쪽에서 막습니다
메모
- 네이티브 스크롤바(
::-webkit-scrollbar)를 쓰지 않기로 했습니다. 처음엔 그렇게 만들었다가 되돌렸습니다 — macOS는 오버레이라 스크롤하는 동안에만 보이고(상시 노출 요구 위반), Chrome은 같은 요소에scrollbar-width/scrollbar-color가 있으면::-webkit-scrollbar규칙을 통째로 무시하며, OS의 「스크롤 막대 표시」 설정에도 흔들립니다. 실제로 헤드리스 Chromium에서는 격리 테스트로도 커스텀 스크롤바가 전혀 렌더되지 않았습니다. 트랙(.ds-xscroll) + 썸(.ds-xscroll__thumb)을 직접 그리고 포인터 드래그를 직접 처리하는 쪽이 어느 OS에서도 동일합니다 - 가로 flex/grid 부모 아래 래퍼에는 붙지 않습니다. 바는 래퍼의 앞뒤 형제로 들어가는데, 부모가 가로 flex면 형제가 새 「열」이 되어 배치가 무너지기 때문입니다. 안전하게 건너뜁니다
- 세로 flex 부모에서는
flex: 0 0 auto가 필수였습니다. flex 아이템은 기본이flex-shrink: 1이라, 컨테이너가 꽉 차면 바가 10px → 7px로 눌려 음수 마진과 어긋났습니다(실측으로 발견) - 제외 대상: 근무 스케줄표(
.sched-grid, Table 2)와 급여 시트(.sheet-grid/.sheet-scroll) — 자체 가상 스크롤 정책이 따로 있습니다. 화면별 해제는data-ds-xscroll="off"
2026-07-20 — BI 적용 범위 결정 기록 (design/apply-BI-UIcomponents)
메모
남은 항목을 훑고 하지 않기로 한 것을 정본에 못박았습니다. 셋 다 「빠진 것」처럼 보여서, 기록해 두지 않으면 다음 사람이 되돌립니다.
- 로그인 화면은 텍스트 유지 — 가입만 BI 락업이라 비대칭이지만 의도된 것입니다. 로그인은 「어느 앱에 들어가는가」가 먼저라 앱 이름이 큰 제목을 차지하고, 가입은 「어느 서비스에 계정을 만드는가」가 먼저라 상단이 락업입니다
- hr-system 헤더는 대상이 아닙니다 — 헤더가 없는 것이 의도된 설계입니다(시트 화면을 세로로 최대한 쓰려고 상단바를 걷어냄). 「브랜드가 빠졌다」고 헤더를 새로 만들지 마세요
og:image는 썸네일 제작 전이라 보류 — 파비콘과 달리 심볼을 그대로 못 씁니다.1200×630가로 캔버스에 락업을 배치해야 해서 배경·크기·앱 이름 표기가 전부 디자인 결정입니다. 시안이 나온 뒤 착수합니다
2026-07-20 — 파비콘 전 앱 적용 + 헤더·LNB 브랜드 정본화 (design/apply-BI-UIcomponents)
추가
- 파비콘을 9개 앱 전부에 적용 — 그동안 11개 앱 중 1개에만 있어 브라우저 탭에 기본 아이콘이 떴습니다. 자산은
packages/static/brand/favicon-hereby-<패밀리>.svg하나뿐이고(앱마다 복사하지 않습니다) 심볼만 씁니다 — 파비콘 크기에서는 워드마크가 읽히지 않기 때문입니다. 직원 앱은 Works, 나머지는 Medical - 경로는 상대경로여야 합니다.
href="static/brand/…"로 걸어야 CDN(STATIC_URL)이 있으면 플러그인이 리라이트하고, 없으면 앱 base(/앱이름/) 기준으로 해소됩니다. 절대경로/static/…는 후자에서 404 납니다 — 실제 빌드로 두 경우를 모두 확인한 뒤 규칙으로 적었습니다
수정
- team-manager 헤더의 구버전 마스터 락업 교체 —
viewBox="0 0 125 32"마스터 스우시를 인라인하고 있어, admin(Medical 심볼)에서 「내부 결재」를 단독 접속하면 로고가 바뀌어 보였습니다. 게다가 그 SVG 는 그라디언트에var()를 써서, 자산이 CSS 변수 없는 곳으로 나가면 심볼이 검붉게 뭉개지고 워드마크가 사라집니다(재현 확인). 정본(icons.ts)의 Medical 락업을 꺼내 쓰도록 바꿨습니다 — 52줄 인라인 SVG 가 6줄로 줄었습니다 - LNB 브랜드의 하드코딩 제거 —
workspace-switcher.html(공용)과 admin 자체 인라인이 심볼 키와 서비스명을 문자열로 박고 있었습니다. 「공용 템플릿에서 브랜드를 하드코딩하지 않는다」를 이 PR 이 직접 명문화해 놓고 정작 이 파일이 어기고 있던 상태입니다. 둘 다$store.hereby.brandFamily·brand를 읽도록 바꿨습니다 - 직원 앱 PWA 아이콘 10종 재생성 — 기존 아이콘은 hereby BI 가 아예 아닌 다른 디자인(사원증 모양) 에 구버전 오렌지였습니다. Works 심볼로 다시 뽑고, 마스크 아이콘(
maskable)은 어떤 모양으로 잘려도 살아남도록 안전영역 62% 안에 넣었습니다.theme_color도 구버전#ea580c→ 토큰값#f66014
2026-07-20 — 가입 화면을 BI 락업으로 + 앱에서 쓰는 락업 자산 등록 (design/apply-BI-UIcomponents)
추가
icons.ts에 락업 2종 등록 —logo-lockup-hereby-medical(865×130) ·logo-lockup-hereby-works(790×130). 그동안 앱에는 심볼만 있어 락업을 쓸 수 없었습니다. 워드마크는 저장소 폰트로 path화했고(정본 4종 재현으로 검증된 생성기),viewBox로 비율만 고정해 크기는 쓰는 쪽 CSS가 정합니다- 스토어에 BI 자산 선택 축 2개 추가 —
brandFamily(이 앱의 패밀리 키) ·linkedBrandFamily(연동되는 상위 서비스). 기존brand가 사람이 읽는 표기라면 이쪽은 자산 선택용 식별자입니다
수정
- 가입 화면 상단을 글자에서 BI 락업으로 교체 —
[상위 서비스 락업]+이 앱 표기+새 계정을 만드세요. 직원 앱은hereby Medical락업 아래hereby Works가 오고, 자기 자신이 상위인 앱(hr-system)은 아래 줄을 생략합니다 — 안 그러면 같은 브랜드가 위아래로 두 번 적힙니다 - 직원 앱 가입 화면의
hereby Medical오표기 해소.RegisterForm의 브랜드 문자열 폴백(title: opts.title ?? "hereby Medical")이 인자 없이 쓰인 가입 화면에 그대로 걸려, Works 앱에 Medical이 큰 제목으로 뜨고 있었습니다. 폴백을 없애고 그 자리에 「브랜드를 글자로 박으면 다른 패밀리가 새어 나간다」를 주석으로 남겼습니다 - 강제 규칙의 「공용 템플릿 하드코딩 금지」를 글자에서 자산까지 확장했습니다
2026-07-20 — 락업 데모의 톤 폴백 버그 수정 (design/apply-BI-UIcomponents)
수정
- 배경을 다크로 바꾸면 서비스명 톤이
light가 아니라primary로 튀던 버그.dark는 어두운 배경에서 금지 조합이라 다른 톤으로 옮겨야 하는데, 데모가 무조건primary로 되돌리고 있었습니다. 패밀리 기본값이dark인 이유는 「심볼이 이미 그라디언트로 색을 지녀서 서비스명까지 오렌지면 심볼이 묻힌다」 이고 이 이유는 배경과 무관한데,primary로 튕기면 정확히 그 피하려던 상태가 됩니다. 체계 전환(setBrand)에도 같은 문제가 있어 함께 고쳤습니다 - 데모는 자산을 내려받는 곳이라 영향이 큽니다 — 다크 배경용 락업을 받으려던 사람이 의도치 않게 오렌지 버전을 받고, 파일명(
…-primary-on-dark.svg)만으로는 눈치채기 어려웠습니다
추가
- 브랜드 · BI에 「중립 톤」 개념 명시 —
dark와light는 워드마크hereby가 배경 따라 뒤집히는 것과 같은 한 짝이고,primary만 그 짝 밖에서 의도를 갖고 직접 고르는 톤입니다. 따라서 배경별 기본은 밝은 배경dark/ 어두운 배경light입니다
2026-07-20 — 브랜드 규칙을 에이전트 가드레일까지 반영 + 이메일 정정 (design/apply-BI-UIcomponents)
추가
- 「마스터 + 일반명사」 허용을 명문화 — 금지되는 건 서비스명(등록된 패밀리 브랜드)을 붙이는 것이지 뒤에 오는 모든 말이 아닙니다.
hereby System Admin·hereby Design System처럼 일반명사로 대상을 가리키는 표현은 정본이고, 구분자(·) 없이 띄어쓰기로 잇습니다. 이 문장이 없으면 다음 사람이hereby System Admin을 위반으로 보고 되돌립니다 design-tokens.md에 BI 그라디언트 hex 예외 추가 — 토큰 규칙만 읽은 에이전트가 BI 그라디언트를var()로 "고쳐서" 심볼을 검게 만드는 능동적 사고를 막습니다(데모 2종에서 실제로 났던 일). 이메일 예외도 색에만 적용되고 브랜드 표기는 규칙을 따른다는 점을 함께 못박았습니다
수정
CLAUDE.md 가드레일에 「표기」 규칙 편입. 그동안 가드레일은 락업(자산) 만 담고 있었고, 정작 이번 사고의 원인인 텍스트 표기 규칙(소문자 강제 · 저작권=마스터 · 앱 이름≠브랜드 · 자리별 기준 · 공용 템플릿 하드코딩 금지)이 통째로 빠져 있었습니다. 자산/표기 두 줄로 분리해 채웠습니다
CLAUDE.md 자신의 위반 정정 — 첫 문장이
Hereby is a multi-tenant…였습니다. 이 파일은 매 세션 자동 로드되므로 「Hereby가 정상 표기」라는 반례를 매번 주입하고 있었습니다라우터(
design-system-docs.md) 정리 — brand 행이 규칙을 4절이나 복제해 자기 원칙(「중복 = 드리프트」)을 어기고 있었고, 예언대로 이미 낡아 있었습니다(소문자·저작권 규칙 없음). 한 줄 힌트로 줄였습니다.paths:글롭에tools/**·docs/ko/design-system/**·docs/.vitepress/theme/components/**를 추가 — DS 문서·데모를 고치는 세션에 라우터가 로드되지 않던 공백을 메웠습니다이메일 7건 정정 (
email.service.ts) — 「app-wide」라던 직전 정정이 이 파일만 통째로 건너뛰어, 가입·초대·비밀번호 메일이 전부 구버전 표기로 나가고 있었습니다. 제목 3종[Hereby]→[hereby], 본문 1곳, 푸터© Hereby Platform×3 →© 2026 hereby(앱 31곳과 동일 형태). 계정 메일은 앱이 공유하므로 특정 서비스를 가리킬 수 없어 마스터가 맞습니다system-admin 로그인 부제
hereby · System Admin→hereby System Admin(구분자 제거)package.jsondescription19개 정정 — 앱 9개 →hereby Medical, 직원 앱 →hereby Works, 전 서비스 대상 도구(importer·system-admin)와 인프라 패키지 →hereby(마스터). 폐기 조합Hereby Platform도 함께 제거했습니다. 화면에 뜨지 않는 메타데이터지만, 남겨두면Hereby검색이 계속 걸려 매번 「저건 괜찮은 것」을 다시 판단해야 합니다SMS 발신 접두사 2곳 (
sms.service.ts) — 임시 비밀번호·로그인 인증번호 문자의[${organizationName || "Hereby"}]폴백. 조직명이 없을 때[Hereby]가 그대로 문자로 발송되고 있었습니다 →[hereby]. 이메일 제목과 같은 부류인데 파일이 달라 P1에서 빠져 있었습니다AI 어시스턴트 시스템 프롬프트 (
ai/prompts/system.ts) —당신은 Hereby의 HR 어시스턴트입니다. Hereby는…. 정적 문자열이 아니라 모델이 답변에서 그대로 따라 쓰는 표기라, 프롬프트가 틀리면 어시스턴트가 사용자에게 계속Hereby라고 말합니다 →hereby
메모
- 남은
Hereby는 대부분 코드 식별자(HerebyClient·HerebyTemplate·listHerebyTemplates·X-Hereby-Timestamp· 마이그레이션 클래스명)와 코드 주석이라 표기 규칙 대상이 아닙니다 - 판단이 남은 3건 — ① 시드 데이터
Hereby Demo Corporation(데모 조직 이름, 참조처는 시드 파일 2곳뿐이라 변경은 안전하지만 브랜드 표기인지 데이터인지 성격 판단 필요), ② CLI 도움말·배너(hereby CLI·=== Hereby Excel Importer ===, 개발자 대상), ③ system-admin 연동 탭 placeholderHereby production(입력 예시)
2026-07-20 — 직원 앱을 hereby Works로 통일 (design/apply-BI-UIcomponents)
추가
- 공용 템플릿용 브랜드 주입 경로 신설 —
createAlpinePlugin({ brand })→$store.hereby.brand(기본값hereby Medical).packages/components/html/의 템플릿은 8개 앱이 같은 파일을 인라인하므로 브랜드를 박아 두면 한 앱을 고치는 순간 나머지 7개가 깨집니다. 「공용 템플릿에서 브랜드를 하드코딩하지 않는다」를 브랜드 · BI의 강제 규칙(표기절)에도 추가했습니다
수정
- 직원 앱 8곳을
hereby Works로 정정 — 로그인 부제 · 공용 로딩 화면 · 「더보기」 버전 표기 ·<title>· iOS 홈화면 라벨 ·manifest(name/short_name) · CapacitorappName· 푸시 알림 제목 폴백(+push-sw.test.ts기대값) - 직원 앱은 한 앱이 자기 이름을 세 가지로 말하고 있었습니다 — 홈화면은
Hereby Works, 로딩·로그인은hereby Medical, 더보기는 다시Hereby Works. 직전 커밋이 8개 앱 로그인 부제를 일괄로hereby Medical로 맞췄는데, 직원 앱은 Works 패밀리라 그 정정이 이 앱에서만 오답이었던 것이 원인입니다 - 로딩 화면은 공용 파일이라 문자열 치환이 불가능해 위 주입 경로로 바꿨습니다. 나머지 7개 앱은 코드 변경 없이 기본값
hereby Medical을 그대로 받습니다(빌드 산출물로 확인)
메모
manifest·appName은 앱을 다시 설치해야 반영됩니다 — 홈화면 아이콘 라벨·앱서랍·런처 이름은 설치 시점에 굳습니다. 나머지(로딩·로그인·더보기·탭 제목)는 배포 즉시 반영됩니다- 검증: 직원 앱 로그인 화면에 「직원 앱」 +
hereby Works+© 2026 hereby, admin 로딩 화면에hereby Medical— 실제 렌더로 확인.push-sw테스트 4/4, 전체 빌드 21/21 green
2026-07-20 — hereby Works를 세 번째 패밀리로 등록 (design/apply-BI-UIcomponents)
추가
브랜드 · BI에 패밀리
hereby Works등록. 직전 항목이 「미결 —Works가 별개 제품명인지 판단 필요」로 남겼던 것의 결론입니다: Works는 패밀리이고, Medical과 연동되는 직원 앱입니다. 이로써 패밀리는 Medical · Sign · Works 셋이 됐고, 「등록된 것은 둘뿐」이라 못박아 둔 서술 3곳(강제 규칙 2곳 + 경고 박스)을 전부 갱신했습니다Works 심볼 확정. 그라디언트가 패밀리 정본(
#FE4D43→#FF7732→#FFAF00)과 stop·offset까지 일치하고, 그라디언트 벡터도 Medical과 동일합니다(박스 오프셋(1, 6)을 더하면 소수점까지 같은 값). 즉 패밀리 규격을 그대로 따른 자산입니다Figma export의
id를 그대로 쓰지 말라는 규칙 추가. export는paint0_linear_…·clip0_…같은 일련번호id를 다는데,id는 문서 전역이라 다른 BI 자산과 충돌합니다(실제로 났던 버그). 등록할 때lnb-hereby-<서비스>-grad로 바꾸고, 클립 사각형이 viewBox와 같은 크기면 아무것도 자르지 않으므로clipPath째 지웁니다국문 서비스명 「웍스」 확정 —
메디컬·사인에 이어 세 번째 국문 정본입니다. 조판 규격표·translate="no"규칙·서비스명 톤 등 국문 서비스명을 열거하던 자리 6곳에 반영했습니다Works 락업(영문·국문) export 수령 + 검증. 조판이 정본 그리드 위에 있음을 교차검증했습니다 — export의
hereby→ 서비스명 간격146.55 − 40.9497 = 105.6003이, 정본 규격(486 − 134 = 352@font-size 100)을font-size 30으로 환산한105.6과 소수점까지 일치합니다락업 폭 산출 규칙을 문서화(비율은 계산으로 구합니다) —
viewBox 폭 = 486 + 서비스명의 커닝 적용 advance(@ font-size 100), 올림. 저장소에 폰트가 있으므로 비율은 계산으로 구하는 값이고 Figma 프레임에서 읽어오는 값이 아닙니다. 이 규칙은 기존 정본 4종(865·746·702·659)을 전부 정확히 재현해 검증했습니다Works 비율 확정 —
hereby Works790 × 130,hereby 웍스659 × 130. 국문이hereby 사인과 같은659인 것은 한글 글리프 advance가 동일해 2글자 국문 서비스명은 항상 같은 폭이기 때문입니다path 정본도 직접 생성 가능함을 확인·문서화. 같은 절차로 정본 path 5종(
hereby·Medical·메디컬·Sign·사인)을 오차0.00으로 재현해 생성기를 검증했습니다. 생성 시 지킬 값(베이스라인100.5· 서비스명x=486· 커닝 적용 · Pretendard는wght=700인스턴싱)도 함께 적었습니다Works 심볼을
icons.ts에 등록 —logo-hereby-works. Medical·Sign과 같은 규약(viewBox="1 6 28 28")을 쓰고, Figma export의 일련번호id는lnb-hereby-works-grad로 갈랐습니다. 함께 딸려온clipPath는 클립 사각형이 심볼 박스와 같아 상단을0.001단위만 자르는 no-op(24px 렌더 기준0.0009px)이라 제거했습니다데모(
DemoBrandLogo.vue)에 Works 락업 2종 추가 — 서비스 선택기에Works가 붙고, 영문·국문 전환·톤·배경·심볼/워드마크 토글·SVG 내보내기가 모두 동작합니다(파일명hereby-bi-works-en-dark-on-white.svg형태로 자동 파생). 심볼은 데모 좌표계(x2~95/ y18.5~111.5)로 정렬했습니다
메모
- 디자인 재요청 없이 완료했습니다. 국문 서비스명 · 비율 · path를 모두 저장소의 폰트로 산출했습니다
- 생성 path가 디자인 export와 동등함을 픽셀로 확인 — 라이브 텍스트 ↔ path 차이가 Works
1.06%(영문)·1.17%(국문)로 기존 정본 Medical1.04%· Sign1.00%와 같은 수준입니다(전부 글자 경계 안티에일리어싱). 대조군 없이 절대값만 보면 오판하므로 기존 정본을 같은 방법으로 함께 재는 것이 핵심이었습니다 - 남은 것은 자산이 아니라 적용입니다 — 직원 앱이
hereby Medical(로그인 부제·공용 로딩)과 옛 대문자Hereby Works(<title>·manifest·appName·버전 표기)를 섞어 쓰는 상태. 공용loading.html은 8개 앱 공유라 앱별 파라미터화가 필요합니다 - Figma 프레임 폭을 비율로 쓰지 말 것을 danger로 명시 — 받은 export는 영문·국문이 똑같이
258 × 40인 고정 프레임이라 우측 여백이 각각26.4/59.6남습니다. 그대로 옮기면 국문 락업에 60 가까운 빈 공간이 붙습니다 - 커닝을 반드시 적용해야 합니다 — 끄면
Medical이384.00(정본379),hereby가338.30(정본328.20)으로 어긋납니다. 브라우저에서file://로 woff2를 로드해 재려다 폰트가 폴백되면 값이 통째로 틀리므로, 측정은 폰트 파일에서 직접 해야 합니다(이 작업에서 실제로 한 번 잘못 재고 정정했습니다) - 받은 export를 그대로 쓰면 3가지가 깨진다는 점을 문서에 명시 — ①
font-family="Pretendard"(등록명은Pretendard Variable, 정본이 경고해 둔 함정을 그대로 밟음), ②font-weight="bold"(정본 표기는700), ③ 영문·국문 두 파일이 같은id(paint0_linear_131_108·clip0_131_108)를 써서 한 페이지에 둘 다 넣으면 그라디언트가 충돌 - 직원 앱이 아직 Medical로 표기돼 있습니다. 직전 항목에서 8개 앱 로그인 부제를 일괄로
hereby Medical로 맞췄는데, 직원 앱은 Works이므로 그 정정이 이 앱 한 곳에서는 오답이었습니다. 로딩 화면은 8개 앱이 공유하는loading.html에 하드코딩돼 있어 문자열만 바꾸면 나머지 7개가 깨집니다 — 앱별 파라미터화가 필요합니다 - 「아직 정리되지 않은 것」의 사실오류 3건 정정 — ①
packages/static/brand/자산은 「8개 중 7개 미사용」이 아니라 8개 전부 미사용(랜딩은.svg를 참조하지 않고 path를 인라인), ②logo-hereby-sign은 「아무 화면도 안 씀」이 아니라 랜딩 데모가 심볼 path를 인라인해 이미 렌더 중(미사용인 것은icons.ts엔트리), ③ team-manager 헤더에 남은125×32구버전 마스터 락업을 별도 항목으로 분리(화면 주체가 Medical인데 마스터를 쓰는 패밀리 규칙 위반)
2026-07-20 — 브랜드 텍스트 표기 규칙 신설 + 전역 정정 (design/apply-BI-UIcomponents)
추가
- 브랜드 · BI의 「규칙 (강제)」에
표기 (락업이 아닌 일반 텍스트)절 신설. 그동안 강제 규칙은 락업(자산) 만 다뤘고, 로그인 제목·푸터처럼 브랜드명을 글자로 적는 자리에는 규칙이 없었습니다. 그 공백에서Hereby Admin·Hereby Inventory·Hereby Platform같은 마스터+서비스명 조합이 8개 앱에 퍼졌습니다 — 정작 이 조합은 락업 규칙이 이미 금지한 형태입니다 - 자리별 표기 기준을 표로 명시 — 제품 화면(로그인·로딩) =
hereby Medical/ 법인·플랫폼(©·계약서·공문) =hereby/ 전 서비스 대상 내부 도구 =hereby. 락업과 같은 기준(그 화면의 주체가 누구인가)을 텍스트에도 적용합니다 - 「© 문구는 대문자 예외」 통념 폐기. 문서에 근거가 없던 구전 규칙이었고, 저작권자는 개별 서비스가 아니라 플랫폼이므로
© 2026 hereby가 정본입니다
수정
- 로그인·가입 화면 8개 앱 전부 정정 — 제목은 i18n한 앱 이름(
nav.admin→ 「관리자」), 부제는hereby Medical.Hereby Admin·Hereby General Affairs·Hereby Inventory·Hereby Internal Approval이 전부 사라졌습니다.LoginForm.ts·RegisterForm.ts의 폴백"Hereby"도hereby Medical로 교체 - © 푸터 30곳 →
© 2026 hereby(8개 앱의 signin/signup/forgot-password/change-password + 랜딩). 서비스명이던Platform도 뗐습니다 - 공용 로딩 화면·재고 대시보드 →
hereby Medical/ system-admin(전 조직 프로비저닝 내부 도구) →hereby · System Admin/Hereby Sign→hereby Sign(등록된 패밀리, 대소문자만 정정) - i18n 키 3종 추가(ko·en) —
nav.admin·nav.employee_app·nav.module_manager. admin 로그인 제목을 하드코딩 대신 i18n으로 넣으려니 키가 없었습니다
메모
- 남은 미결: 직원 앱 하단의
Hereby Works v1.0.0—hereby Works는 등록된 패밀리가 아니라(Medical·Sign 둘뿐) 별개 제품명인지 판단이 필요해 손대지 않았습니다
2026-07-20 — LNB 브랜드를 패밀리(hereby Medical)로 전환 (design/apply-BI-UIcomponents)
수정
- LNB 헤더 심볼을 마스터 스우시 → 패밀리 Medical 심볼로 교체 —
iconFor('logo-hereby')→iconFor('logo-hereby-medical'). 「그 화면의 주체가 누구인가」 규칙에 따라, admin·총무·경영 대시보드 워크스페이스의 주체는 hereby 제품군 전체가 아니라 Medical 서비스 자신입니다. 적용처는 workspace-switcher.html(공용 파셜 — 총무·경영이 include)과 admin의 인라인.ws-brand두 곳 - 브랜드 라벨
Hereby→hereby Medical. 소문자 표기가 정본입니다(hereby는 영문 고유명사). 이에 맞춰.ws-trigger-label의text-transform: uppercase를 삭제했습니다 — 남겨두면HEREBY MEDICAL로 렌더돼 표기 규칙을 깹니다. 대문자 조판용이던letter-spacing: 0.04em도 함께 제거하고, 두 단어로 길어진 만큼nowrap+ellipsis가드를 넣었습니다 - docs 데모 2종의 인라인 심볼 동기화 — DemoLnbSidebar.vue · DemoWorkspaceSwitcher.vue. 이 과정에서 두 데모가 그라디언트
stop-color에var(--color-orange-400/yellow-400)을 쓰던 토큰 규칙 위반도 hex로 정정했습니다(BI 그라디언트는 hex 하드코딩이 정본 — CSS 변수가 없는 곳에서 심볼이 검게 렌더되므로). 그라디언트id는 문서 전역이라 데모별로 분리(demo-lnb-…/demo-ws-…)
메모
- 이 자리는 BI 락업이 아닙니다 — 「패밀리 심볼 + 텍스트 서비스명」입니다. 락업은 비율 고정(
865×130)이라 최소 높이 24px에서 폭이 160px인데, 208px LNB에서 브랜드 가용 폭은 132px입니다(.ds-lnb좌우 패딩 24 + 접기 토글 24 + gap 8 +.ws-brand좌우 패딩 20 차감). 우겨넣으면 높이가 19.8px로 떨어져 최소 높이 규칙 위반이고, 높이를 지키면 토글을 밀어냅니다. 그래서 심볼만 패밀리 정본을 쓰고 서비스명은 일반 텍스트로 뒀습니다. LNB 폭이 넓어지면 락업 승격을 재검토하세요 - 이로써
logo-hereby-medical이 첫 실사용처를 얻었습니다.logo-hereby-sign은 여전히 사용처 0이고, 마스터 심볼은 화면 사용처 0(락업 데모·내보내기 전용)이 되었습니다 - 남은 인접 부채: loading.html의
Hereby대문자 표기,WorkspaceSwitcher.tstriggerLabel의"Hereby"폴백 — 둘 다 LNB 밖이라 이번 범위에서 제외
2026-07-14 — orange 유틸리티 강제 심 제거 (「이름 ≠ 렌더 값」 트랩 해소) (design/landing-update)
수정
- design-system.css의 orange 유틸 강제 심 32규칙(전부
!important) 삭제 — blue→orange 리맵 시대의 잔재로,.text-orange-500이orange-600(#d3440e)으로 렌더되는 등 클래스 이름과 실제 값이 달랐다(랜딩 히어로 타이틀이 챕터 라벨·버튼과 다른 주황으로 보인 원인). 함께 삭제된 "Strong pass" 화석 4규칙 중.text-orange-600/700 → --ds-color-focus는 파란색을 가리키던 부비트랩(뒤 블록에 가려 미발현). 이제 Tailwind 유틸리티가@theme토큰을 직접 참조 → 이름 = 값 - 사용처 전수를 실제 렌더 값 기준으로 기계적 리네임(시각 변화 0) — 약 90파일:
text-orange-500/700→600·text-orange-800→700·text-orange-900→800·bg-orange-600→500·bg-orange-700/800/900 → 한 단계씩 하향·border-orange-600→500·hover:bg/text-orange-700→600·focus:ring-orange-400→300. 리네임 제외(심 미적용이라 원래 진짜 값): 슬래시 투명도(bg-orange-600/50) ·hover:text-orange-900·focus:border-orange-500등 기타 변형 접두사 - 랜딩의
text-orange-50012곳은 리네임 대신text-primary채택 — 디스플레이 타이틀 강조를 진짜 브랜드 주황(#f66014)으로 복원(의도된 시각 변경, 별도 승인)
문서
- Buttons · button-system-spec — 레거시 버튼 예시 표기(
bg-orange-600 px-3 …)를 새 이름(bg-orange-500 …)으로 동기화
메모
- 공유 DS의
!important부채 32건 감소. 남은 인접 부채:.text-red-600/700 → --ds-color-danger·.text-green-600/700 → --ds-color-success·.rounded/.rounded-lg강제(별도 작업)
2026-07-14 — 패밀리 BI(hereby Medical · hereby Sign) 등록
추가
패밀리 BI 체계 신설. 그동안 BI는 마스터 하나뿐이었고
hereby Medical은 "마스터 심볼 + 오렌지 도메인 워드"인 도메인 락업으로만 정의돼 있었습니다. 이번에 패밀리 사이트는 전용 심볼을 가진다는 것이 확정되어(Medical=십자, Sign=서명 획), 브랜드 · BI에 마스터/패밀리 두 체계를 세웠습니다. 락업은 3종 → 7종(마스터 3 + 패밀리 4)선택 규칙 — "그 화면의 주체가 누구인가". 같은
hereby Medical을 두 가지로 그릴 수 있게 되었으므로 선택 규칙이 필요합니다: 패밀리 사이트 자신이 주체인 자리(헤더·파비콘·앱 아이콘·로그인) → 패밀리 락업, hereby가 제품군 전체를 거느리는 자리(통합 랜딩·계약서·공문) → 마스터 락업. 도메인 락업은 폐기하지 않고 둘 다 정본으로 유지합니다패밀리 심볼 2종을
icons.ts에 등록 —logo-hereby-medical·logo-hereby-sign. 마스터 심볼과 같은 박스(x 2..95 / y 18.5..111.5)에 정렬해, 체계를 바꿔도 워드마크가 제자리에 있습니다. 아직 사용처는 0이며, 패밀리 사이트가 생길 때 붙입니다패밀리 워드 path 정본 확보 —
Sign(Geist SemiBold 600)·사인·메디컬(Pretendard Bold 700) 아웃라인을 폰트 파일에서 직접 생성. 이로써 7종 전부 path 폴백·path 내보내기가 가능합니다패밀리는 서비스명이 필수입니다. 패밀리 심볼 +
hereby만 쓰는 락업은 없습니다 — 어느 서비스인지 사라져 "마스터의 심볼이 바뀐 것"처럼 읽히기 때문입니다. 서비스명을 뗀 기본 락업(hereby)은 마스터에만 있습니다(심볼만 쓰는 파비콘·앱 아이콘은 예외 — 워드마크 자체가 없으므로)데모를 체계별로 분리(DemoBrandLogo.vue) — 두 체계는 규칙이 다르므로(특히 서비스명 필수 여부) 한 컨트롤에 섞지 않고
systemprop으로 마스터 영역 / 패밀리 영역을 나눠 심었습니다. 마스터 데모는서비스명 = 없음을 고를 수 있고, 패밀리 데모에는 「없음」이 아예 없습니다. 패밀리 데모에서Medical↔Sign을 바꾸면 심볼과 그라디언트가 함께 바뀌고, 내보내는 SVG의 그라디언트도 따라갑니다심볼만 내보내기 신설 (파비콘·앱 아이콘). 문서는 「24px 미만·파비콘·앱 아이콘 = 심볼만」을 규정해 놓고 정작 그 구성을 만들 수단이 없었습니다 — 심볼 체크박스는 심볼을 끄는 용도라 워드마크만 남았고, 그래서 락업을 받아 디자인 툴에서 잘라 써야 했습니다(자르는 순간 심볼의 크기·여백이 정본에서 벗어납니다). 데모에 「워드마크」 토글을 추가해, 끄면 정사각 심볼(
viewBox 2 18.5 93 93)이 그대로 내려받아집니다(hereby-bi-<체계>-symbol.svg). 심볼과 워드마크를 동시에 끌 수는 없고(체크박스가 잠김), 심볼만일 때는 최소 24px 경고를 띄우지 않습니다(그 하한을 벗어나는 방법이 바로 심볼 단독이므로)내보내는 SVG의 그라디언트
id를 체계별로 분리했습니다(bi-grad-master/-medical/-sign). 종전에는 모든 구성이bi-grad하나를 공유해서, 받은 사람이 마스터 락업과 패밀리 락업을 한 페이지에 인라인으로 붙이면 뒤엣것이 앞엣것의 그라디언트로 칠해졌습니다BI 심볼의 그라디언트 stop을
icons.ts에서도 hex로 통일했습니다. 마스터 심볼이var(--color-orange-400)를 쓰고 있었는데, 이는 이 페이지가 규정한 그라디언트 hex 예외 규칙과 어긋납니다(CSS 변수가 없는 곳에서 심볼이 검게 렌더). 값은 토큰과 동일해 시각 변화는 없습니다국문 서비스명에도
translate="no". 「메디컬」이 브라우저 자동번역으로 바뀌면 Pretendard Bold 700 자리에 폴백 서체가 들어와 락업이 깨집니다 — 언어별 정본은 우리가 고르는 것이지 브라우저가 바꾸는 것이 아닙니다내보내는 파일명이 구성을 담습니다(파일명 규칙). 종전에는 배경이 파일명에 없어 내용이 다른 파일이 같은 이름으로 덮어써졌습니다 — 워드마크 색이 배경에서 갈리는데(다크면 흰색, 아니면 검정)
hereby-bi-master.svg하나로 나왔습니다. 이제hereby-bi-medical-en-dark-on-white.svg처럼 체계·언어·심볼/워드마크 유무·톤·배경이 모두 들어갑니다. 배경에on-을 붙인 것은 톤(dark)과 배경(white)이 뒤섞여 읽히는 것을 막기 위해서입니다.라이브 텍스트/path모드는 일부러 넣지 않았습니다 — 내보내기는 언제나 path라 모드를 바꿔도 파일이 한 바이트도 안 바뀌고(md5 동일 확인), 이름에livetext를 넣으면 path를 담은 파일이 스스로를 라이브 텍스트라 말하게 됩니다강제 규칙 정본을 최종 점검했습니다. 본문에만 있고 「규칙 (강제)」 접이 섹션에 빠져 있던 3건을 편입 — ① 국문 서비스명에도
translate="no", ② 파비콘·앱 아이콘은 「워드마크」를 꺼서 심볼만 받고, 락업을 잘라 쓰지 않는다, ③ SVG 그라디언트id는 문서 전역이라 한 페이지에 BI 자산 둘 이상을 인라인하면id가 겹쳐선 안 된다(실제로 났던 버그). 라우터(design-system-docs.md)와 CLAUDE.md의 한 줄 가드레일에도 "패밀리는 심볼·그라디언트가 다르다" 를 명시했습니다 — 이번 작업에서 가장 놓치기 쉬웠던 지점입니다
변경 (⚠️ 기존 규칙과 달라진 것)
- ⚠️ 도메인 락업을 폐기했습니다. 마스터는
hereby하나뿐입니다. 종전에는 마스터 심볼에 서비스명을 덧붙인 도메인 락업(hereby Medical오렌지 워드 /hereby 메디컬)이 함께 정본이었으나, 서비스명과 영문/국문 축은 전부 패밀리의 것으로 정리했습니다.hereby는 영문 고유명사라 국문 마스터도 없습니다. 락업은 7종 → 5종(마스터 1 + 패밀리 4). 마스터 데모에서 서비스명·언어 선택기가 사라졌고, 죽은 path 상수(MEDICAL_D·MEDICAL_KO_D, x=473 앵커)도 제거했습니다 - 이에 따라 서비스명 색 3톤(primary·dark·light)은 패밀리 전용이 되었고, 기본값은
dark하나입니다(종전에는 "마스터=primary / 패밀리=dark"로 체계마다 달랐습니다) - 그라디언트가 두 벌이 되었습니다. 마스터
#F8884F→#FF4141→#F6BC3D(값이 토큰과 동일, CSS 각도 290°), 패밀리#FE4D43→#FF7732→#FFAF00(대응 토큰 없는 전용 색, 314°). 섞어 쓰면 다른 마크가 됩니다. 종래 "BI 그라디언트 = 토큰과 값이 같다"는 서술은 이제 마스터에만 해당합니다 - 서비스명 색의 기본값이 체계마다 다릅니다 — 마스터 도메인 워드 =
primary(오렌지), 패밀리 워드 =dark. 패밀리는 심볼이 이미 그라디언트로 색을 지니므로 워드까지 오렌지면 색이 겹칩니다. 세 톤(primary·dark·light)이 모두 정본인 것은 그대로이고, 배경×톤 금지 조합도 그대로입니다 - 문서의 「도메인 워드」 표현을 **「서비스명」**으로 통일했습니다(마스터·패밀리 양쪽을 가리키므로)
검증
- 아웃라인 생성 파이프라인을 먼저 검증했습니다. 이미 정본이 있는
Medical·hereby를 폰트 파일에서 다시 뽑아 기존 값과 대조 → 좌표가 완전 일치(481 · 29.5 · 498.5 · 518.1 …). 그 다음에야 같은 방법으로Sign·사인을 생성했습니다 - 생성한 path ↔ 라이브 텍스트 픽셀 비교 → 심볼 0.00%(정확히 일치), 워드는 글자 경계의 안티에일리어싱만. 정본과 바이트 수준으로 같은
hereby가 동일한 오차 패턴을 보이는 것을 기준선으로 삼아 판별했습니다 - Sign 심볼의
clipPath가 형태를 자르는지 확인 — 래스터 비교로는 902px 차이가 나 "자른다"고 오판할 뻔했으나(clip 레이어가 만든 안티에일리어싱), 경로의 실제 bounds가(1.0, 6.0002, 29.0, 34.0002)로 clip 사각형과 정확히 일치함을 기하학적으로 확인 → Figma 상용구로 판정하고 단일 path로 구웠습니다 - 패밀리 락업이 마스터보다 넓은 이유(
865vs852)를 규명 — 심볼/폰트 비율은 동일(0.930 vs 0.933)하고hereby와 서비스명 사이 간격만 2.2배(0.108em → 0.238em) 넓습니다. 디자인 의도로 보고 보존했습니다 - ⚠️ SVG 그라디언트
id는 문서 전역입니다. 데모를 마스터/패밀리 둘로 나누자 두 인스턴스가 같은id를 정의했고, 브라우저는 문서상 먼저 나온 정의를 써서 패밀리 심볼이 마스터 그라디언트로 칠해졌습니다. 형태(검정/흰색)만 비교하던 검증은 이걸 못 잡았고 — 색까지 비교해서야 드러났습니다.id를 체계별로 분리해 해결했고, 정규화한 패밀리 그라디언트가 전달받은 원본 SVG와 픽셀 색 평균 차이 0.04/255(11만 px)로 일치함을 확인했습니다
2026-07-13 — 브랜드(BI) Foundations 신설 + Geist 브랜드 폰트 도입
규칙 정본 편입
- BI 페이지를 「docs = 규칙 정본」 체계에 편입. 상단에
규칙 (강제) · 코딩 에이전트용접이 섹션을 신설해 조판(Geist 600 / Pretendard 700) · 폰트 실패 시 path 폴백 · 번역 금지 · 색과 배경×톤 금지 조합 · 그라디언트 hex 예외 · 형태 제약을 한곳에 모았습니다. 본문의 「하지 말 것」 절은 중복(=드리프트) 이라 제거하고 접이 섹션을 가리키게 했습니다 - 라우터(design-system-docs.md)에는 한 줄만 추가했습니다(규칙 내용 복제 없음). CLAUDE.md의 Foundations 열거에도 brand/BI를 편입
추가
- 브랜드 · BI 페이지 신설. 그동안 BI는 문서에 정의 자체가 없었고, 코드에서도 심볼이
icons.ts·랜딩 자산·team-manager 인라인 SVG 세 곳에 중복돼 있었습니다. Figma 정본(Hearby-med Design system)을 근거로 심볼·워드마크·락업 3종(기본471×130/ 영문 도메인hereby Medical852×130/ 국문 도메인hereby 메디컬733×130)을 정의하고, 두 가지 정본(path / 라이브 텍스트) 과 사용처 기준을 세웠습니다 - 조판 규격 확정 —
hereby·Medical은 Geist SemiBold 600,메디컬은 Pretendard Bold 700, 자간 0. 색은 워드마크--color-neutral-900(#1B1B1B) · 도메인 워드--color-orange-500(#F66014) — 세 색 모두 기존 토큰과 정확히 일치해 새 색을 만들지 않았습니다 - 폰트 로드 실패 시 path로 대체하는 것이 규격. 라이브 텍스트는 "폰트가 도착했을 때만 올라가는 향상"이고 기본값은 항상 path입니다.
document.fonts.load()+check()가 통과했을 때만.brand-font-ready를 붙여 텍스트로 교체하고, 실패하면 path가 그대로 남습니다 — 브랜드 폰트를 못 받은 사용자에게 다른 서체의 워드마크를 보여주는 일은 없어야 하기 때문입니다. 구현 코드는 문서에 포함 - 구성 변형 2종 정의 — 심볼 on/off(워드마크 단독 사용 / 24px 미만이면 반대로 심볼 단독), 도메인 워드 색 3종(
primary--color-orange-500·dark--color-neutral-900·light흰색)을 모두 정본으로 명시. 워드마크hereby는 배경에 따라 dark/light만 오가고, 세 톤을 오가는 것은 도메인 워드입니다 - 배경 × 도메인 톤 조합 제약 — 배경에 묻히는 조합은 금지입니다: 밝은 배경(화이트·뉴트럴)에
light❌, 어두운 배경에dark❌.primary는 모든 배경에서 안전합니다. 데모에서도 금지 조합은 버튼이 잠기고, 배경을 바꿔 현재 톤이 금지 조합이 되면primary로 자동 보정됩니다 - SVG 내보내기 — 항상 path. 데모 하단에서 지금 보고 있는 구성(락업 · 심볼 유무 · 톤)을 다운로드/코드 복사할 수 있습니다. 화면에서 라이브 텍스트를 보고 있어도 내보내지는 파일은 언제나 path입니다 — 배포된 자산이
<text>를 품고 있으면 받는 쪽 폰트 환경에 따라 워드마크가 다른 서체로 그려질 수 있기 때문입니다. 색도 hex로 실체화되어var()가 남지 않습니다(CSS 변수가 없는 곳에서 검게 렌더되는 것을 막음). 즉 라이브 텍스트는 우리 코드 안에서만, 밖으로 나가는 자산은 path로 통일 - 국문 도메인 락업의 path 정본 확보 —
메디컬을 Pretendard Bold 700 아웃라인으로 생성(Figma와 동일 좌표x=473, y=100.5, 100px). 레포의PretendardVariable.woff2@700 렌더와 픽셀 비교해 강한 차이 0.151%(전부 안티에일리어싱)로 일치 확인. advance width 259.3 → 끝 x가 732.3으로 Figma 캔버스733과 맞아떨어져 동일 글자꼴임이 교차 검증됨. 이로써 세 락업 모두 path 폴백·path 내보내기가 가능해졌습니다 - BI 색 정책 — 평면 색은 토큰, 그라디언트는 예외. 워드마크·도메인 워드는 값이 일치하는 기존 토큰(
--color-neutral-900/--color-orange-500)에 맞춥니다. 반면 심볼의 3-stop 그라디언트는 hex 하드코딩을 유지합니다 — BI 자산은 CSS 변수가 없는 컨텍스트(이메일 HTML·단독 SVG 파일·외부 배포본)에서도 열려야 하는데,var()로 두면 그런 곳에서 심볼이 검게 렌더되기 때문입니다. 값 자체는 토큰과 동일하므로, 어긋나서가 아니라 이식성 때문에 두는 예외입니다 - 워드마크 번역·현지화 금지 규칙.
hereby는 영문 고유명사이므로 브라우저 자동번역·접속 지역에 따라 바뀌면 안 됩니다 → 라이브 텍스트로 쓸 때translate="no"+lang="en"필수, 제품 UI 기본값은 번역이 원천 불가능한 path - 브랜드 폰트 Geist SemiBold 도입(Geist-SemiBold.woff2 · SIL OFL, 46KB).
--font-brand토큰 신설(tokens.css:root+design-system.css@theme→ Tailwindfont-brand유틸 자동 생성).@font-face는font-display: block— 기본값swap이면 폴백 서체가 먼저 보였다 바뀌는데, 워드마크에서 그건 느린 렌더가 아니라 브랜드 사고이기 때문입니다. 폰트는 실제로 워드마크를 그리는 화면에서만 내려받습니다(@font-face는 지연 로드) - 라이브 데모(DemoBrandLogo.vue) — 락업 3종 × path ↔ 라이브 텍스트 토글, 폰트 로드 실패 재현, 크기 슬라이더(최소 24px 경고), 보호 영역, 배경 3종
검증
- path 버전과 라이브 텍스트(Geist SemiBold) 버전을 픽셀 비교 → 형태 차이 0.18%, 전부 글자 경계의 안티에일리어싱. 두 정본은 같아 보인다는 것을 측정으로 확인
- Figma 그라디언트 3-stop이 기존 토큰과 일치함을 확인 —
#F8884F=--color-orange-400,#FF4141=--color-hr-red-400,#F6BC3D=--color-yellow-400. 벡터를 CSS 각도로 환산하면 290° 로, 기존--gradient-logo토큰과 같습니다(그동안 사용처 0인 채 방치돼 있던 토큰이 사실 BI 그라디언트였음) - Figma의 SVG export를 그대로 쓰면 국문이 깨진다는 점을 확인 — export에는
font-family="Pretendard"로 적히지만 저장소에 등록된 패밀리는Pretendard Variable이라 폴백됩니다. 문서에 교정 방법을 명시
남은 일 (이번 변경에 포함되지 않음)
packages/static/brand/자산 8개 중 7개 미사용, 유일 사용처인 랜딩hereby-wordmark.svg는 구버전 락업(신규 BI와 비율 상이) → 자산 정본화 필요icons.ts의logo-hereby는 stop이 토큰·hex 혼용 상태 → hex로 통일 필요(그라디언트 예외 정책에 맞춤)- admin 등 대부분 앱에 파비콘·og:image 부재
- 다크 배경용 반전 정본 부재 (현재 제공 자산은 라이트 모드 한 벌)
2026-07-13 — 버튼 모서리(radius) 축 신설 (ds-r-* · --ds-btn-radius) (design/btn-radius)
신규
--ds-btn-radius축 +.ds-r-4/-8/-12/-16/-999모디파이어(design-system.css) — 버튼 모서리를 변형·색·크기와 독립된 축으로 분리. 기본은 종전대로 4px, 클래스를 얹었을 때만 8/12/16/999(알약)로 바뀐다. 굵기 축(--ds-btn-weight·.ds-fw-*)과 동일한 구조: 버튼의border-radius선언이 변수를 읽게 만들어 새!important없이 override가 성립한다(기존 4px 강제 3규칙 —button:not([class*="bg-"])…·button[class*="border"]·button[class*="bg-"]— 의 값을var(--ds-btn-radius, 4px)로 전환). 변수를 읽지 않는 버튼 외 요소는 무영향. 원형 아이콘 버튼(--icon/--circle)은 999px 고정이라 축에서 제외--radius-12: 12px토큰 — 8↔16 중간 단계. 2배수 progression의 예외로 정식 채택(이전의 "신설 취소" 결정을 뒤집음).rounded-12유틸도 함께 생성된다. 덕분에 SubTab이 12px 요청을 토큰 부재로 8px로 내려 채택했던 제약도 해소됨(시각 변경 여부는 별도 결정)- radius 단일 출처화 —
design-system.css가@import하는tokens.css가2·4·6·8·10·12·16이라는 별도 스케일을 갖고 있어@theme스케일과 어긋나 있었다. 사용처 0건이던--radius-6/10/12를 제거해 radius 정의를@theme한 곳으로 모았다(6px·10px는 의미 토큰--ds-radius-sm/--ds-radius-lg로 계속 참조)
문서
- Buttons — 「모서리(radius)」 섹션 신설(클래스 표·컨테이너 상속·아이콘 버튼 제외), 클래스 API 표에 모서리 축 행 추가. 버튼 탐색기(DemoButtonExplorer.vue)에 모서리 컨트롤(기본 4 · 8 · 12 · 16 · 999) 추가 — 색 × 크기 전 조합에 실시간 적용
- 모서리 — primitive 표에
--radius-12행 추가 + 「radius의 단일 출처는 이 스케일 하나」 명시. 트랩 ①의 「커스텀 radius는<div role="button">으로 우회」 안내를 정식 축(ds-r-*) 안내로 교체(규칙 블록 + 본문), 강제 제외 명단을 실제 셀렉터와 일치하도록 보정(.ds-menu__item·.icon-btn·.ds-btn--icon·.ds-btn--circle·.org-tree__action-btn누락분 추가), 로드맵·변경 이력 갱신. 버튼 기본 모서리(4px) 유지 여부와 SubTab 12px 복원 여부를 검토 항목으로 등록 - Sub-tab — 「12px 토큰 부재로 8px 채택」 메모에 토큰 신설로 선행 조건이 해소됐음을 반영(현재 8px 유지)
결정
- 버튼 기본 모서리 =
4px유지 확정 — 탐색기에서 8/12/16/999를 비교한 뒤 내린 결정. 기본값은 그대로 두고, 다른 모서리가 필요한 버튼에만ds-r-*축을 얹는다. 기본값 변경 제안은 다시 열지 않는다 - SubTab radius =
8px현황 유지 확정 —--radius-12신설로 "토큰 부재" 제약은 사라졌으나, 12px 상향은 시각 변경이라 현행 유지. 항목을 닫는다(재개하려면 디자인 재검토 선행)
2026-07-07 — 표·스케줄러·툴팁·필터 DS 정렬 문서화 + 메모 마커 코너 이동 (design/adjust-table)
수정
- 근무 스케줄표 셀 메모 마커(
.memo-corner)를 우상단 → 좌상단으로 이동(admin main.css). 희망근무 보기(prefShow)·출근현황 보기(showAttendance)는 독립 토글이라 함께 켤 수 있는데, 그러면 메모(우상단 삼각형)와 출결 배지(.att-badge, 우상단 원형)가 같은 코너에서 겹쳤음 → 메모를 좌상단으로 옮겨 세 표식이 각기 다른 코너(좌상=메모 / 우상=출결 / 우하=희망근무.pref-corner)를 차지. 삼각형 방향(빗변 우상→좌하)·주석도 갱신. admin 빌드 green - 툴팁
topplacement도 상단 잘림 시 아래로 flip 추가(tooltip.ts · design-system.css.ds-tooltip[data-placement="top"][data-flip="down"]). 짧은 임베드 iframe(admin 안 team-manager/hr-system) 상단의 아이콘 버튼에서 툴팁이 프레임 위로 잘리던 문제 해결. 싱글톤 재사용 요소라show()가 매번data-flip을 선제거 → 직전 flip 잔재(뒤집힌 화살표·오프셋) 누수도 함께 수정
문서
- Tooltip —
top도 flip 반영(placement 표·동작 원리 flip 초기화·collision flip 항목·로드맵·변경이력) - Data table — 「숫자·식별자 표기」(정렬 열
tabular-nums· 식별자font-mono금지, 근거는 타이포그래피) + 「빈 상태(<tbody>안colspan행)」 스펙 신설. 자격유형 표(admin 설정)·공지 표(총무)·문서관리(team-manager)를 공유 스켈레톤으로 마이그레이션함을 사용처에 반영 - 근무 스케줄표 — 「관리자 네이티브 스케줄러 확장」 섹션 신설: 셀 코너 표식 3종(좌상=메모/우상=출결/우하=희망근무 코너 분리) · 희망근무 팔레트(
.pref-corner--*·.pref-tint-*·.pref-shift--*를 근무표 D/E/N 공유 토큰yellow-500·hr-emerald-600·blue-500으로 정렬) · 환자수 행(고정폭 라벨 슬롯 열 정렬 + 접기/펼치기) · sticky 범례 밴드. admin이.sched-grid복제본을 갖는다는 점 단일 출처에 명시. 라이브 데모(DemoScheduleTable.vue + ds-sched-demo.css)에 셀 좌상단 메모 세모(출결 배지와 코너 분리 시연)와 「근무 변경」 피커(기존 인라인 순환을 실제 admin 동작과 일치하는 셀 위치 팝오버로 교체 — 더블클릭·드래그로 2칸+ 선택 완료 두 경로 모두 지원, 다중 선택이면 선택 전체 적용) 반영 - Form controls — 「날짜 범위 필터(프리셋 버튼)」 패턴 신설:
dsDatePicker2개(시작·종료,x-effect교차 clamp) + 프리셋 줄(이번주/지난주/이번달/지난달/최근 3개월 | 전체) +--icon --muted초기화. 공유 헬퍼resolveDatePresetRange/datePresetBtnClass재사용 - Buttons — 사용 규칙에 「리스트 행 상세·보기 =
line · normal · sm」 추가 + 즉석 색 pill 금지·--line은 톤 필수 tip - 모달 — 「상세(읽기전용) 모달」 섹션 신설(공지 상세: 넓은 셸·이전/다음
--icon·닫기--white-bk·메타ds-label·x-text전용). §1에 임베드 앱 백드롭 오프셋(ga-embedded/tm-embeddedpadding-bottom: 10vh), §4에 모달 안 탭 바tabnav-flush-left규칙 추가
메모
- 이 브랜치의 실제 토큰화(10/11/12px →
text-c2/c1/b6,tabular-nums,font-mono제거)는 이미 타이포그래피·Data table에 규정된 규칙을 적용한 것이라 신규 토큰·컴포넌트는 없음 — 문서는 사용처·예시·근거 링크만 보강. 가짜 데이터(전자서명·공지)·푸시 설정 이관 등은 DS 문서 대상 아님(코드 리뷰 이슈로 별도 처리)
2026-07-04 — 전역(시스템) 팝업 z-index 티어 신설 (--ds-z-modal-system · .ds-modal-backdrop--system) (design/adjust-modal-employee-management)
신규
--ds-z-modal-system: 75+.ds-modal-backdrop--system— 권한 요청(permission-denied-modal)처럼 SDK/전역 이벤트로 어떤 화면·모달 스택 위에서든 뜨는 전역 팝업 전용 티어(design-system.css). alert(70)와 toast(80) 사이. 쌓임 원칙: 팝업은 최신 표시 순서대로 위에 쌓인다 — 전역 팝업은 항상 마지막에 뜨므로 최상단 모달 티어를 가진다. 기본 티어(z-50)로 두면 첨부파일 미리보기 뷰어(--top, z-70) 위에서 발생한 403 권한 팝업이 뷰어 아래에 깔려 보이지 않던 문제(직원 수정 팝업 자격증/면허 탭에서 발견) 해결. permission-denied-modal에 첫 적용
문서
- 모달 §단일 출처·규칙 — z 토큰 표에
alert·system행 추가, 쌓임 원칙 명시,--top낡은 z-60 표기를 실제 값(z-70)으로 교정
2026-07-03 — 상태 배지(.ds-status-badge) 색을 등록 라벨 팔레트로 통일 (design/adjust-modal-employee-management)
신규
.ds-status-badge--success— 누락돼 있던 성공(활성) 톤 모디파이어 추가(design-system.css).--success정의가 없어활성·재직·복직배지가 base 스타일(투명 배경)로 떨어져 회색 평문처럼 보이던 문제 해결(직원 수정 팝업 자격증/면허 탭에서 노출)
변경
- 상태 배지 4종(
--success·--warning·--danger·--neutral) 색을 등록 라벨(.ds-label) secondary 팔레트로 통일(design-system.css). 기존 진한--color-*-200(bg)/-300(border)/-800(text) 톤은 등록 라벨(연한 bg + 밝은 글자, solid는 테두리 없음)과 드리프트가 있었음 → 라벨 solid와 동일 색으로:success=--ds-secondary-green-bg/--ds-secondary-green,warning=--ds-secondary-yellow-bg/--color-yellow-500,danger=--ds-secondary-red-bg/--ds-color-danger,neutral=.ds-label--gray값 미러링. base의1px solid transparent테두리를 유지(모디파이어가 border-color 미지정)해 라벨 solid처럼 테두리 없이 렌더. 앱 전역 상태 배지 11곳(활성·휴직·퇴직·비활성 등)에 일괄 반영
문서
- Label §의미색 매핑 규칙 — 상태 배지(
.ds-status-badge)가 등록 라벨의 secondary 팔레트와 동일 색을 공유함을 명시
2026-07-03 — 패널 모달 최소 높이 720px 변형 (.ds-modal--panel-tall) (design/adjust-modal-employee-management)
신규
.ds-modal--panel-tall— 패널 모달(.ds-modal--panel)에 얹는 최소 높이 변형(design-system.css). 기본min-height: 300px를 **720px**로만 재정의 — 입력 필드가 많아 세로 공간이 넉넉해야 하는 폼형 패널에 사용. 낮은 뷰포트에서height(뷰포트−여백)가 720px 밑으로 줄어도 최소 720px 확보(그 아래로는 본문 스크롤). 높이 축만 재정의하므로-narrow(폭)·-loose(상하 여백)와 그대로 조합. 직원 추가·직원 수정 팝업에 첫 적용(둘 다-loose·-narrow와 병용) — 유형·기준 통합 관리는 기존 min-height 300 유지
문서
- 모달 → 패널 모달 §사이즈 —
-narrow설명 아래에-tall변형 설명 추가
2026-07-02 — 패널 모달 좌우 여백 2배 변형 (.ds-modal--panel-narrow) (design/adjust-modal-employee-management)
신규
.ds-modal--panel-narrow— 패널 모달(.ds-modal--panel)에 얹는 좌우 여백 2배(100 → 200px) 변형(design-system.css).width: min(calc(100vw - 400px), 1280px)만 재정의하고 상하·최소 규격(min-width 768 · min-height 300)은 기본 패널과 동일 — 폭이 넓을 필요가 없는 폼형 패널에 사용. 뷰포트 1680px 이상에서는 최대 폭 1280 캡이 먼저 걸려 기본 패널과 동일.-loose(상하축)와 축이 달라 함께 얹을 수 있음. 직원 추가(loose와 병용)·직원 수정 팝업에 첫 적용 — 유형·기준 통합 관리는 기존 좌우 100px 유지
문서
- 모달 → 패널 모달 §사이즈 —
-loose설명 아래에-narrow변형 설명 추가
2026-07-02 — 패널 모달 상하 여백 3배 변형 (.ds-modal--panel-loose) (design/adjust-modal-employee-management)
신규
.ds-modal--panel-loose— 패널 모달(.ds-modal--panel)에 얹는 상하 여백 3배(60 → 180px) 변형(design-system.css).height: calc(100vh - 360px)만 재정의하고 폭·최소 규격(min-width 768 · min-height 300)은 기본 패널과 동일 — 뷰포트 대비 콘텐츠가 가벼운 폼형 패널에 사용. 직원 추가 팝업에 첫 적용
문서
- 모달 → 패널 모달 §사이즈 — 사이즈 표 아래에
-loose변형 설명 한 줄 추가
2026-07-02 — LNB 배지를 DS .ds-label로 렌더 + 다크 서피스 tone 적용 (design/lnb-disabled-menu)
변경
- LNB 배지가 전용
.ds-lnb__badge일회성 스타일 대신 DS.ds-label로 렌더(LnbSidebar.ts).LnbItem.badge가string \| number(알림 카운트 필, primary·round) 또는LnbBadge객체(tone·line·shape·size로 DS 라벨 변형 지정)를 받는다..ds-lnb__badge는 접힘 레일 숨김 훅으로만 남김 - 다크 레일용 tone 적용 — LNB 레일이 어두워(neutral-900) 준비중 라벨에 밝은 배경용
light-gray(어두운 글자)를 쓰면 안 보였음 → 다크 서피스 tonelight-gray-dark(solid·square·xs)로 교체.LnbBadgeTone에gray-dark·light-gray-dark추가. admin 총무 준비 메뉴(대시보드·총무·관리)에 "준비중" 배지 + disabled 적용
문서
- LnbSidebar — 배지 설명을 DS 라벨 기반으로 갱신,
LnbBadge타입·다크 레일 tone 주의 추가, 라이브 미리보기(<DemoLnbSidebar>)의 준비중 배지를light-gray-dark로 렌더
2026-07-02 — 다크 배경용 라벨 색 변형 신설 (--gray-dark · --light-gray-dark) (design/ds-label-dark-tone)
신규
- 다크 bg 위에서 쓰는 라벨 색 변형
--gray-dark·--light-gray-dark추가(design-system.css). 기존--gray/--light-gray는 밝은 배경용(어두운 텍스트)이라 어두운 배경 위에 얹을 중립 라벨이 없었음. 두 변형은 그 반대로 밝은 텍스트 + 흰색 반투명 채움(glass) —--gray-dark는 글자--ds-label-white+ 배경--ds-glass-18(라인 테두리--ds-glass-30),--light-gray-dark는 글자--ds-glass-70(라이트 그레이) + 배경--ds-glass-08(라인 테두리--ds-glass-18). solid·--line모두 지원
문서
- Label §색 변형 표에
--gray-dark·--light-gray-dark행 추가(색 10종 → 12종). 라이브 미리보기(<DemoLabelExplorer>)에 두 컬럼 추가 — light 라벨이라 진짜 어두운 바탕(--color-neutral-800)에 얹어 표시하고, 설명란에 "다크 bg 위에서 쓰는 라벨" 주석 추가
2026-07-01 — 패널 모달(.ds-modal--panel) 문서 등록 + LNB 인-모달 변형 (design/adjust-modal-attendence)
문서
- 모달 → 패널 모달 섹션 신규 — 근태 「유형·기준 통합 관리」에 첫 적용된 좌측 메뉴 + 우측 현황 2단 큰 팝업 변형. 페이지 목차(우측 아웃라인)에 "패널 모달" 항목으로 노출. 인터랙티브 라이브 예시(
<DemoPanelModal>— 좌측 메뉴로 5개 섹션 전환·닫기/다시 열기), 사이즈 규격(width min(100vw-200px,1280) · min-width 768 · height 100vh-120), 구조(좌측 220px 메뉴·우측 헤더/툴바/현황 표), 중첩 자식 ESC 가드(typeManagerChildOpen·stopImmediatePropagation·500ms 유예) 정리. "어디서 쓰이나"에type-manager.html추가 - LnbSidebar → 패널 모달 내비게이션 섹션 신규 — 패널 모달 좌측 메뉴를 LNB 패턴의 인-모달 경량 변형으로 등록. 앱 LNB(
.ds-lnb·다크 레일) vs 패널 메뉴(밝은 배경·플랫·현황 전환) 비교표 + 모달 문서로 바로가기 링크. 아이콘 일치 규칙(출처 페이지 정체성 아이콘 재사용) 공유 명시. "사용처"에도 패널 메뉴 항목 추가 <DemoPanelModal>데모 컴포넌트 신규(theme/components) — 실제.ds-modal--panel/.ds-modal__close/.type-manager__nav-icon/.ds-label/.demo-admin표 CSS로 렌더, 백드롭을 stage 안에 가두는 데모 보정만 스코프 스타일로 추가(하드코딩 hex·새!important0)
2026-06-30 — 근태 현황 날짜 필터 커스텀 달력(dsDatePicker) 일괄 전환 + --32 사이즈 (design/adjust-modal-attendence)
신규
.ds-datepicker--32사이즈 모디파이어 — 트리거 32px. 필터 줄의ds-select--32/ds-input--32와 높이를 정렬하기 위한 중간 크기(기존: 기본 40px ·--sm28px). design-system.css.ds-datepicker--32
변경
- 근태 현황 페이지의 모든 날짜 필터를 네이티브
<input type="date">→ 커스텀 달력(dsDatePicker)으로 일괄 전환 — 근무(출퇴근 현황)·연차(연차/휴가 결재)·연장근무·외근(결재) 탭의 기간from~to입력 10개. 모두ds-datepicker--32로 필터 줄과 높이 정렬,onChange로 기존@change와 동일하게 즉시 재조회 - 연장근무 탭 '기간 드롭다운(초안→적용)'을 인라인 from~to 커스텀 피커(즉시 적용)로 통일 — 커스텀 달력 팝오버는
body로 teleport되어ms-dropdown(바깥 클릭 닫힘) 안에 중첩하면 달력 클릭 시 드롭다운이 닫히는 충돌이 있어, 다른 탭과 동일한 인라인 방식으로 정리. 자주 쓰는 기간(이번주/지난주/…) 프리셋 버튼은 유지
문서
- Form controls — 컴팩트 트리거 항목에
ds-datepicker--32(32px, 필터 줄 정렬) 추가(--sm28px와 병기)
2026-06-30 — SubTab(낱개 하위 탭) 신규 (design/adjust-modal-attendence)
신규
- SubTab 컴포넌트 신규 — 상위 TabNav(밑줄형) 아래에서 쓰는 낱개 하위 탭. 낱개 탭이 간격을 두고 나열되고 선택(active) 탭만 강조, 비선택은 공통 neutral 라인 테두리.
.ds-segment(회색 통 안에서 흰 pill 이동)와 구분되는 별도 형태. CSS.ds-sub-tab/.ds-sub-tab__item(design-system.css) + AlpinesubTab(SubTab.ts · sub-tab.html) - 축(axis) 4종 — size
md(36px·b5 13px) /ml(44px·b4 14px) · radius4/8/16(--radius-*, 기본 8) · colordark/primary· fillsolid/line. color×fill로 선택 강조 4종(dark·solid 기본 = 검정 채움+흰 글자). 모두subTab({ size, radius, color, fill })옵션 →subTabGroupClass모디파이어로 반영 - 토큰만 사용 — color/radius/typography/geometry 모두 DS 토큰. 하드코딩 hex·arbitrary
[...]·새!important0
문서
- SubTab 페이지 신규 — 인터랙티브 라이브 미리보기(
<DemoSubTab controls>— size·radius·color·fill 4축 클릭 토글), 축 4종 표·미리보기, API/토큰. Components — Navigation 사이드바에SubTab추가 - 상위 TabNav와의 계층 데모(
<DemoSubTabHierarchy>) — 상위(밑줄) → 하위를 한 컨테이너에 쌓고, 동일 배경색 공유 + 상·하위 탭 사이 간격 24px로 위계를 시각화 - 검토 필요: radius 중간값은 요청
12였으나 시스템에 12px radius 토큰이 없어 인접한--radius-8(8px) 사용 — 12px 필요 시--radius-12토큰 신규 추가 선행
2026-06-30 — 탭 크기 2단계 변형(md/ml) + 하단 구분선 on/off (design/adjust-modal-attendence)
신규
- TabNav에 크기축(size) 추가 — 기존 단일 사이즈를
md(기본, 현행: 40px · padding 10/12 · b5 13px) /ml(48px · padding 12/16 · b4 14px) 2단계로 변형. CSS.ds-tab-item--ml모디파이어 신설(design-system.css,.ds-tab-*). size축은 padding·높이·라벨 폰트만 바꾸고 색·언더라인 두께(2px)·hover는 공통 - 활성 하단바 오프셋 크기별 재계산 — 라벨이 세로 중앙이라 오프셋 = (탭높이 − 라벨높이)/2. md
-11px(기존) / ml-14px - 컴포넌트 옵션
size—tabNav({ size: 'md' | 'ml' })(기본md) +tabNavSizeClassgetter가 각.ds-tab-item에 모디파이어를 반영(TabNav.ts · tab-nav.html). 기존 호출부는 옵션 생략 시 그대로md— 비파괴
변경
- 탭 바 하단 구분선(회색 1px)을 DS 소유로 이전 + on/off — 기존엔 partial markup의 Tailwind
border-b border-neutral-200였던 것을nav.ds-tab-nav { border-bottom: 1px solid var(--color-neutral-200) }로 DS가 소유. 끄는 모디파이어.ds-tab-nav--flush(border-bottom: 0) 신설. 컴포넌트 옵션tabNav({ divider: boolean })(기본true) +tabNavDividerClassgetter. 기본값은 기존과 동일 1px — 비파괴 - 활성 탭 언더라인을 하단 구분선과 overlap 정렬 — 활성 2px 검정바(
.ds-tab-underline)가 회색 1px 구분선 바로 위에 붙어 "2줄"로 보이던 것을, 구분선을 덮도록 오프셋을 보더 1px만큼 더 내림(md-11px→-12px/ ml-14px→-15px). 활성 탭은 검정 한 줄, 비활성 탭은 회색 한 줄
문서
- TabNav "크기 (size)" 섹션 신설 — md/ml 표 + 라이브 미리보기(
<DemoTabNav size="ml">). API에size옵션·tabNavSizeClass추가, 사용된 토큰(Typography b4·Geometry per-size) 갱신 - TabNav — 하단 구분선 "하단 구분선 (divider)" 섹션 신설 — on/off 라이브 미리보기(
<DemoTabNav :divider="false">) + 활성 바(2px)와 구분선(1px)이 별개임을 명시. API에divider옵션·tabNavDividerClass추가 <DemoTabNav>인터랙티브 컨트롤 —controlsprop 추가로 미리보기 상단에서 체크박스로 ml 크기·divider를 토글(시각 미리보기 §0). 데모 탭 둘레 20px 여백으로 하단 구분선 가시성 개선
2026-06-26 — 모달 텍스트 색 위계 + 인풋 타이틀·간격 표준 (design/adjust-modal-addmember)
변경
- 모달 안 텍스트 색을
label-*시맨틱 토큰 3단 위계로 통일 — 모달 타이틀·그룹 타이틀 =label-normal(neutral-900), 모달 디스크립션·인풋 타이틀 =label-assistive(neutral-500), 기타 보조 설명 =label-muted(neutral-400). rawneutral-*대신 시맨틱 토큰 사용(테마 대응) - 인풋(필드) 타이틀 색
text-neutral-600→text-label-assistive전앱 통일 — 221곳(admin·team-manager·general-affairs·management-dashboard·inventory + 공유 컴포넌트/레퍼런스 위저드). neutral-600(204)·neutral-700(10)로 혼재하던 것을 한 토큰으로. (모바일 employee-app·급여 hr-system·시스템툴 제외) - 그룹(섹션) 타이틀 14px(b4) → 16px(b3) — 인풋 타이틀(13px)과 위계 분리
- 좌우로 놓인 인풋 사이 간격 = 12px(
gap-3) 명문화 — 직원추가 모달이 이미 쓰던 값,gap-2(8px)였던 ward-settings 등 정렬
문서
- 모달 패턴 §4 — 타이포·간격 표에 모달 디스크립션·그룹 타이틀·좌우 인풋 간격 행 추가 + "텍스트 색 위계" 표 신설(normal/assistive/muted). 예제 라벨을
text-label-assistive·보조설명text-label-muted로 갱신
2026-06-26 — 숫자 입력 변형 (ds-input--number) 신설
신규
<input type="number">용 공유 변형ds-input--number추가(design-system.css). 네이티브 증감 화살표(스피너)를 평소엔 숨기고 hover/focus 시에만 노출 — 평소엔 가운데 정렬 숫자가 화살표에 밀리지 않고 깔끔하게, 조작 시엔step단위 증감(▲▼)이 가능(키보드↑/↓는 항상 동작). 숫자 자리수 정렬용font-variant-numeric: tabular-nums포함- 조합형 변형 — 숫자 고유 처리(스피너 + tabular-nums)만 담당. 크기
ds-input--XX· 너비w-full· 가운데 정렬text-center유틸과 조합(정렬은 선택) - 흩어진 핵을 대체할 단일 기준 — 기존엔 admin
.spinner-on-hover,.patient-count-input·.sched-grid input[type=number](항상 숨김), team-manager 인라인style="-moz-appearance:textfield"로 제각각이었음. 그중 검증된 hover/focus 노출 동작(admin.spinner-on-hover)을 공유 DS로 승격
적용
- scheduler 근무지 설정(ward-settings) 의 간호사·조무사·간병인 비율 입력칸(admin · team-manager) — 기존
ds-input--40 w-full text-center(스피너 처리 없음 → 화살표가 가운데 숫자를 밀던 상태)에ds-input--number추가
문서
- Form controls "숫자 입력 (ds-input--number)" 섹션 신설 + 라이브 미리보기(
<DemoFormControls>)에 숫자 토글 추가
2026-06-26 — 시간 입력 피커 = teleport 드롭다운으로 전환 (dsTimePicker)
변경
- 시간 입력 피커를 dim 중앙 다이얼로그 → 인풋 아래 드롭다운으로 전환(날짜 피커와 동작 통일). 기존
te*인라인 로직(.ds-modal-backdrop--top+.ds-mini-modal중앙 다이얼로그)을 공유 컴포넌트dsTimePicker(Alpine 팩토리 DsTimePicker.ts + 마크업 partial ds-time-picker.html +.ds-timepicker*CSS)로 추출. 사용처는 호스트 div + include 한 줄,x-modelable="value" x-model="..."로HH:MM(24h) 바인딩 - 동작:
x-teleport="body"+position:fixed로 트리거 아래 드롭다운 — 모달overflow·filter탈출해 안 잘림, dim 없음, z = 모달 위 1레이어. Esc·바깥클릭·취소 = 피커만 닫힘(모달 유지). 12h 표시 / 24h 저장 ·17→오후5 정규화 · 확인은 유효+변경 시만 활성 (te* 동작 보존) - 적용: admin shift-form(근무 설정 시작/종료시간) · general-affairs notice-form(공지 만료일시 = 네이티브 날짜 + 커스텀 시간 드롭다운). 닫기 이벤트
ds-picker-close로 날짜·시간 피커 통일(동시 1개만 열림) - Form controls "구성 (커스텀 —
dsTimePicker)"로 갱신 +## 피커인트로 "시간 = 커스텀 다이얼로그" → "커스텀 드롭다운"
메모
- docs 라이브 데모(
<DemoTimeInput>/<DemoDateTime>)는 아직 중앙 다이얼로그 형태(미리보기 전용) — 후속으로 드롭다운 형태에 맞춰 갱신 예정
2026-06-26 — 커스텀 날짜 달력 컴포넌트(dsDatePicker) 신설 + 앱 적용 (design/adjust-modal-addmember)
신규
- Form controls "날짜 선택기 구성 (커스텀 —
dsDatePicker)" 신설 — 네이티브type=date를 대체하는 공유 커스텀 달력을 실제 컴포넌트로 등록. Alpine 컴포넌트 DsDatePicker.ts(@hereby/components,createAuthComponentsPlugin등록 → 전 앱 사용 가능) + 공유 마크업 partial ds-date-picker.html +.ds-datepicker*CSS(design-system.css). 사용처는 호스트 div + include 한 줄(로직·외형·마크업 모두 공유, 중복 없음) - 팝오버 =
x-teleport="body"+position:fixed드롭다운 — 트리거 바로 아래에 뜨되 모달의overflow-y-auto·filter를 탈출해 절대 안 잘림(모달 밖으로 넘어가도 OK). dim 없음, z = 모달 위 1레이어(--ds-z-modal-top). 닫기 = Esc · 바깥 클릭 · 날짜 선택 → 피커만 닫힘(모달 유지). 좌우 clamp·위로 flip·동시 1개만 열림 보정 포함 - 옵션·확장:
dsDatePicker({ min, max, placeholder, onChange })(범위 밖 날짜 비활성) · 비활성은 호스트에x-effect="disabled = (조건)"· 선택 시 파생 로직은onChange콜백(유저 pick/clear/today에만 발화 — 네이티브@change동등, 프로그램적 로드엔 안 뜸). 핵심 로직은 단위 테스트로 커버(DsDatePicker.test.ts·DsTimePicker.test.ts)
적용 (12필드 — 데스크톱 폼 전수)
- general-affairs 이수기록(이수일·만료일)·실행기록(예정일) · admin 자격증(발급일·최근신고일·차기신고기간 —
max·비활성·신고일→차기기한 자동계산 보존)·스케줄러 희망일(item.date) · hr-system 연장근무·치료세션(근무일) · inventory 품목(유효기간) · team-manager 치료 인센티브(날짜)·희망일(item.date) - 컴팩트 변형
ds-datepicker--sm(트리거 28px) 신설 — 스케줄러/팀 희망일 목록의 좁은 인라인 행(x-for, text-xs 형제와 같은 높이)에 적용 - 보류: 데스크톱 범위필터(From/To, 카테고리 B) · 모바일 employee-app(Capacitor 네이티브 휠 의도 —
x-clamp-year)
정정
- 기존 "단일 날짜는 그대로 네이티브
type=date(커스텀 달력 안 만듦)" 서술을 정정 — 커스텀 달력 그리드는 단독·결합 모두 사용(단독 = teleport 드롭다운 / 결합 = 한 팝업 인라인).## 피커인트로의 "날짜 = 네이티브"도 "날짜 = 커스텀 달력(dsDatePicker)"으로 수정
2026-06-25 — 시간 입력 피커 — Form controls 섹션 + Modal 내부 피커 등록 (design/adjust-modal-addmember)
신규
- Form controls 문서를 Input · Select · Picker 3패밀리로 정리 — 제목에 Picker 추가,
## 피커 (Picker)상위 섹션 신설하고 달력(날짜·월)·시간 입력을 그 아래로 그룹화(#calendar/#time-input앵커 유지). 피커 = 모달·페이지 공용 패밀리(모달 전용 아님) - Form controls "시간 입력 (Time input)" 섹션 + 라이브 데모(
<DemoTimeInput>) 신설 — 안내형 12시간 입력 다이얼로그를 DS 패턴으로 등록. 오전/오후 드롭다운(ds-select--40) + 시/분 키보드 입력(ds-input--40) + 위·아래 스텝 아이콘 버튼(간격 6px) + 취소/확인(ds-btn--solid white-bk/dark--md),.ds-mini-modalon.ds-modal-backdrop--top(z60). 표시 = 12시간제 · 저장 = 24시간제HH:MM - 값 규칙 명문화: 시 칸은 24시간제 입력 허용(
17→ 오후 5시 자동 정규화), 자정 = 오전 12 · 정오 = 오후 12. 확인 버튼은시 0–23·분 0–59 순수 숫자AND원래 값에서 변경일 때만 활성(숫자 아님/무변경이면 비활성) - 날짜 = 커스텀 달력 우선 (정책 전환) + 단독 커스텀 날짜 피커 데모(
<DemoCustomDate>) 신설 — 결합 피커와 동일한 커스텀 달력(연도 드롭다운 · 월 이동 ‹ › · 삭제 · 오늘 · 오늘=채움/다른 날=테두리 · 바깥클릭 닫기)을 단독 날짜 선택에도 추가. 앞으로 날짜는 커스텀으로 통일·우선하고 네이티브type=date는 대안으로 유지(점진 전환). 기존 "날짜=시스템 기본, 커스텀 안 만듦" 정책을 뒤집음 - 날짜 + 시간 결합 정책 + 라이브 데모(
<DemoDateTime>) 신설({#datetime}) — 네이티브datetime-local(OS마다 외형/동작 상이, 공지 만료일시가 이 케이스)을 한 팝업에 [커스텀 달력 그리드(좌) + 커스텀 시간 폼(우)] 결합 뷰로 대체. ⚠️ 달력 커스텀은 결합 케이스 한정(단일 날짜는 그대로 네이티브type=date— 네이티브 달력은 우리 시간 폼과 한 팝업에 못 합치기 때문). 마이그레이션 대상: 공지 작성 만료일시 등datetime-local은 이 결합 뷰로 교체 권장 - Modal "§7 모달 안의 피커" — 짧은 참조만 추가. 피커(날짜·월·시간)는 공용 폼 컨트롤이라 home은 Form controls(모달 전용 아님). 모달 문서엔 모달 안에서 쓸 때만 더해지는 규칙(다이얼로그형 =
.ds-modal-backdrop--top모달 위 · 팝오버형 = 바디overflow-y-auto비잘림 · Esc/바깥클릭은 피커만 닫고 모달 유지)만 명시
메모
- 달력(날짜 = 네이티브
type=date· 월 = 커스텀 월 선택기)은 기존 "달력(날짜·월 선택)" 섹션 유지 — 이번엔 시간 입력만 추가(사용자 결정: 달력은 기존 기본 사용 + 커스텀 뷰 조합) - 원본 구현: shift-form.html 근무 설정 시작/종료시간 (
teOpenFor/te24/teNormHour/teStep*/teCanConfirm)
2026-06-25 — 모달·얼럿 푸터 버튼 강조 규칙 (위치 → 맥락·중요도, weight 3티어) (design/adjust-modal-addmember)
신규
- Modal "§6 푸터 버튼 — 강조는 위치가 아니라 맥락·중요도로" 섹션 신설. 그동안 문서가 정의하지 않던(라이브 데모로만 암묵 표준이던) 푸터 확정 버튼 강조를 2축으로 명문화: ① 위계(가장 무거운 버튼 = 권장 액션 — "우측이라서"가 아니라 "권장 액션이라서") × ② weight 3티어(중요도로 무게 결정). 통합 버튼 체계의
ds-btn조합 그대로 — 새 클래스 없음 - weight 3티어: 기본/권장 컨펌 =
solid · dark(대부분) · 크리티컬 컨펌(삭제·비가역) =solid · lightor(연주황 틴트, 드물게) · 취소·닫기 등 물러나는 동작 =solid · white-bk(흰 모달 위 텍스트만). 위계dark>lightor>white-bk. "가장 우측이니 orange/dark 채움" 관성 폐기 - 위험 반전: 데이터 손실 확인(변경 폐기·삭제 직전)은 안전한 선택을 권장 버튼(
dark)으로 올리고 파괴 동작은 약화(white-bk)/lightor, 자동 포커스·기본 Enter는 취소에. 라이브 푸터 강조 갤러리(일반/크리티컬/위험반전 3종, 실제ds-btn렌더) 추가
변경
- Modal 라이브 데모를 새 규칙에 동기화 — 저장(권장) = dark, 취소 = white-bk, 저장확인 팝업은 계속편집(안전) = dark · 닫기 = white-bk로 위험 반전 적용, "모달 다시 열기" 유틸도 dark(데모에서 orange 제거). "목표 & 로드맵"에 푸터 버튼 강조 정렬 행 추가
- Buttons "사용 규칙(맥락→조합)" 표 정정 — 모달·얼럿 푸터 행을 권장 =
solid · dark· 크리티컬 =solid · lightor· 취소·닫기 =solid · white-bk3티어로 교체(기존solid · primary/line · dark), 모달 푸터 강조 규칙으로 연결하는 안내 추가. 의미색green/red는 모달 밖 인라인/툴바/워크플로 컨텍스트로 한정 명시
수정
ds-btnlight 변형 hover/pressed 오버레이 색 매칭 (design-system.css) —lightred/lightgreen/lightblue/lightpurple/lightyellow/lightaquablue가 hover overlay 그룹에서 빠져 default--color-label-muted(회색)를 써, 거의 흰-50채움 위에서 hover 시 회색으로 바래던 문제 수정. 각자 의미색(-600/yellow-700)으로--ds-btn-ov-hf/--ds-btn-ov-pr매칭 —lightor가--color-primary(orange)로 매칭하던 것과 동일 패턴. 문서 결재(doc-preview) 반려(lightred) hover가 빨강으로 진해짐. Buttons "상태" 설명 동기화
메모
- 모달 내 워크플로 승인/반려는 예외로 확정 — 문서 결재(
doc-preview) 승인=green·반려=red(연한 시작lightred)는 의미가 강해 의미색 유지하되 DS 통합 버튼(ds-btn--solid)으로 구현(기존bg-green-600/bg-red-*유틸 교체), 같은 푸터 닫기·취소는white-bk. §6 "현황"에 예외 명문화 - 앱 마이그레이션은 버튼 체계 마이그레이션과 함께 점진 진행. 급여(hr-system)는 표준 적용 제외
2026-06-24 — Notice danger 변형 + 문서 신설 (design/adjust-modal-addmember)
신규
- Notice 컴포넌트 문서 신설 —
.ds-notice베이스 + 변형(--info/--warning/--success/--danger)의 라이브 미리보기·규격·토큰·적용 현황. 등록은 돼 있었으나 전용 문서가 0이던 인라인 알림 callout(피드백 그룹에 추가) .ds-notice--danger변형 신설(오류·반려: red-50/200/700, 아이콘 red-500) — 기존 info/warning/success와 동일 규격
변경
- 문서 결재 미리보기(team-manager
doc-preview) 반려 사유 + PermissionDeniedModal 요청 실패 에러의 임시bg-red-50 border-red-200 text-red-*조합을.ds-notice--danger표준 변형으로 정규화 — 반려 사유엔 경고 아이콘(.ds-notice__icon) 추가, notice 아래 footer 구분선 제거(빨간 박스가 시각 구분 제공) - 문서관리 목록(team-manager
document-management)의 구분·상태 배지를 앱 로컬.badge/임시bg-purple-100·bg-orange-100에서 DSds-label(--blue/--primary/--yellow/--green/--red/--gray)로 통일 — 같은 데이터를 띄우는 미리보기 팝업과 배지 컴포넌트·색 매핑 일치(취소=gray 분리 포함) - Modal 라이브 예시 닫기(
.modal-demo-x)에 hover 상태 추가 — doc 전용 클래스라 hover 규칙이 빠져 마우스를 올려도 변화가 없던 문제. 실제 앱 모달(admin/ga/md)의 표준 닫기 버튼.ds-modal__close와 동일하게 재현: 24×24 + 16px SVG ✕, 투명 배경 → hover 시 neutral-100이 ✕ 뒤에 차오름(공유button{border-radius:4px!important}강제 규칙으로 라운드 사각형 렌더, 앱과 동일)·focus-visible윤곽선. 입력·드롭다운은 전역input/.ds-select규칙으로 이미 hover·focus 반영됨
2026-06-23 — 모달 데모 타이틀·마진 표준 동기화 (design/adjust-modal-addmember)
변경
- Modal 라이브 예시 모달 타이틀(
.modal-demo-title)을 16px/700 → 20px/600으로 수정 — 실제ds-modal__title과 어긋나 있던 데모 값을 표준에 동기화(line-height 1.4·letter-spacing -0.2px 포함) - Modal 라이브 예시 카드 패딩 24px → 44·40·36(상/좌우/하), X 닫기 위치 14px → 20px —
ds-modal(header·body·footer) 표준 마진과 일치 - Modal "내부 타이포·간격" 표에 카드 마진(44/40/36)·모달 타이틀(20/600) 행 추가 — 데모 CSS에만 있던 값을 명시 문서화
- 모달 텍스트 가드(
.ds-modal-backdrop p.text-*)에text-h5·text-h4추가 —<p class="text-h5">모달 타이틀(20px)이 design-system.css를 Tailwind에@import하지 않는 앱(team-manager·employee-app 등, 런타임 로드만)에서도 동일 적용. 그 앱들은text-h5유틸이 생성되지 않아 헤딩/타이틀 크기가 누락되던 문제 수정
2026-06-19 — 입력 컴포넌트 문서 보강 (design/apply-ds-input-components)
신규
- Form controls "메뉴 패널(
ds-menu)" 섹션 신설 — 공유 DS의 커스텀 드롭다운 패널(__item/__item--selected/__item--row/__divider/__label/__hint) 라이브 인라인 미리보기 + 클래스/토큰 표. 등록은 돼 있었으나 문서가 0이던 컴포넌트(실제 직원 수정 모달 팀/역할 선택에 사용) - Form controls "달력(날짜·월 선택)" 섹션 신설 + 라이브 데모(
<DemoDatePicker>) — DS 정책 명시: 날짜=시스템 기본type="date"(네이티브 지원 충분, 커스텀 달력 미제작), 월=커스텀 월 선택기(type="month"지원 약함 →.ds-menu팝오버 + 연도.ds-select+ 월 그리드). 팝오버는 트리거 버튼 바로 아래 6px 간격·우측 정렬. 시각 확인도 네이티브 인풋을 직접 보게 구성
변경
- Form controls "사용처" → **"적용 범위 & 도입 현황"**으로 재구성: 전역 기본(자동·전 서비스) vs 명시적 사이즈 전환(도입 진행)으로 2층 분리, 모달 마이그레이션(직원 위저드·근태 모달 →
--40) 진행상황을 표로 반영. 곧 낡는 "사용처 카탈로그" 대신 진행상황 기록으로 전환 - Form controls
ms-dropdown정정 — 공유design-system.css가 아닌 admin·team-managersrc/styles/main.css의 앱 로컬 패턴임을 명시(공유 표준은ds-menu) - Form controls 라이브 미리보기(
<DemoFormControls>)에 textarea·드롭다운 채움 변형(--fill-gray/--fill-white) 추가 — 등록된 변수를 더 시각화(같은 상태가 input·select·textarea에 동시 반영) - Form controls 검토 필요 사항 정리 —
--ds-textfield-*/--ds-dropdown-*primitive 연결 항목을 runtime-safe literal 트레이드오프로 명시(보류:--ds-*는 의도적 literal 세트라@themetree-shaking으로 일부 앱서 primitive가:root서 제거됨 — 실측--color-label-white@module-manager·inventory). chevron(값 on-palette#8f8f8f=neutral-400·data-uri 한계)·24px(일관 literal px)·!important-free(정상) 항목은 검토 완료로 제거 - 라이브 미리보기 = 페이지 상단(데모 우선) 통일 — 데모가 있는 7개 페이지(Form controls · Data table · Tooltip · TabNav · WorkspaceSwitcher · PageHeader · LnbSidebar)에서 미리보기 블록을 H1·인트로 바로 아래로 이동(컴포넌트를 먼저 보고 → 세부 읽기). 본문/캡션은 그대로 유지
- Form controls 상단 라이브 미리보기를 3종 컴포넌트 갤러리(텍스트필드·드롭다운·textarea / 메뉴 패널 / 달력)로 재구성 — 각 항목에 "설명 더보기 →" 앵커로 상세 섹션(
{#size-scale}·{#menu-panel}·{#calendar}) 연결, 상세 섹션의 중복 미리보기는 제거(미리보기는 상단 한 곳에 모음)
메모
- 입력 컴포넌트(크기 스케일·토큰) 자체는 이전 작업(PR #247 등, 커밋
ab7675c8/d99c3fce)에서 이미 DS 등록·develop 머지 완료 — 이 작업은 문서 반영 전용(새 CSS 없음)
2026-06-18 — 토대 문서 누락분 보강 (foundations)
변경
- 색상 Label 표에 누락돼 있던
--color-label-black-normal(=--color-neutral-900별칭) ·--color-label-black-strong(#000000) 2개 토큰 추가 —design-system.css정의와 표 동기화 - 모서리 placeholder → 실문서: primitive
--radius-2~512/full(@theme→ Tailwindrounded-*) + 컴포넌트--ds-radius-sm/md/lg/label-sm/label-md토큰표·미리보기, 전역 버튼 radius 4px!important강제 트랩 명시 - 그림자 placeholder → 실문서:
--ds-shadow-xs~xlrecipe + 칩(--ds-shadow-chip-*)·drop-shadow(--ds-drop-shadow-*) 토큰표·미리보기, box-shadow recipe의 Figma 비전파 사유 명시 - 타이포그래피 "숫자 표기(Numeric figures)" 섹션 신설 — 데이터 숫자는 Inter 기본, 자릿수 정렬이 필요한 표/열에만
tabular-nums적용(단독 인라인 숫자엔 미적용), 사번·코드 등 식별자에font-mono금지(코드/키캡 전용). admin 근태관리 모달 사번font-mono→tabular-nums정리에서 도출 - 타이포그래피 토큰 중복 정리 + 문서 정정: ①
--ds-font-family를var(--font-app)별칭으로 변경(literal 단일화·동기화) ②--typo-*↔--text-*는 Tailwind tree-shaking 때문에 병합 불가임을 빌드 산출물로 검증(미사용--text-h1이:root에서 제거됨) → "단일화 대상" 표현을 "메커니즘 분리 유지"로 정정 ③ "font-weight 토큰 미정의"는 사실과 달라 정정(이미 정의·사용 중)
메모
- 세 페이지 모두 기재값을
design-system.css실제 토큰과 1:1 대조 후 작성 (수기 표 ↔ CSS 일치 검증) - 타이포 정정은 admin dist CSS(빌드 산출물) 직접 확인으로 검증 —
--text-b5/--text-c2존재,--text-h1부재
2026-06-12 — LNB 개편 + 헤더 프로필 (design/update-lnb-structure-and-merge-pages)
신규
- PageHeader: Toolbar 우측 끝 프로필 아바타(
showProfileopt-in, radius 8, 벨과 간격 24) + 프로필 팝오버(썸네일 64 원형 → 메일 → 이름 → 내 정보·로그아웃,onLogout호스트 콜백). LNB 하단 프로필 대체 - LnbSidebar 접힘 레일: 플라이아웃(
.ds-lnb__flyout— 흰 바탕·라인·아이콘/타이틀 행, fixed) · 커스텀 툴팁(.ds-lnb__tip— 네이티브 title 대체, 클릭 즉시 숨김, 열린 메뉴만 억제) ·is-child-active(활성 하위를 가진 부모 강조) .ws-brand: 비클릭 정적 브랜드 변형 — workspace(admin)는 WorkspaceSwitcher 드롭다운 대신 사용- 아이콘 13종:
ic-id-badgeic-file-textic-tableic-clockic-slidersic-calendar-clockic-calendar-checkic-clock-plusic-chart-baric-tagic-map-pinic-gaugeic-list-check— LNB 아이콘 중복 제거용 (LNB·헤더·embed 레지스트리 아이콘 일치 규칙 도입)
변경
- workspace(admin) LNB: 접기 토글 하단 → 브랜드 우측(헤더), 하단 profile 영역 제거, 레일 스크롤바 비표시(스크롤 유지). 접힘 토글이 우측 sticky 헤더에 가려 잘리던 문제 z 조정으로 해소
- NotificationBell 팝업 색 토큰화: Tailwind 기본 gray →
neutral-*,shadow-xl→shadow-popover,rounded-xl→rounded-16— 전역 버튼 강제 보정 의존 제거. 두 팝업(알림·프로필)이 동일한 라인(neutral-100)·radius 16 체계로 통일 - 전역 버튼 radius 강제(4px
!important) 제외 명단에.ds-page-header__avatar추가 — 규칙 자체 정리는 별도 브랜치 예정
메모
- radius 12 토큰 신설은 검토 후 취소 — 기존
--radius-8/16스케일 사용 (토큰 변경 없음) - docs 토큰 브리지 신설 (
.vitepress/theme/ds-token-bridge.css):@theme블록 토큰이 앱 빌드(Tailwind)에서만 :root로 처리되어 docs 라이브 데모에서 비어 있던 문제 수정. design-system.css의 모든@theme토큰(색 팔레트·의미별칭·폰트·타이포·radius·그림자·spacing)을scripts/gen-docs-bridge.mjs로 자동 생성(docs predev/prebuild 시 실행)해 수기 사본 없이 단일 출처에서 동기화 — 전 데모가 프로덕션과 동일하게 렌더링 - 라이브 데모 최신화: PageHeader(프로필 아바타·팝오버 추가,
.vp-doc h1타이틀 간섭 보정) · LnbSidebar(workspace 패턴 — 브랜드+헤더 토글·하단 profile 제거·접힘 플라이아웃/툴팁/child-active·새 아이콘) · WorkspaceSwitcher 문서에 "워크스페이스 미사용" 상태 라벨 명시
2026-06 — 모달 표준 v1 (design/modal-dim-display-standard)
신규
- 토큰:
--ds-dim-40(모달 딤 40%), z-index 스케일--ds-z-*(base 0 / raised 10 / nav 20 / dropdown 40 / modal 50 / modal-top 60 / toast 80 / tooltip 90) - 컴포넌트:
.ds-modal-backdrop— 딤 40% + 가운데 정렬 + z 토큰 + 스크롤 잠금 내장 (overscroll-behavior: none). 변형--top(z-60) - 패턴 문서: 모달 v1 — 표시 · 닫기 · 스크롤 잠금 · 내부 타이포 · 예외 목록 · 라이브 예시
표준 적용 (전 모달)
- dim을 화면별로 제어하지 않는다 — 공통 dim opacity 40%(공용
.ds-modal-backdrop) 적용, 항상 표시, 바깥 클릭으로 닫히지 않음. team-manager · admin(native/탭 인라인/scheduler) · general-affairs(4005) · management-dashboard(4006) · 공유 위자드/설정 모달 적용 - 저장확인 + pristine 가드: 폼 모달의 X/취소/Esc는 변경이 있을 때만 확인 팝업 경유, 변경 없으면 바로 닫힘
- 모달 내부 타이포: 필드 타이틀 13px(
text-b5) + 간격 6px(mb-1.5), 콘텐츠 그룹 간격 16px(space-y-4) - 상태(활성/비활성) 선택을 드롭다운으로 통일 (pill 토글 · 스위치 5곳 변환)