From 7b22fe88a1142c76ad77663610a9936a6e2b7dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?DESKTOP-KPCP6AL=5C=E3=85=85=E3=85=8E?= Date: Sun, 11 Oct 2026 11:36:32 +0900 Subject: [PATCH] fix: align v2 publisher review with HMDesign stories and tokens --- AGENTS.md | 8 ++ README.md | 4 + docs/components/separator.md | 2 + docs/guidelines/brand-color-steps.md | 34 +++++++++ docs/guidelines/designer-theme-guide.md | 2 +- docs/guidelines/runtime-theme-contract.md | 21 +++-- docs/guidelines/runtime-theme.md | 6 +- docs/guidelines/theme-delivery.md | 4 +- docs/guidelines/개발-적용-가이드.md | 2 +- docs/guidelines/프로젝트-지침.md | 2 +- docs/patterns/data-table.md | 4 +- docs/patterns/gnb.md | 2 + .../reports/v2-publisher-review-2026-10-11.md | 75 ++++++++++++++++++ packages/ui/src/theme/contract.ts | 9 +++ .../ui/src/theme/default-values.generated.ts | 10 ++- packages/ui/src/theme/default.css | 12 +++ packages/ui/src/theme/gnb.css | 2 +- packages/ui/src/theme/primitives.css | 12 +++ scripts/check-doc-links.mjs | 67 +++++++++++++++- scripts/check-doc-links.test.mjs | 76 +++++++++++++++++++ scripts/theme-templates/default.css | 12 +++ scripts/theme-templates/primitives.css | 12 +++ src/foundation/color.stories.tsx | 35 +++++---- src/list-screen.css | 5 ++ src/patterns/data-table.stories.tsx | 32 ++++---- src/theme/runtime-loader.stories.tsx | 2 +- themes/default.json | 10 ++- 27 files changed, 413 insertions(+), 49 deletions(-) create mode 100644 docs/guidelines/brand-color-steps.md create mode 100644 docs/reports/v2-publisher-review-2026-10-11.md create mode 100644 scripts/check-doc-links.test.mjs diff --git a/AGENTS.md b/AGENTS.md index 8746b5a..77c5994 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,3 +18,11 @@ 11. 390/768/1280/1440px에서 overflow·줄바꿈·Navigation을 확인한다(Dashboard는 960px 내부 스크롤). 숨김 absolute 알림의 위치 기준과 문서/내부 scrollHeight·clientHeight, ID의 한 줄 식별 가능성도 확인한다. Label/접근성 이름, 키보드·focus 복귀, disabled/readOnly, 핵심 상호작용을 확인한다. 결과는 실제 사용 요소·Pattern 선택/미사용 이유·Navigation·검증·미검증으로 보고하며 확인하지 않은 항목은 `NOT VERIFIED`다. 필요할 때만 [보존된 상세 지침](docs/guidelines/ai-implementation-details.md)을 읽는다. 신규 화면에는 위 필수 규칙이 우선한다. + +## 공식 작업 경로와 Git 정책 — 2026-10-11 사용자 확정 + +- HMDesign 실제 수정은 `D:\디자인시스템\HMDesign-Gitea-Transfer`에서 진행한다. 시작 전 절대경로·`main` 브랜치·공식 origin `https://gitea.hmac.kr/b25027/HMDesign.git`·최신 원격 HEAD·Working Tree·기존 미커밋 변경을 확인한다. +- 퍼블리셔 요청에 필요한 부분만 수정하고 기존 Component·Pattern·Theme·Storybook을 보존한다. 변경 영향과 관련 MD/Storybook 정합성을 검증하고 유효한 기존 정상 검증은 불필요하게 반복하지 않는다. +- 필수 검증을 통과하면 관련 파일만 Stage하고 Staged Diff를 검토한 뒤 Commit·공식 Gitea `main` 일반 Push를 수행한다. 작업별 사용자의 명시적 Commit/Push 보류 지시가 있으면 그 지시를 우선한다. +- Commit/Push 직전에 원격 `main`을 다시 확인한다. 시작 기준 이후 원격이 변경됐거나 중요한 충돌이 있으면 자동으로 덮어쓰지 않고 중단한다. 완료 후 Local/Remote SHA와 Working Tree를 확인한다. +- GitHub 이전 원본 폴더·remote에는 수정/Push하지 않는다. Force Push·Reset·Clean·기존 이력 삭제·백업 브랜치 변경·무관한 파일 Stage를 금지한다. diff --git a/README.md b/README.md index a9ba003..47a8295 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ Storybook은 기존 HM Component·Pattern·Theme·Variant·State·Interaction을 ## Git Clone 설치·실행 (현재 테스트 공유 기준) +v2 추가 5개 요청의 구현·실제 Storybook 검증과 Orange Dark 1/2 보류는 [2026-10-11 선택 검증 보고](docs/reports/v2-publisher-review-2026-10-11.md)를 확인합니다. + 공식 소스 저장소는 [Gitea HMDesign](https://gitea.hmac.kr/b25027/HMDesign)입니다. 이전 원본은 [GitHub HMDesignSystemTest](https://github.com/shkim25027/HMDesignSystemTest)의 `90583ab6046ab5dc1ac35d94d38f3301c9a4dfe1`이며, GitHub 이력을 병합하지 않고 공식 Gitea의 기존 이력 위에 소스만 반영합니다. 먼저 [AGENTS.md](AGENTS.md)를 읽고 기존 Component → Variant/Prop → Composition 순서로 재사용합니다. 현재 검증 범위와 남은 제한은 [2026-10-10 잔여 이슈 회귀 보고](docs/guidelines/publisher-validation-2026-10-10.md)를 확인합니다. @@ -42,6 +44,8 @@ Windows에서는 기존 `start-storybook.bat`도 사용할 수 있습니다. 의 정적 Storybook 빌드는 `npm run build-storybook`, 앱 빌드는 `npm run build`, 타입 확인은 `npm run typecheck`입니다. 명령은 루트 package.json의 현재 scripts를 기준으로 합니다. +문서 내부 링크 검사는 프로젝트 루트에서 `node scripts/check-doc-links.mjs`로 실행합니다. `.git`이 있으면 기존 Git 추적·비추적(ignored 제외) Markdown 수집을 유지하고, `.git` 없는 압축 해제본에서는 파일 시스템과 루트·하위 `.gitignore`의 현재 경로·와일드카드·제외/예외 규칙으로 수집합니다. `.git` 메타데이터와 ignored 폴더는 순회하지 않습니다. Node만 있으면 독립 실행할 수 있으며 결과에 `collectionMode`, 실제 MD/링크 수와 문제 목록을 출력합니다. 종료 코드 0은 통과, 1은 링크 오류, 2는 수집 실행 오류입니다. 회귀 검사는 `node --test scripts/check-doc-links.test.mjs`입니다(두 수집 방식 비교에는 Git 필요). + ## 기존 Component 기반 새 React 화면 ```powershell diff --git a/docs/components/separator.md b/docs/components/separator.md index c5cd67f..b2f0469 100644 --- a/docs/components/separator.md +++ b/docs/components/separator.md @@ -54,6 +54,8 @@ shadcn의 `bg-border`, `h-px`/`w-px` class를 제거하고 theme CSS가 두께· ## 10. 사용자 확정 결정 +2026-10-10: 같은 제목·설명 묶음에서는 제목 아래와 설명 아래에 Separator를 각각 연속으로 사용하지 않고, 필요한 위치 한 곳에만 적용한다. 대표 Story는 제목·설명 묶음 뒤 한 곳에 적용한다. 모든 제목이나 설명에 구분선을 필수로 추가하는 규칙은 아니다. Component 스타일과 API는 유지한다. + 2026-10-02: ui-viz는 참고 자료이며 최종 기준은 현재 HM 구현과 이 문서다(작업 지시). 이 컴포넌트에 대한 개별 수치 결정은 없다. ## 11. 미확정 / 보류 diff --git a/docs/guidelines/brand-color-steps.md b/docs/guidelines/brand-color-steps.md new file mode 100644 index 0000000..d335bfa --- /dev/null +++ b/docs/guidelines/brand-color-steps.md @@ -0,0 +1,34 @@ +# 브랜드 컬러 8단계 확인 + +2026-10-11 v2 요청의 표시 이름은 한맥그린·한맥브라운·한맥오렌지, 단계 순서는 Light 4 → Light 3 → Light 2 → Light 1 → Default → Dark 1 → Dark 2 → Dark 3이다. 읽기 전용 `D:/디자인시스템/ui_viz/docs/brand/hanmac/foundation/color.md` §4.1, `implementations/web/src/tokens/primitives.css`, `docs/foundation/token-inventory.md`의 HEX·단계·승인 상태를 대조했다. 기존 [공개 계약](../../packages/ui/src/theme/contract.ts)과 [Default JSON](../../themes/default.json)의 동일 단계명·HEX는 원본과 일치한다. + +| 단계 | 한맥그린 | 한맥브라운 | 한맥오렌지 | +|---|---|---|---| +| Light 4 | #34DAAA | #ECD4A2 | #FEBA88 | +| Light 3 | #249473 | #C1A770 | #EC8F53 | +| Light 2 | #266651 | #897445 | #F87725 | +| Light 1 | #205241 | #604F32 | #EB5F00 | +| Default | #123328 | #3E3523 | #CC5200 | +| Dark 1 | #0A221D | #352D1D | Needs Review — 공개 Key 없음 | +| Dark 2 | #071E18 | #2F281C | Needs Review — 공개 Key 없음 | +| Dark 3 | #020A07 | #1D1810 | #4F2000 | + +확정 칸의 Key는 `color.palette.{green|brown|orange}-{light-4|light-3|light-2|light-1|default|dark-1|dark-2|dark-3}` 중 실제 선언된 Key다. CSS는 동일 suffix의 `--hm-sem-palette-*` → `--hm-*` Primitive를 사용한다. 기존 Key·값을 보존하고 확정 누락분 8개만 추가했다. 전체 공개 입력은 73 → 81개(색상 71 → 79개, 기존 radius 2개)다. 미확정 칸에는 공개 Key·CSS 변수·HEX를 새로 만들지 않는다. + +## Legacy Key 보존과 미확정 항목 + +| 기존 Key | 확인된 HEX | 확인 필요 사항 | +|---|---|---| +| color.palette.brown-deep | #221D13 | 기존 Button gradient 소비 유지. 새 Dark 2 #2F281C·Dark 3 #1D1810과 다르므로 alias/대체하지 않음 | +| color.palette.orange-light | #D35A00 | 기존 Button 배경 0% stop 유지. 브랜드 Light 1 #EB5F00과 별도 | +| color.palette.orange-mid | #CE5600 | 기존 Button 배경 30% stop 유지. 브랜드 단계 대응 없음; Default #CC5200과도 별도 | +| color.palette.orange-dark | #B44B00 | 기존 Button 배경 65% stop 유지. 번호 있는 Dark 단계로 승격하지 않음 | +| color.palette.orange-deep | #783402 | 기존 Button 배경 100% stop 유지. 브랜드 Dark 3 #4F2000과 별도 | + +Button 원본 근거는 `ui_viz/docs/brand/hanmac/components/button.md`의 D-087 및 `implementations/web/src/components/Button/Button.css`다. 단순 단계 이름 변경으로 기존 Button의 색상을 바꾸지 않는다. Brown Highlight와 Orange Border 계열도 기존 Key·값·용도를 보존한다. + +Orange Dark 1의 원본 후보 `hm-orange-dark-1: #BC4C00`, Dark 2 후보 `hm-orange-dark-2: #883700`은 위 Color §4.1·Primitive CSS·inventory 모두 **Needs Review**다. 원본 Figma 대조/디자인 승인 확인 후 반영해야 한다. 기존 `color.palette.orange-border: #BC4C00`은 Button border 범위 확정값으로 유지하지만 같은 HEX를 근거로 브랜드 Dark 1을 확정하지 않는다. 이미지 추출·보간·밝기 순서 추정은 하지 않는다. + +## 호환성과 Storybook + +[Color 대표 Story](../../src/foundation/color.stories.tsx)는 브랜드별 8개 순서 슬롯을 제공한다. Green 8/8·Brown 8/8 확정, Orange 6/8 확정·Dark 1/2 미확정이다. Legacy Button 색상은 별도로 표시하고 동일 Key를 계속 사용한다. 추가 8개 팔레트는 Color Reference에서 소비하며 기존 Component mapping을 새 값에 연결하지 않는다. 기존 public Key·Property·type·Default·API·schemaVersion 1과 서비스 partial JSON 호환성을 유지한다. [Runtime Theme](runtime-theme.md)의 geometry/상태 권한과 Designer의 기존 17개 상위 역할 입력 UI는 확대하지 않는다. 3색 전체 HEX·단계 확정은 PARTIAL이다. diff --git a/docs/guidelines/designer-theme-guide.md b/docs/guidelines/designer-theme-guide.md index d027f50..dd2e180 100644 --- a/docs/guidelines/designer-theme-guide.md +++ b/docs/guidelines/designer-theme-guide.md @@ -37,7 +37,7 @@ resetRuntimeTheme(document.documentElement) - 일반 새 탭은 독립 세션입니다. 탭 복제 또는 opener로 연 탭은 브라우저가 초기 sessionStorage를 복사할 수 있으며 이후 편집은 탭별로 독립적입니다. - 서버/DB/Publish 기능은 없습니다. 같은 브라우저 탭의 Storybook preview origin에 `sessionStorage`로 보관하며, 세션 저장을 사용할 수 없는 환경에서도 현재 화면의 편집/Export는 동작합니다. 장기 보관은 JSON Export를 사용하세요. - Global Theme는 `document.documentElement`에 적용하며 body Portal도 같은 Theme를 사용합니다. wrapper 밖으로 Portal된 popup은 Scoped Theme를 자동 전달받지 않습니다. 이 제한은 기존 FOLLOW_UP입니다. -- 전체 Runtime Contract는 73개 입력을 유지합니다. 배포하는 Default JSON은 71개 컬러이며 radius/geometry는 package에서 관리합니다. 편집 화면은 상위 컬러 역할 17개만 노출하며 Component Token, radius, palette/gradient 개별 입력 UI를 만들지 않습니다. 기존 컬러 계약의 palette/gradient는 유효 JSON으로 Import할 수 있으며 gradient는 세트 전체를 제공해야 합니다. +- 전체 Runtime Contract는 81개 입력입니다(2026-10-11 확정 브랜드 팔레트 8개 추가, 기존 73개 보존). 배포하는 Default JSON은 79개 컬러이며 radius/geometry는 package에서 관리합니다. 편집 화면은 상위 컬러 역할 17개만 노출하며 Component Token, radius, palette/gradient 개별 입력 UI를 만들지 않습니다. 기존 컬러 계약의 palette/gradient는 유효 JSON으로 Import할 수 있으며 gradient는 세트 전체를 제공해야 합니다. - 명시적 `[data-hm-theme]` scope는 독립 HM preset이므로 Global Theme를 이어받지 않습니다. Production에서 같은 root는 한 owner가 Apply/Reset을 관리합니다. - Storybook 편집기 설치는 이번 코드 변경을 적용하는 최초 빌드가 필요합니다. 설치 후 컬러 값을 바꾸는 운영에는 재빌드가 필요 없습니다. diff --git a/docs/guidelines/runtime-theme-contract.md b/docs/guidelines/runtime-theme-contract.md index 901da57..6c639d8 100644 --- a/docs/guidelines/runtime-theme-contract.md +++ b/docs/guidelines/runtime-theme-contract.md @@ -13,7 +13,7 @@ | `color.action.cancel` | `--hm-sem-action-cancel` | `#6e6759` | 공개 취소 역할 | Yes | Button `variant="cancel"` + `appearance="outline"` | | `color.action.destructive` | `--hm-sem-action-destructive` | `#d32f2f` | 오류/위험 역할 | Yes | checkbox, radio, badge, input, button | | `color.stroke.default` | `--hm-sem-stroke-default` | `#9a9284` | 기본 border | Yes | checkbox, radio, switch, badge | -| `color.stroke.input.default` | `--hm-sem-stroke-input-default` | `#dedbd2` | 입력/패널 divider border | Yes | table, input, select | +| `color.stroke.input.default` | `--hm-sem-stroke-input-default` | `#dedbd2` | 입력/패널 divider border | Yes | table, input, select, GNB 콘텐츠 LNB divider | | `color.stroke.input.focus` | `--hm-sem-stroke-input-focus` | `#266651` | 공통 Focus border/ring | Yes | input, select, button, checkbox, calendar, slider Keyboard Focus halo 등; GNB/LNB는 contextual 예외 | | `color.bg.brand.stop.0` | `--hm-sem-brand-bg-stop-0` | `#0a221d` | 기존 gradient stop: color.bg.brand.stop.0 | Yes | Theme palette / adapter | | `color.bg.brand.stop.12` | `--hm-sem-brand-bg-stop-12` | `#071e18` | 기존 gradient stop: color.bg.brand.stop.12 | Yes | Theme palette / adapter | @@ -78,14 +78,23 @@ | `color.palette.gray-dark` | `--hm-sem-palette-gray-dark` | `#5a5a5a` | 선택적 Theme 팔레트: gray-dark | Yes | button | | `color.palette.indicator` | `--hm-sem-palette-indicator` | `#2d2d2d` | 선택적 Theme 팔레트: indicator | Yes | select | -총 73개. 기존 25개 + 핵심 역할 6개 + 선택적 팔레트 42개. 팔레트는 컴포넌트별 계약이 아니며 Primary / Active / Focus를 개별 지정하면 팔레트 alias보다 우선한다. Focus와 Active, Primary와 On Accent는 각각 sibling이며 서로 alias하지 않는다. +| `color.palette.green-dark-3` | `--hm-sem-palette-green-dark-3` | `#020a07` | ui_viz 확정 브랜드 단계 | Yes | Color Reference / 선택적 Theme palette | +| `color.palette.brown-light-3` | `--hm-sem-palette-brown-light-3` | `#c1a770` | ui_viz 확정 브랜드 단계 | Yes | Color Reference / 선택적 Theme palette | +| `color.palette.brown-dark-2` | `--hm-sem-palette-brown-dark-2` | `#2f281c` | ui_viz 확정 브랜드 단계 | Yes | Color Reference / 선택적 Theme palette | +| `color.palette.brown-dark-3` | `--hm-sem-palette-brown-dark-3` | `#1d1810` | ui_viz 확정 브랜드 단계 | Yes | Color Reference / 선택적 Theme palette | +| `color.palette.orange-light-3` | `--hm-sem-palette-orange-light-3` | `#ec8f53` | ui_viz 확정 브랜드 단계 | Yes | Color Reference / 선택적 Theme palette | +| `color.palette.orange-light-2` | `--hm-sem-palette-orange-light-2` | `#f87725` | ui_viz 확정 브랜드 단계 | Yes | Color Reference / 선택적 Theme palette | +| `color.palette.orange-light-1` | `--hm-sem-palette-orange-light-1` | `#eb5f00` | ui_viz 확정 브랜드 단계 | Yes | Color Reference / 선택적 Theme palette | +| `color.palette.orange-dark-3` | `--hm-sem-palette-orange-dark-3` | `#4f2000` | ui_viz 확정 브랜드 단계 | Yes | Color Reference / 선택적 Theme palette | + +총 81개. 기존 25개 + 핵심 역할 6개 + 선택적 팔레트 50개(2026-10-11 ui_viz 확정 단계 8개 추가). 팔레트는 컴포넌트별 계약이 아니며 Primary / Active / Focus를 개별 지정하면 팔레트 alias보다 우선한다. Focus와 Active, Primary와 On Accent는 각각 sibling이며 서로 alias하지 않는다. `color.action.cancel`의 공개 key·기본값은 보존한다. 2026-10-10 Gray 통일 요청에 따라 기본 Outline 취소 ink는 기존 Gray palette `#767676`을 사용한다. 기본값과 다른 명시적 취소 컬러 override는 내부 Button mapping에 전달하고 Reset 시 복원한다. 전체 Default preset을 주입해도 기본 Gray와 동일하다. Solid 취소는 기존 Gray mapping을 유지한다. [Runtime Theme](runtime-theme.md). -# 디자인팀 Runtime 운영 추가 결정 — 2026-10-06 +# 디자인팀 Runtime 운영 결정 — 2026-10-06, 팔레트 보강 2026-10-11 -기존 v1 입력 73개와 CSS property/type 계약을 유지한다. 공개 API `isRuntimeTheme(unknown): value is RuntimeTheme`를 제공한다. 이 사전 검증은 DOM을 변경하지 않는다. 기존 `applyRuntimeTheme`의 invalid 입력 시 reset 정책은 유지한다. 편집기는 사전 검증으로 invalid Import를 차단해 마지막 유효 Theme를 보존한다. +기존 v1 입력 73개의 Key·CSS property·type·Default를 유지하고 확정 팔레트 8개만 추가한다. Orange Dark 1·2는 Needs Review라 공개 입력에 추가하지 않는다. 공개 API `isRuntimeTheme(unknown): value is RuntimeTheme`를 제공한다. 이 사전 검증은 DOM을 변경하지 않는다. 기존 `applyRuntimeTheme`의 invalid 입력 시 reset 정책은 유지한다. 편집기는 사전 검증으로 invalid Import를 차단해 마지막 유효 Theme를 보존한다. -Default 컬러의 운영 원본은 `themes/default.json`(71개 컬러)이다. `scripts/theme-defaults.mjs`가 JSON과 컬러 literal 없는 alias/geometry template에서 package `primitives.css`, `default.css`, `default-values.generated.ts`를 생성한다. `contract.ts`의 defaultValue와 `defaultRuntimeTheme`는 생성 데이터를 읽는다. 2개 radius/geometry는 template에서 유지한다. CSS fallback/현재 색을 유지하고 세 가지 최종 명령의 pre hook에서 stale 생성 데이터를 검사한다. Theme-only 산출은 `npm run build:themes`로 분리한다. +Default 컬러의 운영 원본은 `themes/default.json`(79개 컬러)이다. `scripts/theme-defaults.mjs`가 JSON과 컬러 literal 없는 alias/geometry template에서 package `primitives.css`, `default.css`, `default-values.generated.ts`를 생성한다. `contract.ts`의 defaultValue와 `defaultRuntimeTheme`는 생성 데이터를 읽는다. 2개 radius/geometry는 template에서 유지한다. CSS fallback/현재 색을 유지하고 세 가지 최종 명령의 pre hook에서 stale 생성 데이터를 검사한다. Theme-only 산출은 `npm run build:themes`로 분리한다. Storybook `Theme/Designer Playground/Editor`는 17개 상위 컬러 역할과 JSON URL/파일 Preview를 제공하는 소비자다. Global decorator에서 `resolveRuntimeTheme(theme, defaultJson)` 후 Production `applyRuntimeTheme(document.documentElement, theme)` → CSS Custom Properties → 기존 Mapping으로 적용한다. JSON Export/Import는 동일 v1 계약이다. 컬러 artifact/편집기만 radius를 거부한다. Production API의 기존 radius/gradient 계약은 유지한다. @@ -95,4 +104,4 @@ Storybook `Theme/Designer Playground/Editor`는 17개 상위 컬러 역할과 JS ## Dashboard 상태 색상 범위 -Dashboard의 `active / hold / done / issue` 업무 상태 계약은 `packages/ui/src/patterns/dashboard-status/contract.ts`에서 ID·label·tone을 제공한다. 상태 변수 원본은 `packages/ui/src/patterns/dashboard-status/dashboard-status.css`이며 `@hm-design/ui/dashboard-status.css`로 배포한다. `src/patterns/dashboard-status-contract.css`는 기존 Story/HTML 경로의 호환 import다. Theme CSS 다음에 상태 CSS를 로드하고 `--hm-dashboard-status-active|hold|done|issue`를 화면 요소의 색상에 별도로 연결한다. 상태 CSS 자체는 Badge나 `data-status`를 자동으로 꾸미지 않는다. active/hold/issue는 기존 Story 색상을 보존하는 compatibility mapping이고 done은 `:root` 및 `[data-hm-theme]`에서 해당 scope의 기존 HM white token을 참조한다. 기존 HM Theme 및 Public Runtime Token/JSON 73개는 변경하지 않으며, 실제 Production 소비처가 반복되어 공통 색상 의미가 확정될 때만 Semantic Token 승격을 검토한다. +Dashboard의 `active / hold / done / issue` 업무 상태 계약은 `packages/ui/src/patterns/dashboard-status/contract.ts`에서 ID·label·tone을 제공한다. 상태 변수 원본은 `packages/ui/src/patterns/dashboard-status/dashboard-status.css`이며 `@hm-design/ui/dashboard-status.css`로 배포한다. `src/patterns/dashboard-status-contract.css`는 기존 Story/HTML 경로의 호환 import다. Theme CSS 다음에 상태 CSS를 로드하고 `--hm-dashboard-status-active|hold|done|issue`를 화면 요소의 색상에 별도로 연결한다. 상태 CSS 자체는 Badge나 `data-status`를 자동으로 꾸미지 않는다. active/hold/issue는 기존 Story 색상을 보존하는 compatibility mapping이고 done은 `:root` 및 `[data-hm-theme]`에서 해당 scope의 기존 HM white token을 참조한다. 기존 HM Theme 및 Public Runtime Token/JSON에 상태 입력은 추가하지 않으며, 실제 Production 소비처가 반복되어 공통 색상 의미가 확정될 때만 Semantic Token 승격을 검토한다. diff --git a/docs/guidelines/runtime-theme.md b/docs/guidelines/runtime-theme.md index 31e1e8a..d86ac62 100644 --- a/docs/guidelines/runtime-theme.md +++ b/docs/guidelines/runtime-theme.md @@ -1,5 +1,7 @@ # Runtime Theme Contract v1 +브랜드 컬러 8단계의 확정값·Legacy 대응 보류 및 공개 Key 보존은 [브랜드 컬러 단계](brand-color-steps.md)를 따른다. 미확정 표시는 새 Runtime Token이나 색상 승인이 아니다. + 2026-10-06 · 분류: HM_THEME / Theme infrastructure. Component props, DOM, Radix 상태, Portal, 키보드, ARIA, breakpoint는 변경하지 않았다. ## 현재 파일과 적용 흐름 @@ -12,7 +14,7 @@ React 외 실행 형식으로 변환할 때도 이 문서의 Token·CSS Custom Properties·Runtime 적용 계약을 유지한다. Target Runtime, standalone asset, `file://` 검증 및 AI/도구 독립성 기준은 [Runtime Portability Policy](runtime-portability.md)를 따른다. -소비 CSS는 `tailwindcss` → `@hm-design/ui/tailwind.css` → `@hm-design/ui/theme.css` 순서로 앱 entry에서 로드한다. Dashboard 상태 표시가 있으면 그 뒤에 `@hm-design/ui/dashboard-status.css`와 소비 Badge mapping을 연결한다([상태 예제](../patterns/dashboard-layout.md#dashboard-업무-상태-공통-계약)). Status CSS는 변수만 정의하며 73개 Runtime Theme 계약에 새 상태 Token을 추가하지 않는다. [Tailwind 검색 범위](runtime-portability.md#21-tailwind-v4-소비자-검색-범위)와 [Noto Sans KR·Asset 경로](runtime-portability.md#41-noto-sans-kr-경로-실행-환경별-확인)는 실행 환경별 소비 가이드를 따른다. +소비 CSS는 `tailwindcss` → `@hm-design/ui/tailwind.css` → `@hm-design/ui/theme.css` 순서로 앱 entry에서 로드한다. Dashboard 상태 표시가 있으면 그 뒤에 `@hm-design/ui/dashboard-status.css`와 소비 Badge mapping을 연결한다([상태 예제](../patterns/dashboard-layout.md#dashboard-업무-상태-공통-계약)). Status CSS는 변수만 정의하며 81개 Runtime Theme 계약에 새 상태 Token을 추가하지 않는다. [Tailwind 검색 범위](runtime-portability.md#21-tailwind-v4-소비자-검색-범위)와 [Noto Sans KR·Asset 경로](runtime-portability.md#41-noto-sans-kr-경로-실행-환경별-확인)는 실행 환경별 소비 가이드를 따른다. Default는 특별한 컴포넌트 내부 상수가 아니라 이 계약을 만족하는 기본 제공 Theme Set이다. 주입이 없으면 패키지 CSS의 Default preset을 사용한다. `defaultRuntimeTheme`는 동일한 값을 가진 JSON 형태 preset이다. Story의 CSS fallback / Default preset 주입 비교와 계약 값 검사로 두 표현의 일치를 검증한다. @@ -22,7 +24,7 @@ Default는 특별한 컴포넌트 내부 상수가 아니라 이 계약을 만 - **내부 Derived**: `--hm-sem-surface-highlight`(Accent 45%), gradient 조합, shadow/overlay 조합, shadcn adapter(`--primary`, `--ring` 등). 외부 JSON은 이 변수를 직접 쓰지 않는다. - **내부 Component Mapping**: `--hm-checkbox-*`, `--hm-calendar-*`, `--hm-pagination-*` 등. 기존 이름을 유지한다. Runtime JSON으로 받지 않는다. - Typography, height, spacing, gradient geometry, shadow geometry, responsive layout은 코드 계약이다. 이번 컬러 구조 변경으로 Runtime 범위를 확대하지 않았다. -- Typography 현황(2026-10-08): 기존 크기와 ui_viz 승인 변경값을 유지한다. `packages/ui/src/theme/*.css`의 font-size는 px이고 shadcn 유래 Tailwind class(`text-sm` 등)는 rem 기반이라 단위가 혼재한다. 소비자가 Tailwind class나 `className`으로 크기를 덮어쓰면 Theme 값보다 우선할 수 있다. Typography Role(heading/body/label 등)은 아직 공통 Token으로 도입하지 않았고 Runtime JSON 73개 계약에도 포함하지 않는다. +- Typography 현황(2026-10-08): 기존 크기와 ui_viz 승인 변경값을 유지한다. `packages/ui/src/theme/*.css`의 font-size는 px이고 shadcn 유래 Tailwind class(`text-sm` 등)는 rem 기반이라 단위가 혼재한다. 소비자가 Tailwind class나 `className`으로 크기를 덮어쓰면 Theme 값보다 우선할 수 있다. Typography Role(heading/body/label 등)은 아직 공통 Token으로 도입하지 않았고 Runtime JSON 81개 계약에도 포함하지 않는다. 기존 25개 allowlist의 JSON 이름과 validation을 유지하고 Active / on Active / Inverse Text / Subtle Surface / Accent / on Accent를 추가했다. 같은 CSS property로 향하는 중복 공개 key는 없다. Component 수백 개의 Token을 요구하는 대신 Theme 팔레트는 선택적 확장이다. 핵심 역할만 지정해도 사용할 수 있다. 팔레트 이름은 색상 계열을 유지하며, 역할 Token은 각각 독립적으로 override할 수 있다. diff --git a/docs/guidelines/theme-delivery.md b/docs/guidelines/theme-delivery.md index 6578f72..b55e4c2 100644 --- a/docs/guidelines/theme-delivery.md +++ b/docs/guidelines/theme-delivery.md @@ -12,7 +12,7 @@ HMDesignSystem/ │ ├─ ui/ # @hm-design/ui: logic, contract, mapping, runtime │ └─ center-graphic/ # 독립 Graphic package ├─ themes/ -│ ├─ default.json # 71개 public color 값의 공식 Default 원본 +│ ├─ default.json # 79개 public color 값의 공식 Default 원본 │ ├─ service-example.json # 5개 역할만 바꾸는 명시적 예제 │ └─ drafts/ # 과거 초안; Theme 산출 대상 아님 ├─ scripts/ @@ -23,7 +23,7 @@ HMDesignSystem/ └─ docs/guidelines/ ``` -`themes/default.json`은 기존 v1 `{schemaVersion:1,tokens:{...}}` 계약을 사용한다. Component Token은 없다. 73개 Runtime 입력 중 71개 컬러를 포함한다. 나머지 두 radius와 layout은 패키지 template에서 관리한다. 서비스 Theme는 같은 계약의 부분 컬러 override다. +`themes/default.json`은 기존 v1 `{schemaVersion:1,tokens:{...}}` 계약을 사용한다. Component Token은 없다. 81개 Runtime 입력 중 79개 컬러를 포함한다. 나머지 두 radius와 layout은 패키지 template에서 관리한다. 서비스 Theme는 같은 계약의 부분 컬러 override다. ## 단일 Default 원본과 package fallback diff --git a/docs/guidelines/개발-적용-가이드.md b/docs/guidelines/개발-적용-가이드.md index eb1472a..dd69600 100644 --- a/docs/guidelines/개발-적용-가이드.md +++ b/docs/guidelines/개발-적용-가이드.md @@ -32,7 +32,7 @@ AI 신규·수정 UI 작업은 사용자의 별도 요청 없이 기존 Componen | `.storybook/`, `src/components/*.stories.tsx` | 현재 시제품 → 계속 사용 | 실제 컴포넌트 검수. 패키지 산출물에는 Story를 넣지 않음 | | `packages/ui/src/patterns/`, `packages/ui/src/theme.css` | 현재 구현 | 업무/Layout composition과 기본 Theme CSS. Library 산출물은 `npm run build:packages`로 생성 | | `packages/center-graphic/` | 현재 구현 | `@hm-design/center-graphic` 독립 package 배포 경계. UI package와 필수 의존 없음 | -| `packages/ui/src/theme/contract.ts`, `runtime.ts` | 현재 구현 | 공개 73개 입력, 형식/범위 검증, atomic fallback·cleanup, Global/Scoped 적용. `@hm-design/ui/theme` export | +| `packages/ui/src/theme/contract.ts`, `runtime.ts` | 현재 구현 | 공개 81개 입력, 형식/범위 검증, atomic fallback·cleanup, Global/Scoped 적용. `@hm-design/ui/theme` export | | `themes/drafts/` | Runtime MVP 제안 | 디자인팀의 Theme 값 초안·검수 이력. Git 커밋만으로 운영 Theme이 바뀌지 않으며 패키지에도 넣지 않음 | | `themes/*.json` | 현재 구현 | `themes/default.json`은 공식 Default Color Source of Truth. 검증된 top-level JSON은 Theme CDN 배포 단위이며 Git push와 CDN publish는 별도 단계 | | 서비스 앱의 bootstrap | 개발팀 앱 | 패키지 버전 설치, themeId/mode 결정, API/CDN fetch·캐시·실패 처리, 검증된 값 적용 | diff --git a/docs/guidelines/프로젝트-지침.md b/docs/guidelines/프로젝트-지침.md index 8bdd2a8..e73e3d0 100644 --- a/docs/guidelines/프로젝트-지침.md +++ b/docs/guidelines/프로젝트-지침.md @@ -43,7 +43,7 @@ Primitive (설계·빌드 시 관리) → Semantic (컴포넌트 참조) → Com 컴포넌트는 `blue-600` 같은 Primitive나 `#2563eb`, `8px` 같은 실제 값을 직접 참조하지 않는다. Tailwind를 쓰는 경우에도 `bg-primary`의 `--color-primary` → `--primary` → `--hm-sem-action-primary` adapter 연결을 확인한다. **Runtime override는 공개 allowlist의 Semantic/Theme palette 값만** 허용한다. Primitive, 컴포넌트 전용 내부 변수, 임의 CSS selector/rule/JavaScript는 Publish 대상이 아니다. -현재 allowlist는 `packages/ui/src/theme/contract.ts`의 73개 입력이다(색상 Semantic/Theme palette와 input/container radius). 정확한 이름·타입·기본값·허용 범위는 이 계약과 `runtime-theme-contract.md`를 따른다. Shadow geometry는 현재 공개 입력이 아니다. Typography와 spacing은 실제 사용처와 레이아웃 영향 검토 후 개별 등록한다. 문서에 예시가 있다는 이유로 곧바로 Runtime 허용 Token이 되지 않는다. +현재 allowlist는 `packages/ui/src/theme/contract.ts`의 81개 입력이다(색상 Semantic/Theme palette와 input/container radius). 정확한 이름·타입·기본값·허용 범위는 이 계약과 `runtime-theme-contract.md`를 따른다. Shadow geometry는 현재 공개 입력이 아니다. Typography와 spacing은 실제 사용처와 레이아웃 영향 검토 후 개별 등록한다. 문서에 예시가 있다는 이유로 곧바로 Runtime 허용 Token이 되지 않는다. 기본 Theme은 `@company/ui`가 배포하는 CSS에 포함하고 앱이 초기 렌더 전에 로드한다. Runtime 값이 없거나 가져오기·검증에 실패해도 기본값으로 UI가 렌더링되어야 한다. 현재 Light/Dark 구분은 없으며 단일 HM Theme 값을 기준으로 한다([테마 토큰 가이드](테마-토큰-가이드.md) 참조). diff --git a/docs/patterns/data-table.md b/docs/patterns/data-table.md index 73fb933..cab65d6 100644 --- a/docs/patterns/data-table.md +++ b/docs/patterns/data-table.md @@ -54,7 +54,7 @@ TanStack Table v9의 state model 그대로. v9는 기능을 명시 등록하는 - 상태는 TanStack 내부가 소유한다(필요하면 `useDataTable`에 `state` / `atoms`로 제어). 검색은 `table.setGlobalFilter`, 열 필터는 `table.getColumn(id)?.setFilterValue`, 초기화는 `resetGlobalFilter(true)` / `resetColumnFilters(true)`로 한다. 필터 변경 시 TanStack이 page index를 0으로 되돌린다. - error는 Data Table이 소유하지 않는다(소비자가 Alert / Toast로 처리). -조회 버튼을 사용하는 목록은 [FilterBar의 draft / applied 소비 예제](filter-bar.md#61-조회-버튼으로-적용하는-소비-예제)를 따른다. 입력 중에는 draft만 바꾸고 조회 시 table 필터 또는 `useMemo`로 만든 `data`를 갱신한다. 초기화는 입력값과 적용값, 사용 중인 table 필터와 페이지를 소비자가 함께 처리한다. 대표 Data Table Story는 표 기능의 운영 Reference이며, `WithFilterBarStory`(`patterns-data-data-table--with-filter-bar-story`)는 **입력 즉시 적용**을 확인하는 숨김 verification이다. 조회 버튼으로 조건을 확정하는 공개 API나 서버 호출 기능은 제공하지 않는다. +조회 버튼을 사용하는 목록은 [FilterBar의 draft / applied 소비 예제](filter-bar.md#61-조회-버튼으로-적용하는-소비-예제)를 따른다. 입력 중에는 draft만 바꾸고 조회 시 table 필터 또는 `useMemo`로 만든 `data`를 갱신한다. 초기화는 입력값과 적용값, 사용 중인 table 필터와 페이지를 소비자가 함께 처리한다. 대표 Data Table Story는 표 기능의 운영 Reference이며, `WithFilterBarStory`(`patterns-data-data-table--with-filter-bar-story`)도 **조회 버튼으로 적용**하는 같은 소비 조합을 확인하는 숨김 verification이다. Pattern 자체는 조회 버튼으로 조건을 확정하는 별도 공개 API나 서버 호출 기능을 제공하지 않는다. ## 7. 조합 컴포넌트 @@ -79,6 +79,8 @@ semantic `` 유지. 정렬 열 `
`(ascending / descending), ## 11. Responsive +2026-10-11 v2 추가 요청: 대표 Story의 목록 검색과 ‘열’ 버튼은 공간이 충분할 때 같은 행에 둔다. 검색 form의 내용 폭을 기준으로 공간이 부족할 때만 목록 도구가 다음 행으로 줄바꿈된다. 기본 검색은 form 안에서 가운데, 추가 조건은 왼쪽, ‘열’ 버튼은 우측 정렬한다. 간격은 기존 `--hm-filter-bar-gap`(8px)을 재사용하고 모바일 검색·버튼 행 정렬을 유지한다. `src/list-screen.css`의 `hm-data-table-*` 조합에만 적용하며 공통 FilterBar·DataTable Component API/CSS는 변경하지 않는다. + Table primitive의 container가 가로 스크롤한다. Pattern은 `[data-slot="data-table"] { min-width: 0 }`로 grid / flex 부모 안에서 페이지를 밀지 않게 한다. 390px에서 페이지 가로 overflow 없음(표만 520px 안에서 스크롤), footer는 요약 / 페이지 크기 / 페이지 이동이 줄바꿈된다. 카드형 변환은 없다. ### 11.1 식별자 줄바꿈과 열 폭 diff --git a/docs/patterns/gnb.md b/docs/patterns/gnb.md index 4af87a4..f04a89f 100644 --- a/docs/patterns/gnb.md +++ b/docs/patterns/gnb.md @@ -1,5 +1,7 @@ # GNB (Pattern) +> 2026-10-11 v2 추가 요청: 콘텐츠 LNB와 본문 사이 세로 구분선은 공통 `gnb.css`에서 1px solid `--hm-sem-stroke-input-default`(Default `#DEDBD2`)를 사용한다. 기존 `--hm-sem-stroke-default`(`#9A9284`) 연결만 교체하며 외곽 Border·배경·Green LNB 2Depth 및 Storybook 목차의 Alpha 8%는 유지한다. + > 2026-10-10 추가 수정 요청 기준: 기존 Reference 콘텐츠 LNB는 본문과 동일 배경·세로 divider 1개, 아이콘 기본 생략(선택적 허용), 해당 컬러 계열 선택·Hover. data-hm-color="brown" 범위는 Brown 색상. 신규 AI GNB_ONLY Sidebar 금지 정책 유지. 과거 검증 기록은 당시 결과이며 현재 검증은 [추가 수정 보고서](../guidelines/publisher-additional-review-2026-10-10.md)를 따릅니다. > **SINGLE NAVIGATION MODE.** 독립 GNB / LNB / Dashboard Mode는 함께 사용하지 않는다. 2026-10-08 확정: GNB ONLY는 기존 LNB 부품을 재사용하는 내부 Sidebar를 포함할 수 있다(§15). [공통 정책](../guidelines/navigation-single-mode.md)이 이전 조합 기록보다 우선한다. diff --git a/docs/reports/v2-publisher-review-2026-10-11.md b/docs/reports/v2-publisher-review-2026-10-11.md new file mode 100644 index 0000000..2f81bca --- /dev/null +++ b/docs/reports/v2-publisher-review-2026-10-11.md @@ -0,0 +1,75 @@ +# HMDesign v2 추가 5개 요청 — 선택 검증 보고 + +RESULT: **PARTIAL**. 브랜드 명칭·8단계 표시 구조와 확정 팔레트, LNB 구분선, Data Table 목록 도구, Separator 문서, Git 없는 문서 검사를 반영했다. Green·Brown은 8/8, Orange는 6/8 확정이다. Orange Dark 1·2는 읽기 전용 ui_viz 원본의 Needs Review 상태를 보존한다. [컬러 근거와 보류](../guidelines/brand-color-steps.md), [공개 계약](../guidelines/runtime-theme-contract.md). + +## 작업 환경과 기준 + +- PROJECT_PATH: `D:/디자인시스템/HMDesign-Gitea-Transfer` +- BRANCH: `main` +- ORIGIN: `https://gitea.hmac.kr/b25027/HMDesign.git` +- BASE_SHA / 시작 Local·Remote HEAD: `1c4e0bb9a1ef4e5db391b93d7c874f78a0e6bcc5` +- 시작 Working Tree: CLEAN. `backup/main-before-transfer-20261011` 및 기존 작업 브랜치 확인, 변경 없음. +- 범위: 기존 Component/Theme와 Storybook Reference 수정(React). 신규 업무 화면 없음. Commit·일반 Push는 후속 명시 지시로 승인됐으며 종료 SHA·시간·Git 상태는 최종 응답 및 로컬 완료 보고에 기록한다. +- 실행 환경: Windows, Node 20.20.1, npm 10.8.2. 최종 정적 Storybook은 6025, 개발 렌더 검사는 6024. + +## 5개 수정 + +| 항목 | 실제 변경과 결과 | 영향 범위 | +|---|---|---| +| 01_COLOR | 한맥그린·한맥브라운·한맥오렌지와 8단계 슬롯. ui_viz 문서·Primitive CSS·inventory에서 Confirmed인 누락 8개만 추가. Green 8/8, Brown 8/8, Orange 6/8. | Color Story·JSON·공개 계약·생성 CSS. 기존 Key/HEX/Mapping 보존. PARTIAL | +| 02_LNB_BORDER | 공통 `gnb.css`의 콘텐츠 LNB 세로선 참조를 stroke-input-default로 변경. 실제 Green/Brown 모두 1px solid rgb(222,219,210). | 기존 콘텐츠 LNB Reference. 외곽 Border·배경·선택/Hover/Focus 유지. PASS | +| 03_DATA_TABLE_LAYOUT | 검색 form과 열 표시를 한 도구 행으로 묶고, 실제 내용 폭이 부족할 때만 줄바꿈. 검색 내부 gap/가운데·왼쪽 정렬 유지. | `hm-data-table-*` opt-in Story 소비 조합. 공통 FilterBar API/CSS 미변경. PASS | +| 04_SEPARATOR_DOC | 같은 제목·설명 묶음에서 필요한 한 곳에만 구분선 적용. 모든 제목에 필수로 추가하는 규칙 없음. | MD만 변경, 기존 Story/Component 스타일·API 유지. PASS | +| 05_GITLESS_DOC_CHECK | Git 메타데이터 유무와 손상/권한 오류 구분. Git 수집 유지, 비Git은 파일 시스템 및 현재 .gitignore 규칙 사용. 0/1/2 종료 코드. | 검사 스크립트·회귀 테스트·README. Node만으로 비Git 실행. PASS | + +8개 추가 Key: green-dark-3, brown-light-3, brown-dark-2, brown-dark-3, orange-light-3, orange-light-2, orange-light-1, orange-dark-3 (`color.palette.*`). 기존 73개 입력(71색+radius 2개)을 보존한 81개 입력(79색+radius 2개)이다. Brown Deep #221D13과 새 Dark 2 #2F281C / Dark 3 #1D1810을 alias하지 않는다. Orange Mid #CE5600은 Button 배경 30% stop이며 브랜드 단계로 추정하지 않는다. 기존 Button 계열 Key·값·소비처를 유지한다. + +## 실제 Storybook 검증 + +| 검사 | 결과 | +|---|---| +| COLOR | 390/768/1280/1440px 모두 8개 슬롯×3. 확정 HEX와 computed background 일치. Orange Dark 1/2는 변수·색 미지정. 가로 칩 목록 내부 스크롤만 있으며 문서 가로 넘침 없음. 최종 static 렌더도 확인. | +| GNB_SIDEBAR | Green/Brown의 Border 1px solid rgb(222,219,210), token #dedbd2, LNB·Main 배경 rgb(245,240,229). 1280/1440px Sidebar 272px, 390/768px 숨김 유지. 문서 가로 넘침 없음. 최종 static도 동일. | +| DATA_TABLE_DESKTOP | 1280/1440px 기본·추가 조건 form과 열 버튼 y 좌표 동일. 1440px 기본 1241/1241, 추가 2221/2221. 열 버튼 right=도구 행 right. 검색 gap 8px, 가운데/왼쪽 정렬, Solid Green 조회/Outline Gray 초기화 유지. | +| DATA_TABLE_MOBILE | 390px 기존 조건/검색/버튼 줄바꿈·버튼 행 우측 정렬. 768px 기본 검색은 한 행을 유지하고 열 버튼만 다음 행, 추가 조건은 공간 부족으로 줄바꿈. 4개 폭 모두 문서 scrollWidth=clientWidth. | +| DATA_TABLE_BEHAVIOR | 김민수 draft 입력 시 47개 유지, 조회 시 4개, 초기화 시 47개/빈 입력 복원. 열 메뉴 열림, Escape 후 trigger focus 복귀. | +| SEPARATOR | 최종 static Story 안내·MD 일치. Horizontal 제목/설명 묶음 뒤 Separator 정확히 1개. 스타일 소스 미변경. | +| RUNTIME_CONTRACT | 추가 계약을 포함해 146 checks / 58 rendered parts PASS, KEYBOARD_FOCUS PASS. Default parity, 모든 공개 입력, 기존 Component·독립 역할·partial/invalid/cleanup/scoped/geometry 확인. | +| JSON_LOADER / DESIGNER | 최종 static Loader 127 checks PASS, Designer 96 checks PASS. 기존 17개 역할 UI·Default/partial·Import/Export·reset·geometry 보존. | + +개발 서버에서 dist 재생성 중 일시적인 CSS HMR 오류가 있었으나, 최종 빌드 성공 후 정적 Color/GNB/Data Table/Separator/Loader/Designer 화면의 console error는 0건이다. 실패/시도 기록을 최종 PASS와 혼동하지 않는다. + +## 기술 검증과 재사용 + +- TYPECHECK / UI_BUILD: 최종 root·UI·CENTER typecheck 및 workspace build PASS. 색상 계약 추가 이후 실행했고 동일 조건으로 다시 실행하지 않았다. +- STORYBOOK_BUILD: 최종 PASS. 기존 bundler 성능/큰 chunk 경고 유지. 앞선 build 이후 CSS·계약이 변경되어 최종 산출물에 필요한 한 번만 추가 build했다. +- TOKEN_COMPATIBILITY: 시작 기준의 모든 기존 JSON 값·Key, contract Property/type, 기존 Primitive/default CSS 선언 동일. 이전 전체 JSON·서비스 partial JSON·empty theme 유효. Needs Review인 orange-dark-1/2는 공개 입력 거부. +- PUBLIC_EXPORT: UI 255 / Theme 8, 공개 export 소스·package exports 미변경. Runtime Token union은 승인된 팔레트 8개만 가산. +- CSS_IMPORTS: 추적 CSS의 상대경로 import 57개, 누락 0. 외부/패키지 import까지 합한 과거 수량과 혼용하지 않는다. +- STORYBOOK_ID: 최종 index 292개. Story title/export 이름 추가·삭제 없음. +- 문서 검사 회귀: 4/4 PASS. Git/비Git 동등 범위, 한글·공백 경로/중복 heading/explicit id, 코드블록 제외, 실제 누락/heading/_local 오류, nested ignore/negation, 손상 Git metadata exit 2 확인. +- GITLESS_DOC_CHECK: 현재 추적·신규 소스 598개만 복사한 별도 `_local/v2-gitless`에서 `.git` 없이 실행, Git 모드와 MD·링크 수 및 오류 목록 동일. 최종 104 MD / 463 internal links / 오류 0. 이 보고서 추가 전의 103 MD / 460 links와 구분한다. +- RELATED_GUARD: 수정 소비 영역 PARTIAL / STORYBOOK 검사. 원본 status/staticStatus NOT VERIFIED, FAIL 0. styleEvidence STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT NOT VERIFIED / FINAL NOT VERIFIED. 결합 styleVerification은 생성하지 않았다. 실제 브라우저 PASS는 별도 근거이며 정적 원본을 덮어쓰지 않는다. +- REUSED PASS: 이전 Guard100·Component15 중 코드/fixtures·영향이 변하지 않은 정책/기능 검사만 재사용. 계약 소비자는 위 Runtime/Loader/Designer로 새로 검증. 과거 전체 Theme/Story/AI 생성 근거를 최신 전체 PASS로 승격하지 않는다. +- NOT VERIFIED: 모든 Story×너비 전수, 실기기 터치·SR 낭독, 신규 Theme CDN 배포. 이번 요청의 변경 영역 외 전체 생산 앱/AI 생성 회귀는 반복 생략. + +## 기존 ZIP 별도 확인 + +읽기 전용 `D:/디자인시스템/HMDesign-main.zip`에는 검사기 자체가 없다. `_local/v2-existing-zip`에 압축을 해제하고 새 검사기만 복사하여 Node로 실행했다. Git 실행 실패 없이 84 MD / 307 internal links를 검사했고, 실제 과거 링크 문제 3건을 정상 검출했다: README와 shadcn 가이드의 삭제된 AGENTS heading 2건, runtime-theme-report의 제외된 _local 증거 1건. 이 과거 ZIP을 수정하거나 새 ZIP을 생성하지 않았다. 현재 공식 소스 사본의 오류 0과 구분한다. + +## 보존과 남은 확인 + +D-01/D-02, Tabs 상단 White 80%·Overflow, Green LNB/목차 8%, Button/Gray/Dialog, Table 20%, Progress/Accordion, Sheet/Drawer, 모바일 Navigation, 기존 Component API, Guard 정책은 시작 기준 소스와 비교해 보존했다. 추가 색상 외 기존 Theme 값·Typography·Spacing·geometry는 동일하다. ui_viz 참조 파일은 조사 전후 SHA-256 동일, 쓰기 없음. GitHub 원본 폴더/remote에는 쓰기·Push 없음. 백업 브랜치 및 Gitea 기존 이력 유지, Force Push·Reset·Clean 없음. + +Orange Dark 1 #BC4C00 / Dark 2 #883700은 ui_viz Color §4.1·inventory·Primitive가 Needs Review다. 승인 확인 전 공개 palette에 넣지 않는다. 기존 orange-border #BC4C00의 Button 범위 승인과 별개다. 현재 안전하게 완료한 나머지 수정은 일반 Commit·Push 대상이다. + +## 수정 파일 범위 + +- Color: `src/foundation/color.stories.tsx`, `themes/default.json`, `packages/ui/src/theme/contract.ts`, `scripts/theme-templates/{primitives,default}.css` 및 생성 `packages/ui/src/theme/{primitives.css,default.css,default-values.generated.ts}`. +- LNB: `packages/ui/src/theme/gnb.css`, `docs/patterns/gnb.md`. +- Data Table: `src/patterns/data-table.stories.tsx`, `src/list-screen.css`, `docs/patterns/data-table.md`(실제 조회 적용 안내도 정정). +- Separator: `docs/components/separator.md`. +- 문서 검사: `scripts/check-doc-links.mjs`, `scripts/check-doc-links.test.mjs`, `README.md`. +- 계약 정합성: `src/theme/runtime-loader.stories.tsx`의 전체 컬러 수 assertion을 실제 contract에서 계산; `docs/guidelines/{brand-color-steps,runtime-theme,runtime-theme-contract,designer-theme-guide,theme-delivery,개발-적용-가이드,프로젝트-지침}.md`의 현행 수량/설명. 과거 날짜별 검증 보고와 baseline snapshot은 보존. +- 본 보고서는 검증 근거 및 보류 범위를 기록한다. 로컬 `_local` 검사 사본·로그·이미지는 Stage/Commit하지 않는다. +- `AGENTS.md`에는 사용자의 별도 후속 지시인 공식 작업 경로·시작/종료 Git 확인·검증 후 일반 Commit/Push·원격 변경 시 중단 정책을 기록했다. 기존 화면 구현 필수 규칙은 보존했다. diff --git a/packages/ui/src/theme/contract.ts b/packages/ui/src/theme/contract.ts index 22a7c89..1b38e9b 100644 --- a/packages/ui/src/theme/contract.ts +++ b/packages/ui/src/theme/contract.ts @@ -75,6 +75,15 @@ export const runtimeThemeContract = { "color.palette.gray": { property: "--hm-sem-palette-gray", defaultValue: runtimeThemeDefaults["--hm-sem-palette-gray"], type: "color" }, "color.palette.gray-dark": { property: "--hm-sem-palette-gray-dark", defaultValue: runtimeThemeDefaults["--hm-sem-palette-gray-dark"], type: "color" }, "color.palette.indicator": { property: "--hm-sem-palette-indicator", defaultValue: runtimeThemeDefaults["--hm-sem-palette-indicator"], type: "color" }, +// Confirmed ui_viz brand stages; legacy keys and component mappings remain unchanged. + "color.palette.green-dark-3": { property: "--hm-sem-palette-green-dark-3", defaultValue: runtimeThemeDefaults["--hm-sem-palette-green-dark-3"], type: "color" }, + "color.palette.brown-light-3": { property: "--hm-sem-palette-brown-light-3", defaultValue: runtimeThemeDefaults["--hm-sem-palette-brown-light-3"], type: "color" }, + "color.palette.brown-dark-2": { property: "--hm-sem-palette-brown-dark-2", defaultValue: runtimeThemeDefaults["--hm-sem-palette-brown-dark-2"], type: "color" }, + "color.palette.brown-dark-3": { property: "--hm-sem-palette-brown-dark-3", defaultValue: runtimeThemeDefaults["--hm-sem-palette-brown-dark-3"], type: "color" }, + "color.palette.orange-light-3": { property: "--hm-sem-palette-orange-light-3", defaultValue: runtimeThemeDefaults["--hm-sem-palette-orange-light-3"], type: "color" }, + "color.palette.orange-light-2": { property: "--hm-sem-palette-orange-light-2", defaultValue: runtimeThemeDefaults["--hm-sem-palette-orange-light-2"], type: "color" }, + "color.palette.orange-light-1": { property: "--hm-sem-palette-orange-light-1", defaultValue: runtimeThemeDefaults["--hm-sem-palette-orange-light-1"], type: "color" }, + "color.palette.orange-dark-3": { property: "--hm-sem-palette-orange-dark-3", defaultValue: runtimeThemeDefaults["--hm-sem-palette-orange-dark-3"], type: "color" }, } as const export type RuntimeThemeToken = keyof typeof runtimeThemeContract diff --git a/packages/ui/src/theme/default-values.generated.ts b/packages/ui/src/theme/default-values.generated.ts index c36e9a8..58b32f8 100644 --- a/packages/ui/src/theme/default-values.generated.ts +++ b/packages/ui/src/theme/default-values.generated.ts @@ -73,5 +73,13 @@ export const runtimeThemeDefaults = { "--hm-sem-palette-orange-border-deep": "#5e2902", "--hm-sem-palette-gray": "#767676", "--hm-sem-palette-gray-dark": "#5a5a5a", - "--hm-sem-palette-indicator": "#2d2d2d" + "--hm-sem-palette-indicator": "#2d2d2d", + "--hm-sem-palette-green-dark-3": "#020a07", + "--hm-sem-palette-brown-light-3": "#c1a770", + "--hm-sem-palette-brown-dark-2": "#2f281c", + "--hm-sem-palette-brown-dark-3": "#1d1810", + "--hm-sem-palette-orange-light-3": "#ec8f53", + "--hm-sem-palette-orange-light-2": "#f87725", + "--hm-sem-palette-orange-light-1": "#eb5f00", + "--hm-sem-palette-orange-dark-3": "#4f2000" } as const diff --git a/packages/ui/src/theme/default.css b/packages/ui/src/theme/default.css index 5193dc1..8077fe4 100644 --- a/packages/ui/src/theme/default.css +++ b/packages/ui/src/theme/default.css @@ -201,3 +201,15 @@ --ring: var(--hm-sem-stroke-input-focus); --radius: var(--hm-radius-container); } + +/* Additive brand palette; does not change existing component aliases. */ +:root, [data-hm-theme] { + --hm-sem-palette-green-dark-3: var(--hm-green-dark-3); + --hm-sem-palette-brown-light-3: var(--hm-brown-light-3); + --hm-sem-palette-brown-dark-2: var(--hm-brown-dark-2); + --hm-sem-palette-brown-dark-3: var(--hm-brown-dark-3); + --hm-sem-palette-orange-light-3: var(--hm-orange-light-3); + --hm-sem-palette-orange-light-2: var(--hm-orange-light-2); + --hm-sem-palette-orange-light-1: var(--hm-orange-light-1); + --hm-sem-palette-orange-dark-3: var(--hm-orange-dark-3); +} diff --git a/packages/ui/src/theme/gnb.css b/packages/ui/src/theme/gnb.css index ed4ea3e..1857e7b 100644 --- a/packages/ui/src/theme/gnb.css +++ b/packages/ui/src/theme/gnb.css @@ -566,7 +566,7 @@ --hm-lnb-group-open-background: transparent; --hm-lnb-group-open-line: 0; --hm-lnb-focus-outline: 2px solid var(--hm-gnb-content-menu-color); - border-right: 1px solid var(--hm-sem-stroke-default); + border-right: 1px solid var(--hm-sem-stroke-input-default); } [data-hm-gnb-layout="sidebar"][data-hm-color="brown"] > [data-hm-gnb-body] > [data-hm-gnb-sidebar] { --hm-gnb-content-menu-color: var(--hm-sem-palette-brown-default); diff --git a/packages/ui/src/theme/primitives.css b/packages/ui/src/theme/primitives.css index 605db94..527bc1c 100644 --- a/packages/ui/src/theme/primitives.css +++ b/packages/ui/src/theme/primitives.css @@ -44,3 +44,15 @@ :root { --hm-yellow-accent: #ffc600; } + +/* Confirmed brand palette additions (ui_viz Color 4.1). */ +:root { + --hm-green-dark-3: #020a07; + --hm-brown-light-3: #c1a770; + --hm-brown-dark-2: #2f281c; + --hm-brown-dark-3: #1d1810; + --hm-orange-light-3: #ec8f53; + --hm-orange-light-2: #f87725; + --hm-orange-light-1: #eb5f00; + --hm-orange-dark-3: #4f2000; +} diff --git a/scripts/check-doc-links.mjs b/scripts/check-doc-links.mjs index 8f7b7b3..82a0b0f 100644 --- a/scripts/check-doc-links.mjs +++ b/scripts/check-doc-links.mjs @@ -3,7 +3,70 @@ import path from 'node:path' import { execFileSync } from 'node:child_process' const root = process.cwd() -const files = [...new Set(execFileSync('git', ['ls-files', '--cached', '--others', '--exclude-standard', '-z'], { encoding: 'utf8' }).split('\0'))].filter(file => file.endsWith('.md')) +// A portable checkout must not accidentally use a parent directory's Git repository. +let collectionMode +try { + fs.lstatSync(path.join(root, '.git')) + collectionMode = 'git' +} catch (error) { + if (error.code === 'ENOENT') collectionMode = 'filesystem' + else { + console.error(JSON.stringify({ error: 'Git metadata detection failed', detail: error.message })) + process.exit(2) + } +} +function ignoreRules(directory) { + const ignoreFile = path.join(directory, '.gitignore') + let source + try { source = fs.readFileSync(ignoreFile, 'utf8') } + catch (error) { if (error.code === 'ENOENT') return []; throw error } + return source.split(/\r?\n/).flatMap(line => { + if (!line || line.startsWith('#')) return [] + const negate = line.startsWith('!') + let pattern = (negate ? line.slice(1) : line).trimEnd() + const directoryOnly = pattern.endsWith('/') + if (directoryOnly) pattern = pattern.slice(0, -1) + const anchored = pattern.startsWith('/') || pattern.includes('/') + pattern = pattern.replace(/^\//, '') + // Repository ignore rules use literal paths, *, **, ? and negation. + let expression = '' + for (let i = 0; i < pattern.length; i++) { + const char = pattern[i] + if (char === '*' && pattern[i + 1] === '*') { + i++ + if (pattern[i + 1] === '/') { expression += '(?:.*/)?'; i++ } + else expression += '.*' + } else if (char === '*') expression += '[^/]*' + else if (char === '?') expression += '[^/]' + else expression += char.replace(/[\\^$+?.()|{}\[\]]/g, '\\$&') + } + return [{ directory, negate, directoryOnly, regex: new RegExp(`${anchored ? '^' : '(?:^|/)'}${expression}$`) }] + }) +} +function filesystemMarkdown(directory = root, inherited = []) { + const rules = [...inherited, ...ignoreRules(directory)] + return fs.readdirSync(directory, { withFileTypes: true }).flatMap(entry => { + if (entry.name === '.git') return [] + const absolute = path.join(directory, entry.name) + let ignored = false + for (const rule of rules) { + const relative = path.relative(rule.directory, absolute).replaceAll('\\', '/') + if ((!rule.directoryOnly || entry.isDirectory()) && rule.regex.test(relative)) ignored = !rule.negate + } + if (ignored) return [] + if (entry.isDirectory()) return filesystemMarkdown(absolute, rules) + return entry.isFile() && entry.name.endsWith('.md') ? [path.relative(root, absolute).replaceAll('\\', '/')] : [] + }) +} +let files +try { + files = collectionMode === 'git' + ? [...new Set(execFileSync('git', ['ls-files', '--cached', '--others', '--exclude-standard', '-z'], { cwd: root, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).split('\0'))].filter(file => file.endsWith('.md')) + : filesystemMarkdown().sort() +} catch (error) { + console.error(JSON.stringify({ collectionMode, error: 'Markdown collection failed', detail: error.message })) + process.exit(2) +} const stripCode = text => text.replace(/^```[^\n]*\n[\s\S]*?^```[^\n]*$/gm, '') const slug = text => text.toLowerCase().replace(/`/g, '').replace(/[^\p{Letter}\p{Number}\p{Mark}_\- ]/gu, '').replace(/ /g, '-') const anchors = file => { @@ -30,5 +93,5 @@ for (const file of files) for (const [, raw] of stripCode(fs.readFileSync(file, if (reason) problems.push({ file, href, reason }) } catch (error) { problems.push({ file, href, reason: error.message }) } } -console.log(JSON.stringify({ files: files.length, internalLinks, problems }, null, 2)) +console.log(JSON.stringify({ collectionMode, files: files.length, internalLinks, problems }, null, 2)) if (problems.length) process.exitCode = 1 diff --git a/scripts/check-doc-links.test.mjs b/scripts/check-doc-links.test.mjs new file mode 100644 index 0000000..a03df1a --- /dev/null +++ b/scripts/check-doc-links.test.mjs @@ -0,0 +1,76 @@ +import { test } from 'node:test' +import assert from 'node:assert/strict' +import fs from 'node:fs' +import path from 'node:path' +import os from 'node:os' +import { fileURLToPath } from 'node:url' +import { spawnSync, execFileSync } from 'node:child_process' + +const script = fileURLToPath(new URL('./check-doc-links.mjs', import.meta.url)) +function fixture(t, contents) { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'hm-doc-links-')) + t.after(() => { + const relative = path.relative(path.resolve(os.tmpdir()), path.resolve(root)) + assert.ok(relative.startsWith('hm-doc-links-') && !relative.includes(path.sep), 'cleanup must stay in its own temporary fixture') + fs.rmSync(root, { recursive: true, force: true }) + }) + for (const [name, source] of Object.entries(contents)) { + const target = path.join(root, name) + fs.mkdirSync(path.dirname(target), { recursive: true }) + fs.writeFileSync(target, source) + } + return root +} +function run(root) { + const result = spawnSync(process.execPath, [script], { cwd: root, encoding: 'utf8' }) + return { status: result.status, report: JSON.parse(result.stdout || result.stderr) } +} + +test('Git and filesystem modes preserve current ignore scope and link counts', t => { + const root = fixture(t, { + '.gitignore': 'node_modules/\n_local/\n**/dist-dashboard/\n*.log\n.env.*\n!.env.example\n', + 'README.md': '[한글](docs/a%20b.md#한글-제목) [asset](.env.example)\n```md\n[x](missing.md)\n```\n', + 'docs/a b.md': '# 한글 제목\n# 한글 제목\n[duplicate](#한글-제목-1) [id](#custom)\n\n', + '.env.example': '', + 'node_modules/package/bad.md': '[x](missing.md)', + '_local/bad.md': '[x](missing.md)', + 'app/dist-dashboard/bad.md': '[x](missing.md)', + }) + const portable = run(root) + assert.equal(portable.status, 0) + assert.equal(portable.report.collectionMode, 'filesystem') + assert.equal(portable.report.files, 2) + assert.equal(portable.report.internalLinks, 4) + execFileSync('git', ['init', '--quiet', root]) + execFileSync('git', ['add', '.'], { cwd: root }) + const repository = run(root) + assert.equal(repository.status, 0) + assert.equal(repository.report.collectionMode, 'git') + assert.deepEqual({ ...repository.report, collectionMode: 'filesystem' }, portable.report) +}) + +test('missing paths, headings and excluded local evidence return exit 1', t => { + const root = fixture(t, { + 'README.md': '[path](missing.md) [heading](doc.md#missing) [local](_local/proof.json)', + 'doc.md': '# Present', '_local/proof.json': '{}', + }) + const result = run(root) + assert.equal(result.status, 1) + assert.deepEqual(result.report.problems.map(problem => problem.reason), ['missing path', 'missing heading', 'local evidence is excluded from Git Clone']) +}) + +test('nested ignore rules and negation retain the intended Markdown files', t => { + const root = fixture(t, { + '.gitignore': '*.md\n!README.md\n', 'README.md': '# Readme', 'ignored.md': '[x](missing)', + 'docs/.gitignore': '!keep.md\n', 'docs/keep.md': '# Kept', 'docs/drop.md': '[x](missing)', + }) + assert.equal(run(root).report.files, 2) +}) + +test('broken Git metadata is an operational error, never a silent fallback', t => { + const root = fixture(t, { '.git': 'gitdir: missing-metadata', 'README.md': '# Readme' }) + const result = run(root) + assert.equal(result.status, 2) + assert.equal(result.report.collectionMode, 'git') + assert.equal(result.report.error, 'Markdown collection failed') +}) diff --git a/scripts/theme-templates/default.css b/scripts/theme-templates/default.css index 95f82c1..90b5d6d 100644 --- a/scripts/theme-templates/default.css +++ b/scripts/theme-templates/default.css @@ -200,3 +200,15 @@ --ring: var(--hm-sem-stroke-input-focus); --radius: var(--hm-radius-container); } + +/* Additive brand palette; does not change existing component aliases. */ +:root, [data-hm-theme] { + --hm-sem-palette-green-dark-3: {{color.palette.green-dark-3|var(--hm-green-dark-3)}}; + --hm-sem-palette-brown-light-3: {{color.palette.brown-light-3|var(--hm-brown-light-3)}}; + --hm-sem-palette-brown-dark-2: {{color.palette.brown-dark-2|var(--hm-brown-dark-2)}}; + --hm-sem-palette-brown-dark-3: {{color.palette.brown-dark-3|var(--hm-brown-dark-3)}}; + --hm-sem-palette-orange-light-3: {{color.palette.orange-light-3|var(--hm-orange-light-3)}}; + --hm-sem-palette-orange-light-2: {{color.palette.orange-light-2|var(--hm-orange-light-2)}}; + --hm-sem-palette-orange-light-1: {{color.palette.orange-light-1|var(--hm-orange-light-1)}}; + --hm-sem-palette-orange-dark-3: {{color.palette.orange-dark-3|var(--hm-orange-dark-3)}}; +} diff --git a/scripts/theme-templates/primitives.css b/scripts/theme-templates/primitives.css index 3ef8bd0..6cb7151 100644 --- a/scripts/theme-templates/primitives.css +++ b/scripts/theme-templates/primitives.css @@ -43,3 +43,15 @@ :root { --hm-yellow-accent: {{color.palette.yellow-accent}}; } + +/* Confirmed brand palette additions (ui_viz Color 4.1). */ +:root { + --hm-green-dark-3: {{color.palette.green-dark-3}}; + --hm-brown-light-3: {{color.palette.brown-light-3}}; + --hm-brown-dark-2: {{color.palette.brown-dark-2}}; + --hm-brown-dark-3: {{color.palette.brown-dark-3}}; + --hm-orange-light-3: {{color.palette.orange-light-3}}; + --hm-orange-light-2: {{color.palette.orange-light-2}}; + --hm-orange-light-1: {{color.palette.orange-light-1}}; + --hm-orange-dark-3: {{color.palette.orange-dark-3}}; +} diff --git a/src/foundation/color.stories.tsx b/src/foundation/color.stories.tsx index 68f7d4e..2222d5c 100644 --- a/src/foundation/color.stories.tsx +++ b/src/foundation/color.stories.tsx @@ -2,23 +2,30 @@ import type { Meta, StoryObj } from "@storybook/react-vite" import { runtimeThemeContract, type RuntimeThemeToken } from "@hm-design/ui/theme" import { ReferencePage, ReferenceSection } from "./reference" -// Existing palette tokens only; values come from the public default contract. +// ui_viz Color 4.1 confirmed stages only. Orange Dark 1/2 remain Needs Review. +const stages = ["light-4", "light-3", "light-2", "light-1", "default", "dark-1", "dark-2", "dark-3"] as const const palettes = [ - { name: "Green", prefix: "green", stages: ["light-4", "light-3", "light-2", "light-1", "default", "dark-1", "dark-2"] }, - { name: "Brown", prefix: "brown", stages: ["light-4", "light-2", "light-1", "default", "dark-1", "deep"] }, - { name: "Orange", prefix: "orange", stages: ["light-4", "light", "mid", "default", "dark", "deep"] }, + { name: "한맥그린", prefix: "green", confirmed: [...stages], legacy: [] }, + { name: "한맥브라운", prefix: "brown", confirmed: [...stages], legacy: ["deep"] }, + { name: "한맥오렌지", prefix: "orange", confirmed: ["light-4", "light-3", "light-2", "light-1", "default", "dark-3"], legacy: ["light", "mid", "dark", "deep"] }, ] +const stageLabel = (stage: string) => stage === "default" ? "Default · 기본색" : stage.replaceAll("-", " ").replace(/^./, letter => letter.toUpperCase()) +function ColorChip({ prefix, stage, confirmed = true }: { prefix: string; stage: string; confirmed?: boolean }) { + const token = `color.palette.${prefix}-${stage}` as RuntimeThemeToken + const definition = confirmed ? runtimeThemeContract[token] : undefined + return
+
+

{stageLabel(stage)}

{definition ? definition.defaultValue.toUpperCase() : "미확정"}

+
+} function ColorReferencePage() { - return - {palettes.map(palette => -
{palette.stages.map(stage => { - const token = `color.palette.${palette.prefix}-${stage}` as RuntimeThemeToken - const definition = runtimeThemeContract[token] - return
-
-

{stage === "default" ? "Default · 기본색" : stage.replaceAll("-", " ")}

{definition.defaultValue.toUpperCase()}

-
- })}
+ return + {palettes.map(palette => +
{stages.map(stage => )}
+ {palette.legacy.length > 0 && <> +

기존 버튼 그라데이션 색상 · 브랜드 단계와 별도 유지

+
{palette.legacy.map(stage => )}
+ }
)}
} diff --git a/src/list-screen.css b/src/list-screen.css index 9ea142c..a7cdb08 100644 --- a/src/list-screen.css +++ b/src/list-screen.css @@ -1,4 +1,9 @@ /* Opt-in list composition. FilterBar's general layout contract stays unchanged. */ +/* Data Table Story composition only: intrinsic search width decides when tools wrap. */ +.hm-data-table-tools { display: flex; flex-wrap: wrap; align-items: center; gap: var(--hm-filter-bar-gap); min-width: 0; } +.hm-data-table-search { flex: 1 0 max-content; min-width: 0; max-width: 100%; } +.hm-data-table-search [data-slot="filter-bar-search"] { width: var(--hm-filter-bar-search-width); max-width: 100%; } +.hm-data-table-view-options { flex: 0 0 auto; margin-left: auto; } .hm-list-search-basic, .hm-list-search-extended { gap: 8px; } .hm-list-search-basic { justify-content: center; } .hm-list-search-extended { justify-content: flex-start; } diff --git a/src/patterns/data-table.stories.tsx b/src/patterns/data-table.stories.tsx index f82e537..e6d4a1b 100644 --- a/src/patterns/data-table.stories.tsx +++ b/src/patterns/data-table.stories.tsx @@ -122,19 +122,21 @@ function WithFilterBar({ extended = true }: { extended?: boolean }) { 사용자 목록조건을 입력한 뒤 조회합니다.
-
{ event.preventDefault(); apply() }}> - - - {extended && <> - } - - - -
-
+
+
{ event.preventDefault(); apply() }}> + + + {extended && <> + } + + + +
+
+
@@ -159,8 +161,8 @@ export const All: Story = { - - + + ), } diff --git a/src/theme/runtime-loader.stories.tsx b/src/theme/runtime-loader.stories.tsx index 2b639be..cc017f4 100644 --- a/src/theme/runtime-loader.stories.tsx +++ b/src/theme/runtime-loader.stories.tsx @@ -39,7 +39,7 @@ async function verifyLoader(container: HTMLElement) { const initial = JSON.stringify(geometry()) const origin = performance.timeOrigin const defaults = await fetchRuntimeTheme("/themes/default.json", { cache: "no-store" }) - assert(Object.keys(defaults.tokens).length === 71, "Default JSON exposes 71 color contract entries") + assert(Object.keys(defaults.tokens).length === Object.values(runtimeThemeContract).filter(token => token.type === "color").length, "Default JSON exposes every public color contract entry") for (const [key, value] of Object.entries(defaults.tokens)) assert(defaultRuntimeTheme.tokens[key as keyof typeof runtimeThemeContract] === value, `${key}: JSON equals generated package fallback`) assert(await loadRuntimeTheme(root, "/themes/default.json", { request: { cache: "no-store" } }), "Default JSON uses official URL Loader") assert(JSON.stringify(geometry()) === initial, "Default JSON preserves component geometry") diff --git a/themes/default.json b/themes/default.json index b828221..bf16d6b 100644 --- a/themes/default.json +++ b/themes/default.json @@ -71,6 +71,14 @@ "color.palette.orange-border-deep": "#5e2902", "color.palette.gray": "#767676", "color.palette.gray-dark": "#5a5a5a", - "color.palette.indicator": "#2d2d2d" + "color.palette.indicator": "#2d2d2d", + "color.palette.green-dark-3": "#020a07", + "color.palette.brown-light-3": "#c1a770", + "color.palette.brown-dark-2": "#2f281c", + "color.palette.brown-dark-3": "#1d1810", + "color.palette.orange-light-3": "#ec8f53", + "color.palette.orange-light-2": "#f87725", + "color.palette.orange-light-1": "#eb5f00", + "color.palette.orange-dark-3": "#4f2000" } }