From 1c4e0bb9a1ef4e5db391b93d7c874f78a0e6bcc5 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:09:03 +0900 Subject: [PATCH] feat: integrate latest HMDesign design system updates --- .gitignore | 10 + .storybook/main.ts | 2 +- .storybook/manager.css | 6 +- .storybook/preview.tsx | 2 +- .storybook/qa/STORY-INVENTORY.md | 60 +- .storybook/qa/generate-inventory.mjs | 6 +- .storybook/qa/story-inventory.json | 121 +- AGENTS.md | 17 +- CLAUDE.md | 2 + README.md | 59 +- docs/components/accordion.md | 8 +- docs/components/alert-dialog.md | 10 +- docs/components/badge.md | 2 + docs/components/button.md | 28 +- docs/components/card.md | 17 +- docs/components/checkbox.md | 2 + docs/components/combobox.md | 14 +- docs/components/date-picker.md | 4 +- docs/components/dialog.md | 6 +- docs/components/drawer.md | 4 +- docs/components/label.md | 26 + docs/components/menubar.md | 2 + docs/components/progress.md | 2 + docs/components/sheet.md | 2 + docs/components/table.md | 12 +- docs/components/tabs.md | 86 +- docs/components/toggle-group.md | 2 + docs/guidelines/ai-generation-guard.md | 205 +- .../ai-generation-improvement-2026-10-10.md | 96 + .../ai-generation-stabilization-2026-10-10.md | 71 + docs/guidelines/ai-implementation-details.md | 10 +- docs/guidelines/ai-screen-contract.md | 54 + .../guidelines/component-pattern-reference.md | 12 +- docs/guidelines/designer-theme-guide.md | 10 +- ...-source-transfer-preparation-2026-10-10.md | 70 + ...-source-transfer-preparation-2026-10-11.md | 144 ++ docs/guidelines/guard-accuracy-2026-10-10.md | 139 ++ docs/guidelines/navigation-single-mode.md | 4 + .../publisher-additional-review-2026-10-10.md | 71 + .../publisher-distribution-guide.md | 60 + .../publisher-integration-2026-10-09.md | 336 ++++ .../publisher-stabilization-2026-10-10.md | 127 ++ .../publisher-validation-2026-10-10.md | 98 + docs/guidelines/runtime-portability.md | 6 +- docs/guidelines/runtime-theme-contract.md | 4 +- docs/guidelines/runtime-theme-report.md | 2 +- docs/guidelines/runtime-theme.md | 2 +- docs/guidelines/shadcn-기반-구현-원칙.md | 2 +- docs/guidelines/개발-적용-가이드.md | 32 +- docs/guidelines/프로젝트-지침.md | 2 +- docs/patterns/dashboard-layout.md | 4 + docs/patterns/data-table.md | 20 +- docs/patterns/filter-bar.md | 33 +- docs/patterns/gnb.md | 8 +- docs/patterns/lnb.md | 22 +- docs/patterns/page-header.md | 16 +- .../accessibility-residual-2026-10-10.md | 78 + .../ai-quality-improvement-2026-10-10.json | 1671 +++++++++++++++++ .../ai-quality-improvement-2026-10-10.md | 144 ++ .../gitea-source-inventory-2026-10-11.json | 616 ++++++ ...ard-css-intent-dynamic-token-2026-10-10.md | 84 + .../pre-gitea-finalization-2026-10-11.md | 78 + .../publisher-a11y-guard-2026-10-10.md | 56 + examples/ai-quality/01/index.html | 1 + examples/ai-quality/01/main.tsx | 6 + examples/ai-quality/01/requirements.json | 22 + examples/ai-quality/02/index.html | 1 + examples/ai-quality/02/main.tsx | 6 + examples/ai-quality/02/requirements.json | 21 + examples/ai-quality/03/index.html | 1 + examples/ai-quality/03/main.tsx | 6 + examples/ai-quality/03/requirements.json | 25 + examples/ai-quality/04/index.html | 1 + examples/ai-quality/04/main.tsx | 6 + examples/ai-quality/04/requirements.json | 24 + examples/ai-quality/05/index.html | 1 + examples/ai-quality/05/main.tsx | 6 + examples/ai-quality/05/requirements.json | 18 + examples/ai-quality/06/index.html | 1 + examples/ai-quality/06/main.tsx | 6 + examples/ai-quality/06/requirements.json | 18 + examples/ai-quality/07/index.html | 1 + examples/ai-quality/07/main.tsx | 5 + examples/ai-quality/07/requirements.json | 18 + examples/ai-quality/08/index.html | 1 + examples/ai-quality/08/main.tsx | 5 + examples/ai-quality/08/requirements.json | 19 + examples/ai-quality/09/index.html | 1 + examples/ai-quality/09/main.tsx | 6 + examples/ai-quality/09/requirements.json | 20 + examples/ai-quality/10/index.html | 1 + examples/ai-quality/10/main.tsx | 6 + examples/ai-quality/10/requirements.json | 21 + examples/ai-quality/README.md | 32 + examples/ai-quality/shared/Dashboard.tsx | 14 + examples/ai-quality/shared/GNBFrame.tsx | 6 + examples/ai-quality/shared/Heading.tsx | 5 + examples/ai-quality/shared/LNBFrame.tsx | 6 + .../ai-quality/shared/OverlayWorkflow.tsx | 8 + examples/ai-quality/shared/Permissions.tsx | 14 + examples/ai-quality/shared/RequestForm.tsx | 18 + examples/ai-quality/shared/SearchList.tsx | 25 + examples/ai-quality/shared/StateWorkflow.tsx | 12 + examples/ai-quality/shared/hm-theme.css | 12 + examples/ai-quality/shared/index.css | 2 + examples/ai-quality/tsconfig.json | 4 + examples/ai-quality/vite.config.ts | 6 + examples/publisher-list/README.md | 22 + examples/publisher-list/hm-theme.css | 1 + examples/publisher-list/index.css | 5 + examples/publisher-list/index.html | 2 + examples/publisher-list/main.tsx | 32 + examples/publisher-list/tsconfig.json | 5 + examples/publisher-list/vite.config.ts | 5 + index.html | 3 +- package-lock.json | 3 +- package.json | 4 + .../components/CenterSpatialInformation.tsx | 4 +- packages/ui/package.json | 3 +- packages/ui/src/components/ui/checkbox.tsx | 7 +- packages/ui/src/components/ui/combobox.tsx | 16 +- packages/ui/src/components/ui/date-picker.tsx | 8 + packages/ui/src/components/ui/menubar.tsx | 5 +- packages/ui/src/components/ui/progress.tsx | 17 +- .../ui/src/components/ui/toggle-group.tsx | 3 +- packages/ui/src/index.ts | 2 +- .../ui/src/patterns/data-table/data-table.tsx | 5 +- .../ui/src/patterns/filter-bar/filter-bar.tsx | 22 +- packages/ui/src/patterns/gnb/gnb.tsx | 6 +- packages/ui/src/patterns/lnb/lnb.tsx | 4 +- packages/ui/src/theme/accordion.css | 2 +- packages/ui/src/theme/button.css | 6 +- packages/ui/src/theme/combobox.css | 3 +- packages/ui/src/theme/dashboard-layout.css | 1 + packages/ui/src/theme/date-picker.css | 3 +- packages/ui/src/theme/drawer.css | 8 +- packages/ui/src/theme/filter-bar.css | 6 + packages/ui/src/theme/gnb.css | 28 +- packages/ui/src/theme/lnb.css | 4 +- packages/ui/src/theme/progress.css | 4 + packages/ui/src/theme/runtime.ts | 7 +- packages/ui/src/theme/sheet.css | 6 + packages/ui/src/theme/table.css | 6 +- packages/ui/src/theme/tabs.css | 4 +- scripts/accessibility-regression.test.mjs | 41 + scripts/ai-generation-guard.mjs | 134 +- scripts/ai-generation-guard.test.mjs | 647 +++++++ scripts/audit-quality-contracts.mjs | 59 + scripts/build-package.mjs | 4 +- scripts/check-doc-links.mjs | 34 + scripts/create-publisher-distribution.mjs | 27 + scripts/guard-evidence.mjs | 85 + scripts/guard-evidence.test.mjs | 146 ++ scripts/guard-navigation-analysis.mjs | 230 +++ scripts/guard-requirements.mjs | 74 + scripts/guard-requirements.test.mjs | 29 + scripts/guard-style-analysis.mjs | 138 ++ scripts/guard-style-evidence.mjs | 46 + scripts/guard-theme-analysis.mjs | 131 ++ scripts/portable-preview/preview-server.cjs | 10 +- scripts/portable-preview/start-preview.bat | 17 +- scripts/prepare-quality-experiments.mjs | 24 + scripts/public-theme-tokens.mjs | 8 + scripts/publisher-components.test.mjs | 109 ++ scripts/serve-quality-reference.mjs | 18 + scripts/summarize-quality-evidence.mjs | 34 + scripts/theme-verification.test.mjs | 29 + scripts/verify-accessibility-runtime.mjs | 88 + scripts/verify-quality-reference.mjs | 84 + scripts/verify-quality-regression.mjs | 44 + scripts/verify-quality-runtime.mjs | 140 ++ src/App.tsx | 101 +- src/components/alert-dialog.stories.tsx | 6 +- src/components/button.stories.tsx | 10 +- src/components/combobox.stories.tsx | 2 +- src/components/dialog.stories.tsx | 10 +- src/components/drawer.stories.tsx | 12 +- src/components/label.stories.tsx | 10 +- .../publisher-regression.stories.tsx | 37 + src/components/select.stories.tsx | 6 +- src/components/separator.stories.tsx | 4 +- src/components/sheet.stories.tsx | 2 + src/components/story-board.tsx | 6 +- src/components/tabs.stories.tsx | 82 +- src/components/toggle.stories.tsx | 11 +- src/exports/recruit/index.html | 12 + src/exports/recruit/main.tsx | 17 + src/foundation/color.stories.tsx | 39 +- src/foundation/foundation.css | 4 + src/foundation/typography.stories.tsx | 2 +- src/index.css | 4 +- src/list-screen.css | 12 + src/main.tsx | 4 +- src/patterns/data-table.stories.tsx | 101 +- src/patterns/filter-bar.stories.tsx | 23 +- src/patterns/gnb.stories.tsx | 12 +- src/patterns/lnb.stories.tsx | 5 +- src/patterns/page-header.stories.tsx | 32 +- src/recruit/recruit-data.ts | 80 + src/recruit/recruit-screen.tsx | 265 +++ src/theme/designer-theme.stories.tsx | 43 +- src/theme/runtime-loader.stories.tsx | 13 +- src/theme/runtime-theme.stories.tsx | 39 +- src/theme/verification-state.tsx | 14 + src/work-dashboard/WorkDashboard.tsx | 205 ++ src/work-dashboard/data.ts | 53 + src/work-dashboard/work-dashboard.css | 50 + tailwind.css | 2 + vite.recruit-export.config.ts | 28 + 209 files changed, 8845 insertions(+), 552 deletions(-) create mode 100644 docs/guidelines/ai-generation-improvement-2026-10-10.md create mode 100644 docs/guidelines/ai-generation-stabilization-2026-10-10.md create mode 100644 docs/guidelines/ai-screen-contract.md create mode 100644 docs/guidelines/gitea-source-transfer-preparation-2026-10-10.md create mode 100644 docs/guidelines/gitea-source-transfer-preparation-2026-10-11.md create mode 100644 docs/guidelines/guard-accuracy-2026-10-10.md create mode 100644 docs/guidelines/publisher-additional-review-2026-10-10.md create mode 100644 docs/guidelines/publisher-distribution-guide.md create mode 100644 docs/guidelines/publisher-integration-2026-10-09.md create mode 100644 docs/guidelines/publisher-stabilization-2026-10-10.md create mode 100644 docs/guidelines/publisher-validation-2026-10-10.md create mode 100644 docs/reports/accessibility-residual-2026-10-10.md create mode 100644 docs/reports/ai-quality-improvement-2026-10-10.json create mode 100644 docs/reports/ai-quality-improvement-2026-10-10.md create mode 100644 docs/reports/gitea-source-inventory-2026-10-11.json create mode 100644 docs/reports/guard-css-intent-dynamic-token-2026-10-10.md create mode 100644 docs/reports/pre-gitea-finalization-2026-10-11.md create mode 100644 docs/reports/publisher-a11y-guard-2026-10-10.md create mode 100644 examples/ai-quality/01/index.html create mode 100644 examples/ai-quality/01/main.tsx create mode 100644 examples/ai-quality/01/requirements.json create mode 100644 examples/ai-quality/02/index.html create mode 100644 examples/ai-quality/02/main.tsx create mode 100644 examples/ai-quality/02/requirements.json create mode 100644 examples/ai-quality/03/index.html create mode 100644 examples/ai-quality/03/main.tsx create mode 100644 examples/ai-quality/03/requirements.json create mode 100644 examples/ai-quality/04/index.html create mode 100644 examples/ai-quality/04/main.tsx create mode 100644 examples/ai-quality/04/requirements.json create mode 100644 examples/ai-quality/05/index.html create mode 100644 examples/ai-quality/05/main.tsx create mode 100644 examples/ai-quality/05/requirements.json create mode 100644 examples/ai-quality/06/index.html create mode 100644 examples/ai-quality/06/main.tsx create mode 100644 examples/ai-quality/06/requirements.json create mode 100644 examples/ai-quality/07/index.html create mode 100644 examples/ai-quality/07/main.tsx create mode 100644 examples/ai-quality/07/requirements.json create mode 100644 examples/ai-quality/08/index.html create mode 100644 examples/ai-quality/08/main.tsx create mode 100644 examples/ai-quality/08/requirements.json create mode 100644 examples/ai-quality/09/index.html create mode 100644 examples/ai-quality/09/main.tsx create mode 100644 examples/ai-quality/09/requirements.json create mode 100644 examples/ai-quality/10/index.html create mode 100644 examples/ai-quality/10/main.tsx create mode 100644 examples/ai-quality/10/requirements.json create mode 100644 examples/ai-quality/README.md create mode 100644 examples/ai-quality/shared/Dashboard.tsx create mode 100644 examples/ai-quality/shared/GNBFrame.tsx create mode 100644 examples/ai-quality/shared/Heading.tsx create mode 100644 examples/ai-quality/shared/LNBFrame.tsx create mode 100644 examples/ai-quality/shared/OverlayWorkflow.tsx create mode 100644 examples/ai-quality/shared/Permissions.tsx create mode 100644 examples/ai-quality/shared/RequestForm.tsx create mode 100644 examples/ai-quality/shared/SearchList.tsx create mode 100644 examples/ai-quality/shared/StateWorkflow.tsx create mode 100644 examples/ai-quality/shared/hm-theme.css create mode 100644 examples/ai-quality/shared/index.css create mode 100644 examples/ai-quality/tsconfig.json create mode 100644 examples/ai-quality/vite.config.ts create mode 100644 examples/publisher-list/README.md create mode 100644 examples/publisher-list/hm-theme.css create mode 100644 examples/publisher-list/index.css create mode 100644 examples/publisher-list/index.html create mode 100644 examples/publisher-list/main.tsx create mode 100644 examples/publisher-list/tsconfig.json create mode 100644 examples/publisher-list/vite.config.ts create mode 100644 scripts/accessibility-regression.test.mjs create mode 100644 scripts/audit-quality-contracts.mjs create mode 100644 scripts/check-doc-links.mjs create mode 100644 scripts/create-publisher-distribution.mjs create mode 100644 scripts/guard-evidence.mjs create mode 100644 scripts/guard-evidence.test.mjs create mode 100644 scripts/guard-navigation-analysis.mjs create mode 100644 scripts/guard-requirements.mjs create mode 100644 scripts/guard-requirements.test.mjs create mode 100644 scripts/guard-style-analysis.mjs create mode 100644 scripts/guard-style-evidence.mjs create mode 100644 scripts/guard-theme-analysis.mjs create mode 100644 scripts/prepare-quality-experiments.mjs create mode 100644 scripts/public-theme-tokens.mjs create mode 100644 scripts/publisher-components.test.mjs create mode 100644 scripts/serve-quality-reference.mjs create mode 100644 scripts/summarize-quality-evidence.mjs create mode 100644 scripts/theme-verification.test.mjs create mode 100644 scripts/verify-accessibility-runtime.mjs create mode 100644 scripts/verify-quality-reference.mjs create mode 100644 scripts/verify-quality-regression.mjs create mode 100644 scripts/verify-quality-runtime.mjs create mode 100644 src/components/publisher-regression.stories.tsx create mode 100644 src/exports/recruit/index.html create mode 100644 src/exports/recruit/main.tsx create mode 100644 src/list-screen.css create mode 100644 src/recruit/recruit-data.ts create mode 100644 src/recruit/recruit-screen.tsx create mode 100644 src/theme/verification-state.tsx create mode 100644 src/work-dashboard/WorkDashboard.tsx create mode 100644 src/work-dashboard/data.ts create mode 100644 src/work-dashboard/work-dashboard.css create mode 100644 tailwind.css create mode 100644 vite.recruit-export.config.ts diff --git a/.gitignore b/.gitignore index ad49a10..fdaa671 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,13 @@ theme-dist/ _local/ .claude/settings.local.json + +# Generated recruitment exports and local test output. +**/dist-recruit/ +coverage/ +test-results/ +playwright-report/ + +# Local dashboard verification artifacts (preserved in the full backup). +src/work-dashboard/guard-report.json +src/work-dashboard/dashboard-preview.jpg diff --git a/.storybook/main.ts b/.storybook/main.ts index c46ff1b..0b2c1b2 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -15,7 +15,7 @@ const config: StorybookConfig = { features: { sidebarOnboardingChecklist: false, changeDetection: false }, // Verification Stories (the same sections as a component's representative Story) stay out of the sidebar by default. // They keep their Story IDs and stay reachable by direct URL and the sidebar tag filter. - tags: { verification: { defaultFilterSelection: "exclude" } }, + tags: { verification: { defaultFilterSelection: "exclude" }, internal: { defaultFilterSelection: "exclude" } }, // Storybook replaces vite.config's `server`, so watcher excludes go here. // _local/ (gitignored work copies, ~20k files) would otherwise make the Vite watcher stall the dev server. viteFinal: (config) => { diff --git a/.storybook/manager.css b/.storybook/manager.css index e0c1390..bde02b5 100644 --- a/.storybook/manager.css +++ b/.storybook/manager.css @@ -102,6 +102,8 @@ #storybook-sidebar-region [role='search'] > button:hover { background: rgba(0, 0, 0, .10); } +/* Storybook's named creation action only; search/filter remain available. */ +#storybook-sidebar-region button[aria-label='Create a new story'] { display: none; } #storybook-explorer-tree .sidebar-subheading { position: relative; @@ -141,8 +143,10 @@ background: transparent; color: var(--color-fg-on-green-primary); } +/* Manager-only expanded 2-depth branch face: Black 8%, independent of HM LNB CSS. + The separate Black 20% mapping remains reserved for the selected item's inset depth. */ #storybook-explorer-tree .sidebar-item[data-hm-group='true'] { - background: var(--overlay-black-20); + background: rgba(0, 0, 0, .08); box-shadow: inset 1px 0 var(--hm-green-light-2); } #storybook-explorer-tree .sidebar-item[data-hm-group-start='true'] { diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 53d0a83..cf27174 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -55,7 +55,7 @@ const preview: Preview = { ensurePseudoStates() // One Toaster for every Story, the same way an app mounts it once at its root. return ( - + diff --git a/.storybook/qa/STORY-INVENTORY.md b/.storybook/qa/STORY-INVENTORY.md index 60ed74f..1643bfd 100644 --- a/.storybook/qa/STORY-INVENTORY.md +++ b/.storybook/qa/STORY-INVENTORY.md @@ -1,21 +1,21 @@ -# Internal Story inventory (ZIP excluded) +# Track B Story inventory -Current references reconciled against the running Storybook index (288 entries). The historical Track B generator is not a recipient tool; this QA record is excluded from the test ZIP. +Generated from every `src/**/*.stories.ts(x)` using the installed Storybook CSF parser. Run `node .storybook/qa/generate-inventory.mjs` from Track B to refresh. This is a classification record. No Story IDs, exports or titles are renamed. HIDE uses the existing verification filter, not deletion. MERGE_CANDIDATE is advisory and can coexist with HIDE. Utility includes executable checks and compatibility/playground tools. No legacy or delete candidate is asserted without evidence. ## Summary - PRIMARY: 65 -- VERIFICATION: 210 -- UTILITY: 13 +- VERIFICATION: 212 +- UTILITY: 15 - LEGACY / DUPLICATE: 0 - KEEP: 65 -- HIDE: 223 +- HIDE: 227 - MERGE_CANDIDATE: 31 - DELETE_CANDIDATE: 0 -- Story files: 61 -- Stories: 288 +- Story files: 63 +- Stories: 292 - Hierarchy: Foundation → Components → Patterns → Center Graphic → Theme ## Full inventory @@ -24,13 +24,13 @@ This is a classification record. No Story IDs, exports or titles are renamed. HI Source: `src/center/center-facility.stories.tsx`; meta.tags: [] -- Tunnel — 터널; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. -- Bridge — 교량; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. -- Intersection — 교차로; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. -- Junction — 분기점; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. -- Interchange — 나들목; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. -- Roadside — 휴게소 · 졸음쉼터; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. -- Linked — 연계 시설물; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Tunnel — Tunnel; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Bridge — Bridge; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Intersection — Intersection; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Junction — Junction; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Interchange — Interchange; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Roadside — Roadside; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. +- Linked — Linked; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. - TunnelPlayground — 직접 테스트 (터널); tags: ["verification"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. ### Center Graphic/Information @@ -321,6 +321,12 @@ Source: `src/components/progress.stories.tsx`; meta.tags: [] - Full — Full; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - NonDefaultMax — Non Default Max; tags: ["verification","play-fn"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +### Verification/Publisher Regression + +Source: `src/components/publisher-regression.stories.tsx`; meta.tags: ["verification"] + +- ConsumersRegression — Consumers Regression; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. + ### Components/Form/Radio Group Source: `src/components/radio-group.stories.tsx`; meta.tags: [] @@ -447,6 +453,7 @@ Source: `src/components/tabs.stories.tsx`; meta.tags: [] - Overflow — Overflow; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - SecondaryOverflow — Secondary Overflow; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - LongLabel — Long Label; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Compatibility — Compatibility; tags: ["verification"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. ### Components/Form/Textarea @@ -514,13 +521,13 @@ Source: `src/foundation/typography.stories.tsx`; meta.tags: [] Source: `src/patterns/dashboard-layout.stories.tsx`; meta.tags: [] - All — Dashboard; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. -- WithSidebar — Dashboard Header + Sidebar; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. -- ThirdLevel — Third Level; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. -- SidebarOnly — Dashboard Sidebar; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. -- LongContent — Long Content; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- WithSidebar — VERIFICATION ONLY — 운영 사용 금지 · Dashboard Header + Sidebar; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- ThirdLevel — VERIFICATION ONLY — 운영 사용 금지 · Third Level; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- SidebarOnly — VERIFICATION ONLY — 운영 사용 금지 · Dashboard Sidebar; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LongContent — VERIFICATION ONLY — 운영 사용 금지 · Long Content; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - Loading — Loading; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - Empty — Empty; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. -- Framed — Framed; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Framed — VERIFICATION ONLY — 운영 사용 금지 · Framed; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. ### Patterns/Data/Data Table @@ -544,6 +551,7 @@ Source: `src/patterns/gnb.stories.tsx`; meta.tags: [] - All — GNB; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. - Default — Default; tags: ["verification"]; VERIFICATION; HIDE; MERGE_CANDIDATE. Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed. - Sticky — Sticky; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Sidebar — GNB Sidebar; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - NavigationMenuOpen — Navigation Menu Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - ShortDepthOpen — Short Depth Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - RightEdgeLongDepth — Right Edge Long Depth; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. @@ -578,7 +586,7 @@ Source: `src/patterns/lnb.stories.tsx`; meta.tags: [] - ThirdLevel — Third Level; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - ThirdLevelLongLabels — Third Level Long Labels; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - ThirdLevelScroll — Third Level Scroll; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. -- WithGNB — With GNB; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- WithGNB — VERIFICATION ONLY · GNB + LNB; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - StandaloneDesktop — LNB Standalone Desktop; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - LNBProfileMenuOpen — LNB Profile Menu Open; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. - StandaloneMobile — LNB Standalone Mobile; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. @@ -591,12 +599,18 @@ Source: `src/patterns/loading-screen.stories.tsx`; meta.tags: [] - All — Loading Screen; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. - Inline — Inline; tags: ["verification"]; VERIFICATION; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +### Patterns/Layout/Page Header + +Source: `src/patterns/page-header.stories.tsx`; meta.tags: [] + +- All — Page Header; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. + ### Theme/Designer Playground -Source: `src/theme/designer-theme.stories.tsx`; meta.tags: [] +Source: `src/theme/designer-theme.stories.tsx`; meta.tags: ["internal"] -- Editor — Designer Playground; tags: []; PRIMARY; KEEP. Representative story or distinct reference page. -- LiveVerification — Live Verification; tags: ["verification","play-fn"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- Editor — Designer Playground; tags: ["internal"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. +- LiveVerification — Live Verification; tags: ["internal","verification","play-fn"]; UTILITY; HIDE. Keep direct URL and tag-filter access; excluded from default sidebar. ### Theme/Runtime Contract/Loader diff --git a/.storybook/qa/generate-inventory.mjs b/.storybook/qa/generate-inventory.mjs index 8511d8c..b1c7c45 100644 --- a/.storybook/qa/generate-inventory.mjs +++ b/.storybook/qa/generate-inventory.mjs @@ -5,7 +5,7 @@ import { loadCsf } from 'storybook/internal/csf-tools' // Run from the Track B root: node .storybook/qa/generate-inventory.mjs // Only writes Storybook documentation; never renames or removes stories. const root = process.cwd() -if (path.basename(root) !== 'HMDesignSystem-storybook') throw new Error('Run only from Track B') +if (!fs.existsSync(path.join(root, '.storybook/main.ts'))) throw new Error('Run from the HMDesign repository root') const walk = dir => fs.readdirSync(dir, { withFileTypes: true }).flatMap(e => e.isDirectory() ? walk(path.join(dir, e.name)) : [path.join(dir, e.name)]) const files = walk(path.join(root, 'src')).filter(f => /\.stories\.tsx?$/.test(f)).sort() const inventory = files.map(file => { @@ -17,9 +17,9 @@ const inventory = files.map(file => { const storyTags = story.tags ?? [] const effectiveTags = [...new Set([...metaTags, ...storyTags])] const utility = /Playground|Compatibility/.test(exportName) || /Verification$/.test(exportName) || exportName === 'Verification' - const category = utility ? 'UTILITY' : effectiveTags.includes('verification') ? 'VERIFICATION' : 'PRIMARY' + const category = utility || effectiveTags.includes('internal') ? 'UTILITY' : effectiveTags.includes('verification') ? 'VERIFICATION' : 'PRIMARY' const duplicateCandidate = effectiveTags.includes('verification') && /^(Default|Sizes|Roles|Variants|States|Basic|Selected|Disabled|Horizontal|Vertical)$/.test(exportName) - return { id: story.id, exportName, name: story.name, storyTags, effectiveTags, category, decision: effectiveTags.includes('verification') ? 'HIDE' : 'KEEP', candidate: duplicateCandidate ? 'MERGE_CANDIDATE' : null, reason: duplicateCandidate ? 'Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed.' : effectiveTags.includes('verification') ? 'Keep direct URL and tag-filter access; excluded from default sidebar.' : 'Representative story or distinct reference page.' } + return { id: story.id, exportName, name: story.name, storyTags, effectiveTags, category, decision: effectiveTags.some(tag => ['verification', 'internal'].includes(tag)) ? 'HIDE' : 'KEEP', candidate: duplicateCandidate ? 'MERGE_CANDIDATE' : null, reason: duplicateCandidate ? 'Representative board may cover the same specimen; keep direct QA until behavioral equivalence is reviewed.' : effectiveTags.some(tag => ['verification', 'internal'].includes(tag)) ? 'Keep direct URL and tag-filter access; excluded from default sidebar.' : 'Representative story or distinct reference page.' } }) return { file: path.relative(root, file).replaceAll('\\', '/'), title, metaTags, stories } }) diff --git a/.storybook/qa/story-inventory.json b/.storybook/qa/story-inventory.json index 0d28449..abc1cf3 100644 --- a/.storybook/qa/story-inventory.json +++ b/.storybook/qa/story-inventory.json @@ -1,16 +1,16 @@ { "summary": { - "storyFiles": 61, - "stories": 288, + "storyFiles": 63, + "stories": 292, "categories": { "PRIMARY": 65, - "VERIFICATION": 210, - "UTILITY": 13, + "VERIFICATION": 212, + "UTILITY": 15, "LEGACY / DUPLICATE": 0 }, "decisions": { "KEEP": 65, - "HIDE": 223, + "HIDE": 227, "MERGE_CANDIDATE": 31, "DELETE_CANDIDATE": 0 }, @@ -31,7 +31,7 @@ { "id": "center-graphic-facility--tunnel", "exportName": "Tunnel", - "name": "터널", + "name": "Tunnel", "storyTags": [], "effectiveTags": [], "category": "PRIMARY", @@ -42,7 +42,7 @@ { "id": "center-graphic-facility--bridge", "exportName": "Bridge", - "name": "교량", + "name": "Bridge", "storyTags": [], "effectiveTags": [], "category": "PRIMARY", @@ -53,7 +53,7 @@ { "id": "center-graphic-facility--intersection", "exportName": "Intersection", - "name": "교차로", + "name": "Intersection", "storyTags": [], "effectiveTags": [], "category": "PRIMARY", @@ -64,7 +64,7 @@ { "id": "center-graphic-facility--junction", "exportName": "Junction", - "name": "분기점", + "name": "Junction", "storyTags": [], "effectiveTags": [], "category": "PRIMARY", @@ -75,7 +75,7 @@ { "id": "center-graphic-facility--interchange", "exportName": "Interchange", - "name": "나들목", + "name": "Interchange", "storyTags": [], "effectiveTags": [], "category": "PRIMARY", @@ -86,7 +86,7 @@ { "id": "center-graphic-facility--roadside", "exportName": "Roadside", - "name": "휴게소 · 졸음쉼터", + "name": "Roadside", "storyTags": [], "effectiveTags": [], "category": "PRIMARY", @@ -97,7 +97,7 @@ { "id": "center-graphic-facility--linked", "exportName": "Linked", - "name": "연계 시설물", + "name": "Linked", "storyTags": [], "effectiveTags": [], "category": "PRIMARY", @@ -2158,6 +2158,28 @@ } ] }, + { + "file": "src/components/publisher-regression.stories.tsx", + "title": "Verification/Publisher Regression", + "metaTags": [ + "verification" + ], + "stories": [ + { + "id": "verification-publisher-regression--consumers-regression", + "exportName": "ConsumersRegression", + "name": "Consumers Regression", + "storyTags": [], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + } + ] + }, { "file": "src/components/radio-group.stories.tsx", "title": "Components/Form/Radio Group", @@ -3208,6 +3230,21 @@ "decision": "HIDE", "candidate": null, "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, + { + "id": "components-navigation-tabs--compatibility", + "exportName": "Compatibility", + "name": "Compatibility", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." } ] }, @@ -3649,7 +3686,7 @@ { "id": "patterns-layout-dashboard-layout--with-sidebar", "exportName": "WithSidebar", - "name": "Dashboard Header + Sidebar", + "name": "VERIFICATION ONLY — 운영 사용 금지 · Dashboard Header + Sidebar", "storyTags": [ "verification" ], @@ -3664,7 +3701,7 @@ { "id": "patterns-layout-dashboard-layout--third-level", "exportName": "ThirdLevel", - "name": "Third Level", + "name": "VERIFICATION ONLY — 운영 사용 금지 · Third Level", "storyTags": [ "verification" ], @@ -3679,7 +3716,7 @@ { "id": "patterns-layout-dashboard-layout--sidebar-only", "exportName": "SidebarOnly", - "name": "Dashboard Sidebar", + "name": "VERIFICATION ONLY — 운영 사용 금지 · Dashboard Sidebar", "storyTags": [ "verification" ], @@ -3694,7 +3731,7 @@ { "id": "patterns-layout-dashboard-layout--long-content", "exportName": "LongContent", - "name": "Long Content", + "name": "VERIFICATION ONLY — 운영 사용 금지 · Long Content", "storyTags": [ "verification" ], @@ -3739,7 +3776,7 @@ { "id": "patterns-layout-dashboard-layout--framed", "exportName": "Framed", - "name": "Framed", + "name": "VERIFICATION ONLY — 운영 사용 금지 · Framed", "storyTags": [ "verification" ], @@ -3880,6 +3917,21 @@ "candidate": null, "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." }, + { + "id": "patterns-layout-gnb--sidebar", + "exportName": "Sidebar", + "name": "GNB Sidebar", + "storyTags": [ + "verification" + ], + "effectiveTags": [ + "verification" + ], + "category": "VERIFICATION", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." + }, { "id": "patterns-layout-gnb--navigation-menu-open", "exportName": "NavigationMenuOpen", @@ -4321,7 +4373,7 @@ { "id": "patterns-layout-lnb--with-gnb", "exportName": "WithGNB", - "name": "With GNB", + "name": "VERIFICATION ONLY · GNB + LNB", "storyTags": [ "verification" ], @@ -4429,20 +4481,42 @@ ] }, { - "file": "src/theme/designer-theme.stories.tsx", - "title": "Theme/Designer Playground", + "file": "src/patterns/page-header.stories.tsx", + "title": "Patterns/Layout/Page Header", "metaTags": [], "stories": [ { - "id": "theme-designer-playground--editor", - "exportName": "Editor", - "name": "Designer Playground", + "id": "patterns-layout-page-header--all", + "exportName": "All", + "name": "Page Header", "storyTags": [], "effectiveTags": [], "category": "PRIMARY", "decision": "KEEP", "candidate": null, "reason": "Representative story or distinct reference page." + } + ] + }, + { + "file": "src/theme/designer-theme.stories.tsx", + "title": "Theme/Designer Playground", + "metaTags": [ + "internal" + ], + "stories": [ + { + "id": "theme-designer-playground--editor", + "exportName": "Editor", + "name": "Designer Playground", + "storyTags": [], + "effectiveTags": [ + "internal" + ], + "category": "UTILITY", + "decision": "HIDE", + "candidate": null, + "reason": "Keep direct URL and tag-filter access; excluded from default sidebar." }, { "id": "theme-designer-playground--live-verification", @@ -4453,6 +4527,7 @@ "play-fn" ], "effectiveTags": [ + "internal", "verification", "play-fn" ], diff --git a/AGENTS.md b/AGENTS.md index 6085b65..8746b5a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,15 +1,20 @@ # HMDesign AI 화면 구현 — 필수 규칙 +> 기준 우선순위: 2026-10-10 추가 수정 요청의 확정 사항 → 충돌하지 않는 D-01/D-02 → 현재 HM Component/Pattern/Theme → 현재 Storybook/구현 → 과거 문서. [현재 추가 수정 기준](docs/guidelines/publisher-additional-review-2026-10-10.md). 미확정 값은 임의 확정하지 않으며 과거 검증 기록은 보존합니다. + 이 파일을 먼저 읽고 필요한 항목만 탐색한다. 전체 문서·전체 소스·전체 Storybook을 반복해서 읽지 않는다. -1. 범위 `STORYBOOK_ONLY | PRODUCTION_UI | BOTH`와 Runtime을 확인한다. 신규 AI 업무 화면은 실제 HM React 컴포넌트를 사용한다. 명시적 다른 Runtime adapter는 [상세 계약](docs/guidelines/runtime-portability.md)을 따른다. -2. [기존 Component / Pattern 인덱스](docs/guidelines/component-pattern-reference.md)에서 후보를 찾는다. Import는 `@hm-design/ui` 공개 API가 기본이다. 선택한 문서·대표 All Story·필요한 Props만 확인한다. verification Story는 운영 기준이 아니다. -3. 기존 Component → Variant/Prop → Composition 순으로 재사용한다. 기본 HTML·임의 CSS로 HM 컴포넌트를 모방하거나 `data-slot`만 붙여 재사용했다고 보고하지 않는다. Import만으로 재사용을 인정하지 않는다. 실제 렌더링에 사용해야 한다. -4. 기존 Theme/Semantic Token을 사용한다. React CSS entry는 앱 `index.css → hm-theme.css` 및 필요 시 공개 status CSS를 연결한다. [Runtime Theme](docs/guidelines/runtime-theme.md), [Tailwind/Asset 계약](docs/guidelines/runtime-portability.md)은 관련 변경 때만 읽는다. +1. 범위 `STORYBOOK_ONLY | PRODUCTION_UI | BOTH`, Runtime, 요구사항·필수 동작·완료 조건을 확인한다. 신규 AI 업무 화면은 실제 HM React 컴포넌트를 사용한다. 명시적 다른 Runtime adapter는 [상세 계약](docs/guidelines/runtime-portability.md)을 따른다. +2. [기능별 선택·완료 계약](docs/guidelines/ai-screen-contract.md) → [Component / Pattern 인덱스](docs/guidelines/component-pattern-reference.md) → 선택한 문서·대표 All Story·필요한 Props 순으로 읽는다. Import는 `@hm-design/ui` 공개 API가 기본이다. 의존성은 package.json의 exports/types와 실제 파일 목록을 확인하며 내부 src/dist 경로를 추측하지 않는다. verification Story는 운영 기준이 아니다. +3. 요구 기능에 맞는 기존 Pattern을 먼저 확인한 뒤 기존 Component → Variant/Prop → Composition 순으로 재사용한다. 목록 검색은 [FilterBar](docs/patterns/filter-bar.md)를 Table 유무와 관계없이 검토한다. 미사용 시 요구사항과 맞지 않는 API·구조 제약을 기록하며 강제하지 않는다. 기본 HTML·임의 CSS·`data-slot`으로 HM을 모방하지 않는다. 재사용은 import가 아닌 실제 렌더링으로 확인한다. +4. 기존 Theme/Semantic Token의 실제 선언을 확인한다. 변수 이름을 추측하거나 없는 Token을 fallback 색으로 감추지 않는다. React CSS entry는 `index.css → hm-theme.css` 및 필요 시 공개 status CSS를 연결한다. [Runtime Theme](docs/guidelines/runtime-theme.md), [Tailwind/Asset 계약](docs/guidelines/runtime-portability.md)은 관련 변경 때만 읽는다. 5. 페이지 Navigation은 GNB/LNB/DASHBOARD 중 하나다. 공식 LNB ONLY는 허용한다. 신규 GNB ONLY·DASHBOARD ONLY의 Sidebar는 금지한다. GNB는 GNBMobileMenu도 구성한다. Dashboard는 전용 48px Header와 최소 폭 960px·내부 스크롤을 유지한다. 미지정 시 [선택 근거](docs/guidelines/navigation-single-mode.md#선택-근거)를 기록한다. 6. Storybook은 참조 전용이다. 신규 업무 화면을 Story로 등록하거나 대표 Story를 교체하지 않는다. 독립 미리보기 HTML은 실제 React bootstrap을 실행해야 한다. 별도 HTML 모방 화면·이미지를 실제 React 결과로 제출하지 않는다. 7. 완료 후 Guard를 1회 실행한다: `node scripts/ai-generation-guard.mjs --target <앱> --scope FULL_SCREEN|PARTIAL`. Navigation 재사용 시 `--mode GNB_ONLY|LNB_ONLY|DASHBOARD_ONLY`, PARTIAL은 `--files <수정 파일,...>`을 전달한다. 최종 제출 경로 확인은 같은 실행에 `--result <실제 React HTML 또는 bootstrap>`을 추가한다. -8. FAIL만 수정하고 수정 범위만 PARTIAL 재검사한다. 정상 검사는 반복하지 않는다. Guard PASS는 정적 계약만 뜻한다. 화면별 빌드와 실제 React 브라우저 렌더링(utility 적용·레이아웃·핵심 동작)을 최소 1회 확인한다. 미확인은 `NOT VERIFIED`로 보고한다. [Guard 범위](docs/guidelines/ai-generation-guard.md). -9. Component·Pattern·Theme·대표 Story 디자인/동작·공개 API를 요청 없이 변경하지 않는다. 결과는 실제 사용 요소·Navigation·검증·미검증만 간결하게 보고한다. +8. FAIL만 수정하고 수정 범위만 PARTIAL 재검사한다. 정상 검사는 반복하지 않는다. Guard `staticStatus`는 정적 계약, 원본 `status`는 미검증을 포함한다. 실제 React 빌드·브라우저 결과는 [별도 증거 연결](docs/guidelines/ai-generation-guard.md#runtime-증거-연결)로 기록한다. 원본 미검증을 덮어쓰지 않는다. + CSS 검사는 `styleEvidence`/결합 `styleVerification`의 STATIC·RUNTIME·DESIGN_INTENT·FINAL을 함께 보고한다. HM Component의 HEX/RGB/HSL·Spacing/Radius 하드코드 override는 STYLE-001 FAIL이며 일반 콘텐츠 CSS는 일괄 금지하지 않는다. 동적 Token의 유한 후보는 모두 선언을 확인하고 외부 입력·Props·미해석 className/style은 위치별 Runtime 근거 없이는 NOT VERIFIED다. 문서 recipe나 computed PASS는 디자인 의도 승인이 아니다. [검사 범위와 증거 형식](docs/guidelines/ai-generation-guard.md#css-intent와-동적-token-검사-2026-10-10)을 따른다. +9. Field Label은 공개 [Label](docs/components/label.md#5-api)과 실제 control ID/접근성 이름을 연결한다. 레이아웃·콘텐츠용 HTML은 허용하며 HM이 지원하지 않는 native control/label은 이유를 기록한다. 무조건적인 native HTML 금지는 적용하지 않는다. +10. 요구사항에 없는 CSV·다운로드·외부 API·추가 메뉴·Navigation은 구현하지 않고 제안으로만 보고한다. Story의 예제 기능은 필수 요구사항이 아니다. Component·Pattern·Theme·대표 Story·공개 API를 요청 없이 변경하지 않는다. +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)을 읽는다. 신규 화면에는 위 필수 규칙이 우선한다. diff --git a/CLAUDE.md b/CLAUDE.md index df99a57..0e93b39 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,5 +1,7 @@ # HMDesignSystem +> 기준 우선순위: 2026-10-10 추가 수정 요청의 확정 사항 → 충돌하지 않는 D-01/D-02 → 현재 HM Component/Pattern/Theme → 현재 Storybook/구현 → 과거 문서. [현재 추가 수정 기준](docs/guidelines/publisher-additional-review-2026-10-10.md). 미확정 값은 임의 확정하지 않으며 과거 검증 기록은 보존합니다. + shadcn/ui(Radix, Tailwind v4, CVA) 기반 HM 디자인 시스템 시제품. React + TypeScript + Vite, 개발 Storybook 포트 6024(포터블 테스트 ZIP은 기본 6007, 충돌 시 자동 변경). 실행법과 파일 위치는 `README.md`를 따른다. ## 지침 문서 diff --git a/README.md b/README.md index d9b662a..a9ba003 100644 --- a/README.md +++ b/README.md @@ -6,46 +6,53 @@ React + TypeScript + Vite와 shadcn/Radix 기반 HM 디자인 시스템입니다 **ONE PAGE = ONE NAVIGATION MODE.** GNB ONLY / LNB ONLY / DASHBOARD ONLY 중 하나를 선택한다. 모바일도 선택한 Navigation 자체의 Responsive Variant를 사용한다. 이 정책은 AI 코드 생성·제안, 신규 페이지, 기존 화면 수정, 프로토타입, Storybook, Production에 동일하게 적용한다. [상세 정책](docs/guidelines/navigation-single-mode.md), [AI 작업 지침](AGENTS.md). -새 화면을 구현하는 AI는 [AI 화면 구현 Quick Start](AGENTS.md#ai-화면-구현-quick-start)의 7단계를 순서대로 따른다. 세부 Component·Pattern·Theme·Runtime 계약과 대표 Story 경로도 그 지침에서 연결한다. +새 화면을 구현하는 AI는 [AI 화면 구현 필수 규칙](AGENTS.md)을 따른다. 세부 Component·Pattern·Theme·Runtime 계약과 대표 Story 경로도 그 지침에서 연결한다. ## Storybook Reference와 화면 생성 +퍼블리셔·개발자는 [화면 조합 확인](docs/guidelines/개발-적용-가이드.md#8-publisher--developer-화면-조합-확인)과 [Tabs의 Card/페이지 Usage](docs/components/tabs.md#16-publisher--developer-조합-안내-2026-10-09)를 참고한다. 사전공유 자료의 반영·보류 범위는 [2026-10-09 통합 보고](docs/guidelines/publisher-integration-2026-10-09.md)에 기록한다. 디자인 방향 승인과 공통 Theme·외부 소비 화면 적용 완료를 구분한다. + Storybook은 기존 HM Component·Pattern·Theme·Variant·State·Interaction을 확인하는 Reference 환경이다. 일반적인 업무 화면 생성에서는 신규 화면을 Storybook Story나 사이드바에 등록하지 않고, 대표 Story를 교체하지 않으며, Storybook 실행을 필수 미리보기 경로로 요구하지 않는다. AI는 별도의 React 화면을 만들고 화면별 빌드·필요한 검증과 독립 미리보기를 제공한다. H-2 또는 유사 자동화가 사용 가능한 경우에는 `preview.html`·desktop/mobile 이미지 같은 독립 미리보기 산출에만 사용하며 Storybook 등록 기능으로 취급하지 않는다. Storybook 전용 helper/CSS는 운영 화면에 사용하지 않는다. Component·Pattern 추가/수정, 대표/Verification Story 작성 또는 Storybook Reference 개선을 명시적으로 요청한 경우에만 Storybook을 수정한다. -## 설치 없는 테스트 배포 +## Git Clone 설치·실행 (현재 테스트 공유 기준) -현재 검증된 테스트 배포 기준은 `HMDesignTest-Portable-Test-r3.zip`입니다. 사전 빌드된 Storybook과 Windows x64 포터블 Node를 포함하며 정확한 포함 범위는 r3의 BUILD-INFO·Inventory를 기준으로 확인합니다. `HMDesignTest-Portable-Test-r2.zip`과 `HMDesignTest-Portable-Test.zip`은 이전 스냅샷 기록으로 보존하며 현재 기준이 아닙니다. 포터블 ZIP의 기본 포트는 6007이고 충돌 시 자동으로 다른 포트를 사용합니다. 개발 저장소 Storybook의 6024와는 별개입니다. 테스트 사용자는 ZIP 해제 → `HMDesignTest/start-preview.bat` 더블클릭 → 표시된 로컬 URL 열기만 수행합니다. 별도 Node/npm 설치, npm 명령, Git 작업 또는 운영 업로드는 필요 없습니다. [실행 및 포함 범위](docs/guidelines/portable-test-distribution.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 순서로 재사용합니다. -## Gitea 소스 다운로드 / Windows Storybook 실행 +현재 검증 범위와 남은 제한은 [2026-10-10 잔여 이슈 회귀 보고](docs/guidelines/publisher-validation-2026-10-10.md)를 확인합니다. -정식 저장소 `https://gitea.hmac.kr/b25027/HMDesign.git`를 Git Clone하거나 Gitea 소스 ZIP을 다운로드해 전체 압축을 풉니다. 이 방식은 포터블 ZIP과 달리 PC에 Node.js/npm이 필요합니다. 현재 lockfile의 요구 조건은 **Node.js 20.x의 20.19 이상 또는 22.12 이상**이며 Node.js 21.x는 지원하지 않습니다. +후속 Select 비교 화면 넘침 수정, Guard 46개 회귀 테스트, 개발 의존성 분석과 접근성 제한은 [2026-10-10 최종 안정화 보고](docs/guidelines/publisher-stabilization-2026-10-10.md)를 확인합니다. 이전 보고서는 당시 기록으로 보존합니다. -프로젝트 루트의 `start-storybook.bat`을 더블클릭하세요. 의존성이 없으면 `npm ci`를 자동 실행하고, workspace 패키지 산출물이 없으면 기존 `npm run build:packages`를 실행합니다. 이후 기존 `npm run storybook`으로 서버를 시작하며, HMDesign Storybook의 Story index와 preview HTTP 응답이 준비된 뒤 기본 브라우저에서 **http://localhost:6024/** 를 엽니다. 이미 준비된 HMDesign Storybook이 있으면 서버를 추가 실행하지 않고 기존 주소를 엽니다. 서버 실행 창의 `Ctrl+C`로 종료합니다. +이후 Guard CSS Intent·동적 Token 확장(75개 + 증거 연결 20개 + 요구사항 연결 5개)은 [Guard 보고](docs/reports/guard-css-intent-dynamic-token-2026-10-10.md), Combobox·GNB/LNB 접근성 보완은 [접근성 보고](docs/reports/accessibility-residual-2026-10-10.md)를 따릅니다. 현재 [Gitea 이전 전 최종 정합성 확인](docs/reports/pre-gitea-finalization-2026-10-11.md)과 [2026-10-12 이전 준비](docs/guidelines/gitea-source-transfer-preparation-2026-10-11.md)에는 재사용 근거와 잔여 미검증을 구분합니다. 공식 이전의 Commit·Push는 최종 검토와 별도 승인 후 수행합니다. 인용한 과거 보고서는 당시 기록으로 보존합니다. -실행 파일 위치를 기준으로 동작하므로 한글·공백 폴더, 다른 드라이브 및 Git이 없는 ZIP 환경에서도 사용할 수 있습니다. 최초 설치에는 npm 레지스트리 접속이 필요합니다. 포트 6024를 다른 프로그램이 사용하거나 아직 준비되지 않은 서버가 점유하면 포트를 바꾸지 않고 오류를 표시합니다. 해당 프로그램을 종료하거나 기존 서버 준비를 기다린 뒤 다시 실행하세요. 오류가 발생한 경우에만 창을 유지합니다. 출력된 오류, Node.js/npm의 PATH와 버전, 전체 소스 압축 해제 여부 및 네트워크를 확인하세요. 의존성이 불완전하면 프로젝트 루트에서 `npm ci`를 직접 실행하고 다시 시도합니다. - -## 오전 소스 ZIP / 개발자 전용 절차 (AI 참조용) - -오전 소스 배포의 역사적 기준은 `HMDesignTest.zip`입니다. 당시 전달 구성에서는 `.git` 디렉터리와 Git 이력·원격 연결 정보를 제외하고 `.gitignore`와 소스·Storybook·문서·패키지 설정을 유지했습니다. 이 절의 수량·실행 조건은 오전 ZIP에 대한 기록이며 현재 포터블 r3의 포함 범위는 위 절과 BUILD-INFO를 기준으로 확인합니다. `HMDesignSystem-test-2026-10-08-r4.zip`, `HMDesignSystem-Distribution-Test-v2.zip`, `HMDesignSystem-AI-Reference-Test.zip`은 오전 기준을 대체하지 않습니다. - -기준 ZIP에는 UI·Pattern·CENTER Graphic, 공개 API, Theme, Storybook 소스, 문서, 예제, 패키지 dist, lockfile 및 폰트가 포함됩니다. `node_modules`와 `storybook-static`은 포함되지 않으므로 ZIP 단독으로 정적 Storybook을 바로 열 수는 없습니다. - -오전 소스 ZIP을 개발 환경으로 실행할 경우 Node.js 20.20.1 / npm 10.8.2 환경의 프로젝트 루트에서 다음을 실행합니다. +Node.js는 lockfile의 `^20.19.0 || >=22.12.0` 조건을 충족해야 합니다(Node 21 제외). 이번 재현 환경은 **Node 20.20.1 / npm 10.8.2**입니다. Node 설치 시 제공되는 npm과 PATH를 확인하고, 재현 시에는 검증한 버전을 사용합니다. 최초 설치에는 공식 Gitea와 npm 레지스트리 접근이 필요합니다. ```powershell -# ZIP을 풀고 프로젝트 루트에서 실행 +git clone https://gitea.hmac.kr/b25027/HMDesign.git +cd HMDesign +node --version +npm --version npm ci npm run build:packages -npm run dev npm run storybook ``` -HM 패키지는 ZIP 안의 workspace를 사용하므로 HM Registry 설치가 필요하지 않습니다. `npm ci`로 의존성을 복원하고 `npm run build:packages`로 패키지를 빌드한 뒤 앱과 Storybook을 시작합니다. 정적 Storybook이 필요하면 `npm run build-storybook`을 실행합니다. 이 문서는 배포본의 새 환경 실행 결과를 보증하지 않습니다. +브라우저에서 **http://localhost:6024/** 를 엽니다. Component는 Components, 업무/Layout Pattern은 Patterns, 브랜드 컬러·Typography는 Foundation에서 확인합니다. 내부 Theme 확인은 `?path=/story/theme-runtime-contract--comparison`, Designer는 `?path=/story/theme-designer-playground--editor`를 사용합니다. 확정 컬러로 검수하며 Designer 임시 편집은 해당 Story 안에서만 적용됩니다. [Designer 사용법](docs/guidelines/designer-theme-guide.md). -- Storybook: 기준 ZIP의 `npm run storybook`은 기본 포트 6007입니다. 다른 포트는 `npm run storybook -- --port `로 지정할 수 있습니다. -- ZIP 확인 빌드: `npm run build`, `npm run build-storybook` +Windows에서는 기존 `start-storybook.bat`도 사용할 수 있습니다. 의존성이 없으면 `npm ci`, package 산출물이 없으면 `npm run build:packages`를 수행하고 Story index·preview 응답 준비 후 6024 주소를 엽니다. 포트 충돌 시 오류를 표시합니다. 서버 종료는 실행 터미널의 Ctrl+C입니다. -현재 개발 저장소는 ZIP과 별개로 `npm run storybook` 기본 포트가 6024이며(포터블 ZIP 6007과 통일하지 않음), `npm run build:dashboard-export` → `dist-dashboard/index.html`도 제공합니다. `dist-dashboard`는 H-1(독립 단일 HTML 미리보기 POC, PASS)의 성공 산출물과 동일하지 않으며 ZIP에도 있다고 가정하지 않습니다. H-2(AI 생성 화면의 자동 빌드·캡처·검증)는 Storybook 배포와 별도 기능이며 구현 완료 여부는 현재 파일과 별도 작업 결과로 확인합니다. +정적 Storybook 빌드는 `npm run build-storybook`, 앱 빌드는 `npm run build`, 타입 확인은 `npm run typecheck`입니다. 명령은 루트 package.json의 현재 scripts를 기준으로 합니다. + +## 기존 Component 기반 새 React 화면 + +```powershell +npm run dev:publisher-example +``` + +별도 터미널에서 실행하고 출력된 Vite 주소(기본 5173)를 엽니다. [예제](examples/publisher-list/README.md)는 실제 공개 HM React Component로 GNB·모바일 메뉴·PageHeader·Card·FilterBar·Select·Button·DataTable을 조합합니다. 새 폴더에 복사한 뒤 title/data/columns를 수정하고 bootstrap의 CSS 연결을 유지합니다. 신규 업무 화면은 Storybook에 등록하지 않습니다. 제작·빌드·Guard·브라우저 확인 절차는 [Git 공유 안내](docs/guidelines/publisher-distribution-guide.md)에 있습니다. + +## 과거 배포 기록 + +과거 `HMDesignTest.zip`, 포터블 r1/r2/r3 및 `HMDesign-Publisher-2026-10-10.zip`은 당시 스냅샷 기록입니다. 현재 Git Clone에는 ZIP·포터블 Node·사전 빌드 Storybook·로컬 검증 증거가 포함되지 않습니다. 이번 공유를 위해 ZIP을 새로 생성하지 않습니다. 당시 실행 조건과 검증 결과는 [과거 포터블 안내](docs/guidelines/portable-test-distribution.md)와 [추가 수정 보고서](docs/guidelines/publisher-additional-review-2026-10-10.md)에 보존합니다. 과거 ZIP의 6007과 현재 개발 Storybook의 6024를 구분합니다. ## 사용 (workspace) @@ -67,11 +74,11 @@ import { DataTable, FilterBar, useDataTable } from "@hm-design/ui" // Patterns: 화면 생성 Reference: [Navigation 선택 근거](docs/guidelines/navigation-single-mode.md#선택-근거), [GNB Desktop·Mobile 구성](docs/patterns/gnb.md#8-desktop--mobile)과 [내부 Sidebar](docs/patterns/gnb.md#15-gnb-only-sidebar-composition-2026-10-08), [FilterBar 조회·초기화 소비 예제](docs/patterns/filter-bar.md#61-조회-버튼으로-적용하는-소비-예제). 일반 업무 목록의 모드를 임의 확정하거나 FilterBar에 조회 API가 있다고 가정하지 않습니다. -루트 `package.json`의 `workspaces`(`packages/*`)로 연결됩니다. 확인: `npm run typecheck`, `npm run build`. 두 패키지는 `private: false`이며 ESM JS·타입 선언·CSS를 `dist`에서 export합니다. `npm run build:packages`로 재빌드할 수 있고, Registry publish 이력은 이번 ZIP 검증 범위에 포함하지 않습니다. +루트 `package.json`의 `workspaces`(`packages/*`)로 연결됩니다. 확인: `npm run typecheck`, `npm run build`. 두 패키지는 `private: false`이며 ESM JS·타입 선언·CSS를 `dist`에서 export합니다. `npm run build:packages`로 재빌드할 수 있고, Registry publish 이력은 Git 기반 테스트 공유의 검증 범위에 포함하지 않습니다. ## 파일 위치 -아래 경로는 저장소 루트(압축 해제 시 `HMDesignTest/`) 기준입니다. `packages/ui/` 하위 경로도 전체 상대경로로 표기합니다. +아래 경로는 Git 저장소 루트 기준입니다. `packages/ui/` 하위 경로도 전체 상대경로로 표기합니다. - `packages/ui/` (`@hm-design/ui`): UI Component·업무/Layout Pattern과 HM Theme - `packages/ui/src/components/ui/`: shadcn 기반 컴포넌트, `packages/ui/src/patterns/`: 조합 Pattern(Data Table, Filter Bar 등), `packages/ui/src/index.ts`: 공개 export @@ -92,11 +99,11 @@ import { DataTable, FilterBar, useDataTable } from "@hm-design/ui" // Patterns: - `docs/components/`: 컴포넌트별 현재 구현 기준 문서 - `public/fonts/`: 기존 자료의 Noto Sans KR 글꼴과 라이선스 - `docs/guidelines/`: 프로젝트 지침·구현 원칙·Theme Token·적용/배포 가이드 -- 로컬 검증·감사·진단 자료는 테스트 ZIP에서 제외합니다. +- 로컬 검증·감사·진단 자료는 Git에서 제외합니다. 과거 증거 경로는 로컬 보관 기록이며 Clone에서 열 수 있는 링크가 아닙니다. ## 참고 범위 -초기 디자인의 색상, Typography, Radius와 Button·Input·Select·Dialog에는 외부 ui_viz Reference를 참고했습니다. 이 Reference는 ZIP에 포함되지 않으며 실행에 필요하지 않습니다. 현재 Source of Truth는 HM Component/Pattern 구현, 대표 Storybook Story와 아래 공식 정책 문서입니다. +초기 디자인의 색상, Typography, Radius와 Button·Input·Select·Dialog에는 외부 ui_viz Reference를 참고했습니다. 이 Reference는 Git 공유에 포함되지 않으며 실행에 필요하지 않습니다. 현재 Source of Truth는 HM Component/Pattern 구현, 대표 Storybook Story와 아래 공식 정책 문서입니다. HM Default 컬러의 운영 원본은 `themes/default.json`이며 생성한 CSS preset은 최초 렌더와 네트워크 실패 시 package fallback입니다. 외부 Theme도 같은 공개 계약을 사용합니다. Active와 Focus는 독립 Semantic이고 Global override와 독립 Scoped override를 지원합니다. `Theme/Designer Playground/Editor`에서 JSON URL/파일 Preview와 컬러 편집·Reset·Export/Import를 지원합니다. Portal은 실제 DOM scope를 따릅니다. @@ -117,6 +124,6 @@ Component Mapping은 Semantic/Theme palette를 참조하며 기본 API로 HM 스 ## Package Registry / Publish (별도 내부 운영 정보) -패키지 build와 Registry publish는 별도 단계입니다. Registry 설치·publish·실제 CDN 운영은 이번 테스트 ZIP의 실행 조건이나 검증 범위가 아닙니다. 서비스 배포·버전·캐시·롤백 계획은 [운영/배포 가이드](docs/guidelines/운영-배포-가이드.md)를 따릅니다. +패키지 build와 Registry publish는 별도 단계입니다. Registry 설치·publish·실제 CDN 운영은 Git 기반 테스트 공유의 실행 조건이나 검증 범위가 아닙니다. 서비스 배포·버전·캐시·롤백 계획은 [운영/배포 가이드](docs/guidelines/운영-배포-가이드.md)를 따릅니다. CENTER Graphic은 현재 내부 테스트/검토 목적 범위에서 제공합니다. 외부 공개 또는 사외 배포 전 별도 License 확정이 필요합니다. 임의의 오픈소스 License를 부여하지 않습니다. diff --git a/docs/components/accordion.md b/docs/components/accordion.md index 7b92e95..613185a 100644 --- a/docs/components/accordion.md +++ b/docs/components/accordion.md @@ -1,5 +1,7 @@ # Accordion +> 2026-10-10 추가 수정 요청 기준: 펼침·접힘 모두 hover한 제목 Trigger에만 Black 5% 배경. 본문 배경은 유지. 과거 검증 기록은 당시 결과이며 현재 검증은 [추가 수정 보고서](../guidelines/publisher-additional-review-2026-10-10.md)를 따릅니다. + 현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Accordion 고유의 API·Token·상태·결정만 기록한다. Collapsible(`collapsible.md`)과 값을 공유하지 않는다. ## 1. 상태 @@ -38,7 +40,7 @@ | 상태 | 값 | |---|---| | Closed / Open | Radix `data-state` | -| Hover | Trigger background `transparent`, cursor pointer. 기존 underline/focus 정책 유지, disabled 제외 | +| Hover | 펼침·접힘 모두 Trigger에 Black Alpha 5%, cursor pointer. 본문 유지, disabled 제외 | | Focus (`:focus-visible`) | shadcn ring (`ring-3 ring-ring/50`) 유지 | | Disabled | `opacity: 0.6`(이 컴포넌트 값, 전역 Disabled 규칙으로 승격 안 함), `cursor: not-allowed`, 키보드 이동에서 제외 | | Open / Close animation | shadcn `accordion-down` / `accordion-up` 0.2s ease-out (height). 첫 렌더에서는 Radix가 animation을 막는다 | @@ -83,7 +85,7 @@ Radix 구조·ARIA(`aria-expanded`, `aria-controls`, `role=region`)·키보드(A ## 11. 미확정 / 보류 - 수치(48px, 16/24/500, padding, divider 색, Disabled opacity 0.6)는 현재 HM 규격이며 디자인 확정 시 재검토. -- 현재 Hover는 `transparent`다. Focus Light 2 50% 3px ring과 Disabled opacity 0.6·cursor not-allowed는 유지. +- 현재 Hover는 제목 영역 Black Alpha 5%다. Focus Light 2 50% 3px ring과 Disabled opacity 0.6·cursor not-allowed는 유지. - Indicator는 shadcn chevron 유지(Select의 HM glyph와 별개). 별도 Source glyph 없음. - Motion 규칙이 없어 shadcn animation 유지. `prefers-reduced-motion` 대응은 추가하지 않았다(기존 구현에도 없음). - Container 외곽 박스/Card형 variant는 만들지 않았다. @@ -101,7 +103,7 @@ src/components/accordion.stories.tsx ## 13. 검증 상태 -> HISTORICAL_RECORD: 아래 날짜별 검증은 당시 구현의 기록이다. 과거 soft surface 측정은 보존하며, 현재 Hover 규격은 §4의 `transparent`를 따른다. +> HISTORICAL_RECORD: 아래 날짜별 검증은 당시 구현의 기록이다. 과거 soft surface 측정은 보존하며, 현재 Hover 규격은 §4의 Black Alpha 5%를 따른다. - 2026-10-06 Visual QA: 1280/1024/768/390px에서 Closed/Open Hover의 전체 soft surface·pointer, Focus 3px ring, Disabled opacity 0.6·not-allowed 및 가로 overflow 없음 확인. `typecheck`, `build`, `build-storybook` 통과. diff --git a/docs/components/alert-dialog.md b/docs/components/alert-dialog.md index b8d9a98..4c3d3e5 100644 --- a/docs/components/alert-dialog.md +++ b/docs/components/alert-dialog.md @@ -1,5 +1,7 @@ # Alert Dialog +> 2026-10-10 추가 수정 요청 기준: 취소 Gray Solid / 삭제 Red Solid, 기존 Radix focus·닫기 계약 유지. 과거 검증 기록은 당시 결과이며 현재 검증은 [추가 수정 보고서](../guidelines/publisher-additional-review-2026-10-10.md)를 따릅니다. + 현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Alert Dialog 고유의 API·Token·상태·결정만 기록한다. Dialog(`dialog.md`)와 값을 공유하지 않는다. ## 1. 상태 @@ -14,7 +16,7 @@ ## 2. 적용 근거 -- **참고 근거**: 외부 ui_viz Reference (ZIP 미포함)의 Dialog 문서가 Alert Dialog를 FOLLOW_UP(DIALOG-03)으로만 두고 규격은 없다. 의도(확인·취소를 명시적으로 요구하는 중요한 결정)를 참고했다. 현재 Button 조합은 사용자 확정 규칙에 따라 일반 확인은 취소 Gray Solid / 확인 Green Solid, 삭제 확인은 취소 Gray Outline / 삭제 Red Outline이며 같은 세트의 appearance·size를 통일한다. +- **참고 근거**: 외부 ui_viz Reference (ZIP 미포함)의 Dialog 문서가 Alert Dialog를 FOLLOW_UP(DIALOG-03)으로만 두고 규격은 없다. 의도(확인·취소를 명시적으로 요구하는 중요한 결정)를 참고했다. 현재 Button 조합은 사용자 확정 규칙에 따라 일반 확인은 취소 Gray Solid / 확인 Green Solid, 삭제 확인은 취소 Gray Solid / 삭제 Red Solid이며 같은 세트의 appearance·size를 통일한다. - **현재 HM 규격**: 아래 §3~§4. Dialog의 접근성 원칙과 `asChild` 처리 규칙은 재사용하고 수치는 현재 HM Semantic으로 새로 정했다. - **구현 방식**: shadcn / Radix AlertDialog 구조 유지. Dialog + destructive 플래그로 합치지 않고 별도 컴포넌트로 둔다. - **Dialog와의 차이**: Close(X) 없음, 바깥 클릭으로 닫히지 않음, 초기 focus가 Cancel, Body·Header divider 없음, 폭 400px 고정 상한. @@ -48,9 +50,9 @@ Radix AlertDialog props 그대로. export: `AlertDialog`, `AlertDialogTrigger`, `AlertDialogPortal`, `AlertDialogOverlay`, `AlertDialogContent`, `AlertDialogHeader`, `AlertDialogFooter`, `AlertDialogTitle`, `AlertDialogDescription`, `AlertDialogAction`, `AlertDialogCancel`. -- `AlertDialogAction`: HM Button을 Radix Action 위에 `asChild`로 합성(``). 기본 `variant="primary"` `appearance="solid"`, `size` 기본 `default`. 위험한 작업은 `variant="destructive" appearance="outline"`. +- `AlertDialogAction`: HM Button을 Radix Action 위에 `asChild`로 합성(``). 기본 `variant="primary"` `appearance="solid"`, `size` 기본 `default`. 위험한 작업은 `variant="destructive" appearance="solid"`. - `AlertDialogCancel`: 같은 방식, 기본 `variant="cancel"` `appearance="solid"`. -- `AlertDialogAction variant="destructive"`만 지정하면 기본 `solid`가 적용되어 **미지원 Destructive Solid**가 된다. 삭제 확인은 Action과 Cancel 모두 `appearance="outline"`을 명시하고 같은 size를 사용한다. 자동 appearance 변경은 없다. [Button 허용 조합·기본값 주의](button.md#62-기본값별칭합성-주의)를 따른다. +- `AlertDialogAction variant="destructive"`의 기본 `solid`는 지원된다. 삭제 확인은 Action과 Cancel 모두 Standard Solid와 같은 size를 사용한다. 자동 appearance 변경은 없다. [Button 허용 조합·기본값 주의](button.md#62-기본값별칭합성-주의)를 따른다. - 합성한 Radix 요소에는 `data-slot`을 붙이지 않는다. 붙이면 `asChild` Slot 병합에서 자식 값이 이겨 Button의 `data-slot="button"`이 사라지고 HM Button 스타일이 적용되지 않는다(Dialog에서 해결한 문제와 같은 원인). 출력 속성은 Button의 `data-slot="button"`, `data-variant`, `data-appearance`, `data-size`. - Alert Dialog 자체에는 Button 스타일을 정의하지 않는다. Button API(`variant`/`appearance`/`size`)만 전달한다. - `AlertDialogContent`에 Close(X)는 없다. 새 prop(`size`, `variant`, `destructive`)은 만들지 않았다. @@ -84,7 +86,7 @@ Radix AlertDialog 구조 전부: Portal·Overlay·modal·focus trap·초기 focu - 수치(400px, 18px Title, 24px padding, 그림자)는 현재 HM 규격이며 디자인 확정 시 재검토. - Overlay 색·blur, Motion은 shadcn 기본 유지. -- 파괴적 작업의 기본 강조(Red outline)는 Dialog 삭제 확인과 같은 조합이며 Alert Dialog 고유 규칙이 아니다. +- 파괴적 작업의 기본 강조(Red Solid)는 Dialog 삭제 확인과 같은 조합이며 Alert Dialog 고유 규칙이 아니다. - 아이콘/Media slot, non-dismissible 정책 변경(예: Escape 막기)은 만들지 않았다. - 소비 화면의 공통 header/footer 높이 대응 없음. diff --git a/docs/components/badge.md b/docs/components/badge.md index ee9543e..30e16db 100644 --- a/docs/components/badge.md +++ b/docs/components/badge.md @@ -52,6 +52,8 @@ Badge: `packages/ui/src/theme/badge.css` (`--hm-badge-*`): `radius`, `padding-x/y`, `border-width`, `font-size/line-height/font-weight`, variant별 `{default,secondary,destructive,outline}-{background,text,border-color}`. Semantic 참조. Primitive 신규 추가 없음. +Dashboard 업무 상태의 outline Badge는 [공식 상태 mapping](../patterns/dashboard-layout.md#dashboard-업무-상태-공통-계약)에서 기존 Token을 연결한다. 이 문서화된 소비 예제는 사용자별 디자인 의도 승인이나 Runtime Theme JSON key 확장이 아니다. Guard는 선언 확인·DOM 적용값·override 의도를 [별도 판정](../guidelines/ai-generation-guard.md#css-intent와-동적-token-검사-2026-10-10)하며 실제 Badge 스타일·API는 변경하지 않는다. + ## 7. shadcn / Radix에서 유지한 항목 CVA 구조, `asChild`(Radix Slot), `data-slot`, `cn(..., className)`, 아이콘 크기 규칙(`[&>svg]:size-3`). diff --git a/docs/components/button.md b/docs/components/button.md index e4c765d..281502f 100644 --- a/docs/components/button.md +++ b/docs/components/button.md @@ -1,5 +1,7 @@ # Button +> 2026-10-10 추가 수정 요청 기준: Gray Solid/Outline 기준 #767676, Red Solid 추가. 팝업 결정 버튼은 동일 Solid·size. 일반 화면의 여러 반복·보조 버튼은 Outline, 검색은 조회 Solid Green / 초기화 Outline Gray 예외. 과거 검증 기록은 당시 결과이며 현재 검증은 [추가 수정 보고서](../guidelines/publisher-additional-review-2026-10-10.md)를 따릅니다. + 현재 HM 구현과 Runtime-neutral 사용 기준 문서. 공통 원칙은 `AGENTS.md`와 `docs/guidelines/runtime-portability.md`를 따른다. 이 문서는 Button API·Token·상태·사용 위치·조합 계약을 기록한다. ## 1. 상태 @@ -15,7 +17,7 @@ ## 2. 적용 근거 - **ui_viz Confirmed**: size, padding-x, label size/line-height(button.md §26, D-087), Brand gradient·bevel(D-087), Standard Solid/Outline 색(§28, D-086, D-087), Hover/Pressed 동일 Black 20%(§12), Red Outline Hover/Pressed Red 8%. -- **HM 사용자 확정 규칙**: 실제 화면 Brand 최대 1개·팝업 Brand 금지, 일반 확인/작성 Solid 세트, 삭제 확인·행/반복 작업 Outline 세트, 같은 세트의 appearance와 size 통일(§5). Disabled `0.6`은 ui_viz 규격 값이며, Input·Select의 2026-10-02 결정과는 별개다. +- **HM 사용자 확정 규칙**: 실제 화면 Brand 최대 1개·팝업 Brand 금지, 일반 확인/작성 Solid 세트, 삭제 확인 Solid 세트·행/반복 작업 Outline, 같은 세트의 appearance와 size 통일(§5). Disabled `0.6`은 ui_viz 규격 값이며, Input·Select의 2026-10-02 결정과는 별개다. - **shadcn 유지**: 구조(CVA, `data-slot`, `cn(..., className)`), focus/invalid ring class, `ghost`·`link` variant와 기존 size 이름. - **미정의/TBD**: Focus 표현(ui_viz TBD → shadcn 기본 focus ring 유지), `aria-invalid` 표현(ui_viz에 없음 → shadcn class 유지), `ghost`·`link`의 규격(ui_viz 없음). @@ -57,10 +59,10 @@ - `outline`: 반복·보조 작업과 표/목록 행 작업에 사용한다. - 일반 작성·수정창: 취소 Solid + 등록/저장 Solid. - 일반 확인창: 취소 Solid + 확인 Solid. -- 삭제 확인창: 취소 Outline + 삭제 Outline. +- 삭제 확인창: 취소 Solid + 삭제 Solid. - 표/목록 행과 반복·보조 작업: 동일 `appearance`의 Outline 조합. -- FilterBar 대표 Story의 초기화·조회: `cancel` + `primary`, 둘 다 Outline·`size="default"`. 목록의 반복 조회·보조 작업 세트다. -- 하나의 세트는 `appearance`와 `size`를 통일한다. 역할 차이는 variant의 기존 역할색과 문구로 표현한다. +- 검색은 조회 `primary/solid` + 초기화 `cancel/outline`·같은 size를 사용한다. 조회·초기화는 진행/취소 결정 세트가 아니므로 같은 appearance를 강제하지 않는다. +- 취소·적용처럼 동일 작업의 결정 세트는 `appearance`와 `size`를 통일한다. 역할 차이는 variant의 기존 역할색과 문구로 표현한다. 사용 위치를 기준으로 appearance를 명시하며 버튼 문구만으로 판단하지 않는다. 컴포넌트는 appearance 생략 시 기본 `brand`를 출력하므로, 팝업·버튼 세트에서는 기본값에 의존하지 않는다. @@ -89,7 +91,7 @@ asChild: shadcn 그대로 - `secondary` (Brown): `brand`, `solid`, `outline`. - `modify` (Orange): `brand`, `solid`. **`outline` 미지원**. - `cancel` (Gray): `solid`, `outline`. **`brand` 미지원**. -- `destructive` (Red): `outline`만 지원. **`brand`, `solid` 미지원**. +- `destructive` (Red): `solid`, `outline` 지원. **`brand` 미지원**. 공개 Props는 `variant`와 `appearance`를 독립적으로 받는다. TypeScript 통과나 Storybook Controls에서 선택 가능하다는 사실은 해당 조합의 디자인 지원을 뜻하지 않는다. 미지원 조합에는 필요한 역할별 CSS Custom Property가 없으며 자동 보정·fallback도 없다. 기존 CSS 변수에 임의 fallback이나 새로운 역할색을 추가하지 않는다. @@ -98,16 +100,16 @@ asChild: shadcn 그대로 - ` - + + -// Alert Dialog 삭제 확인: 두 요소 모두 Outline을 명시 -취소 -삭제 +// Alert Dialog 삭제 확인: 두 요소 모두 Standard Solid +취소 +삭제 ``` 미지원 조합 추가, role별 자동 appearance 선택, 기본 appearance 변경은 사용자 결정이 필요한 별도 변경이다. 현재 계약과 공개 API를 유지한다. @@ -129,7 +131,7 @@ React가 아닌 Runtime도 배포된 `@hm-design/ui/theme.css` 또는 승인된 - Role × Appearance는 §6.1의 허용 조합만 사용한다. 위 속성 vocabulary가 모든 곱집합을 허용하는 것은 아니다. - CSS는 `--hm-button-*` Component variable과 `--hm-sem-*`/기존 Theme variable을 소비한다. HEX와 임의 variable을 추가하지 않는다. - native `disabled`, `:hover`, `:active`, `:focus-visible`로 Disabled/Hover/Pressed/Focus 계약을 유지한다. `aria-disabled`만으로 native disabled를 대체하지 않는다. -- 팝업의 취소/확인 조합은 같은 `size`를 사용하고, 삭제 확인은 취소·삭제 모두 Outline이어야 한다. Escape, Tab 순서, 초기 포커스와 닫힌 뒤 trigger 포커스 복귀는 modal primitive 또는 동등한 keyboard contract로 보존한다. +- 팝업의 취소/확인 조합은 같은 `size`를 사용하고, 삭제 확인은 취소·삭제 모두 Standard Solid이어야 한다. Escape, Tab 순서, 초기 포커스와 닫힌 뒤 trigger 포커스 복귀는 modal primitive 또는 동등한 keyboard contract로 보존한다. React의 `variant` prop을 그대로 복제할 필요는 없지만, 동일한 role/treatment attribute와 시각·상태·접근성 계약을 유지해야 한다. @@ -148,7 +150,7 @@ CVA 구조, `data-slot`, `asChild`(Radix `Slot`), `className` 확장, focus-visi ## 11. Storybook - 대표 Story `All`("Button")이 기본 sidebar의 유일한 Button 항목이다 → 1뎁스. -- `All`의 구역: Overview → Sizes → Variants → States. 기본 버튼·크기·허용 조합·Brand/Standard 상태를 보여준다. Red Outline은 Variants와 Standard States에도 포함되며, 삭제·취소 세트와 Long Label은 verification Story에서 따로 확인한다. +- `All`의 구역: Overview → Sizes → Variants → States. 기본 버튼·크기·허용 조합·Brand/Standard 상태를 보여준다. Red Solid와 Outline은 Variants와 Standard States에도 포함되며, 삭제·취소 세트와 Long Label은 verification Story에서 따로 확인한다. - verification(`tags: ["verification"]`, 기본 sidebar 제외): `Roles`, `Sizes`, `States`("Brand States"), `StandardStates`, `LongLabel`, `KoreanLabels`, `IconTextNarrow`, `Destructive`, `Playground`(HM 전용), `Compatibility`(ghost/link, default/outline 별칭). Controls는 조합별 선택을 제한하지 않으므로 §6.1의 허용 조합을 직접 지킨다. Verification은 기술 점검용이며 운영 화면의 사용 위치·세트 규칙은 §5가 우선한다. - 대표 경로/ID: `Components/Form/Button → All` (`components-form-button--all`), `Components/Overlay/Dialog → All` (`components-overlay-dialog--all`), `Components/Overlay/Alert Dialog → All` (`components-overlay-alert-dialog--all`), `Patterns/Data/Filter Bar → Filter Bar` (`patterns-data-filter-bar--all`). 삭제 세트 점검 ID는 `components-form-button--destructive`(verification)다. - 운영 규칙은 `프로젝트-지침.md` §6.3, §7 참조. diff --git a/docs/components/card.md b/docs/components/card.md index 3c24927..e2bdba5 100644 --- a/docs/components/card.md +++ b/docs/components/card.md @@ -72,11 +72,21 @@ CardHeader, CardTitle, CardDescription, CardAction, CardContent, CardFooter: sha |---|---|---| | `src/components/card.stories.tsx` `Compatibility` | shadcn 호환 확인 | verification 전용 | | `src/patterns/dashboard-layout.stories.tsx` | 업무 목록 건수(`role="status"`), KPI 라벨(Title 없이 Description만), 가로 스크롤 안내 | Pattern Story 안의 소비 예시. 별도 스타일 없음 | -| `src/App.tsx` (Demo) | Panel 부제 | `.hm-panel-header`가 11px·`--hm-muted`로 덮어씀. Demo 전용(§8.1) | +| 과거 UI Demo (`src/App.tsx`, 공식 이전 전 baseline) | Panel 부제 | `.hm-panel-header`가 11px·`--hm-muted`로 덮어씀. Demo 전용(§8.1) | - Header에 Description이 있으면 shadcn의 `has-data-[slot=card-description]:grid-rows-[auto_auto]`로 2행 grid가 되고, `CardAction`은 `row-span-2`로 두 행에 걸친다. 이 동작은 shadcn 그대로다. - Description의 HM Token·크기·색은 미정의다. 필요해지면 이 문서에서 결정한 뒤 Token으로 추가한다(§11). +### 5.2 Tabs를 Card 안에 조합할 때 + +기본 White Card 안의 탭은 `TabsList panelSurface="surface"`를 사용하고, CardContent 안의 각 `TabsContent`는 `px-0 py-6`로 조합한다. Card의 배경·border·radius·기본 여백은 유지하며 내부 탭 패널의 중복 `border bg-background rounded-b p-6`만 제거한다. [Tabs 조합 예제](tabs.md#16-publisher--developer-조합-안내-2026-10-09)와 Tabs 대표 Story의 Usage를 기준으로 한다. + +- CardContent는 기본 24px 좌우 여백을 소유한다. 내부 TabsContent에 24px 좌우 여백을 다시 더하지 않는다. `size="sm"`의 12px은 기존 호환 규격이며 이번 조합 때문에 24px로 강제하지 않는다. +- `surface="content"` Card는 실제 배경에 맞춰 `panelSurface="content"`를 사용한다. `panelSurface`는 Card의 표면이나 패널 배경을 자동 변경하지 않는다. +- 탭 콘텐츠 상하 24px은 조합 예시 값이며, Card의 Header→Content 24px이나 전체 화면 섹션 간격을 대체하는 공통 Token이 아니다. §11의 Tabs Meta 예외(0px 간격)를 새로 구현한 것도 아니다. +- 내부 Input·Textarea의 border, Table의 행 배경·구분선, Alert 등의 역할별 스타일은 보존한다. CardDescription의 호환 범위는 §5.1을 유지하며 PageHeaderDescription의 8px/14·20 규격으로 바꾸지 않는다. +- 좁은 화면에서는 flex/grid 자식인 Card 자체와 CardContent의 `min-w-0` 등 소비 Layout 수축 조건을 확인한다. Table이 있는 grid 조합에서 Card 자체의 자동 최소 폭을 유지하면 내부 스크롤 전에 페이지가 넓어질 수 있다. Tabs/Table 내부 스크롤을 함께 확인하며 Card의 새로운 Responsive Variant나 폭 Token은 추가하지 않는다. + ## 6. Token Component Mapping 위치: `packages/ui/src/theme/card.css` (`--hm-card-*`), `packages/ui/src/theme.css`에서 import. @@ -116,7 +126,7 @@ semantic HTML(`div`), React composition(Header/Title/Description/Action/Content/ ### 8.1 Demo `.hm-panel`과 운영 Card 구분 - **운영 Card**: `@hm-design/ui`의 ``와 `packages/ui/src/theme/card.css`. 이 문서 §3의 값(padding 24px, Title 20/28/600 등)과 대표 Story `All`이 기준이다. 소비 프로젝트는 `hm-panel*` class 없이 이 API만 쓴다. -- **Demo Panel**: `src/App.tsx`의 4개 Card에 붙은 `hm-panel`, `hm-panel-header`, `hm-panel-icon`, `hm-panel-content` class와 `src/hm-theme.css` 규칙. 패키지(`packages/ui`)에 포함되지 않으며 Card 규격 Reference가 아니다. +- **과거 Demo Panel**: 공식 이전 전 UI Demo의 `src/App.tsx`에 있던 4개 Card에 붙은 `hm-panel`, `hm-panel-header`, `hm-panel-icon`, `hm-panel-content` class와 `src/hm-theme.css` 규칙. 패키지(`packages/ui`)에 포함되지 않으며 Card 규격 Reference가 아니다. - `.hm-panel`은 Card 외형(border, radius, background, shadow)을 지정하지 않는다. 다만 Demo class 묶음은 운영 Card 값을 다음처럼 덮어쓴다. | Demo class | 덮어쓰는 값 | 운영 Card 값 | @@ -129,6 +139,7 @@ semantic HTML(`div`), React composition(Header/Title/Description/Action/Content/ | `.hm-panel-content` | `padding: 23px` | 좌우 24px | - Demo 값은 HM Token이 아닌 hex 값을 포함한다. Card Token·Story의 근거로 쓰지 않으며, Demo 화면을 Card 디자인 Reference로 캡처·인용하지 않는다. +- 현재 `src/App.tsx`는 `WorkDashboard`를 렌더한다. 현재 업무 앱에는 `hm-panel-header` 사용이 없지만 과거 CSS와 Git 이력은 보존한다. 기존 Demo override 기록을 현재 공개 Card 규격이나 신규 override 승인으로 해석하지 않는다. Guard의 Component 식별·진입점 범위 판정은 [STYLE-001 범위 계약](../guidelines/ai-generation-guard.md#style-001-component-식별과-진입점-범위-2026-10-11)을 따른다. ## 9. Storybook @@ -166,7 +177,7 @@ packages/ui/src/theme.css (card.css import) src/components/card.stories.tsx src/components/story-board.tsx src/patterns/dashboard-layout.stories.tsx (CardDescription 소비 예시) -src/App.tsx (Demo, hm-panel* class 사용) +src/App.tsx (현재 WorkDashboard; 과거 hm-panel* Demo 기록은 §8.1) src/hm-theme.css (.hm-panel* Demo 전용 스타일, §8.1) ``` diff --git a/docs/components/checkbox.md b/docs/components/checkbox.md index 1f5a084..acedf4d 100644 --- a/docs/components/checkbox.md +++ b/docs/components/checkbox.md @@ -1,5 +1,7 @@ # Checkbox +> 2026-10-10 추가 수정 요청 기준: `asChild`는 단일 React DOM 호환 요소를 받으며 소비자 자식·ID·이벤트·ref를 보존하고 내부 Indicator를 추가한다. 커스텀 요소는 props/ref를 최종 DOM으로 전달해야 한다. 기존 기본 button·외부 form 유지. 과거 검증 기록은 당시 결과이며 현재 검증은 [추가 수정 보고서](../guidelines/publisher-additional-review-2026-10-10.md)를 따릅니다. + 현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Checkbox 고유의 API·Token·상태·결정만 기록한다. ## 1. 상태 diff --git a/docs/components/combobox.md b/docs/components/combobox.md index 7079971..44b08be 100644 --- a/docs/components/combobox.md +++ b/docs/components/combobox.md @@ -1,5 +1,9 @@ # Combobox +> 현재 접근성 보완(2026-10-10): 흰 Trigger의 빈 값 안내는 기존 `--hm-sem-fg-secondary`를 사용한다. 기본 Theme에서 #6e6759/흰색이며 공통 placeholder Token과 Disabled opacity는 보존한다. 아래 과거 검증의 #999 실측은 당시 기록이다. + +> 2026-10-10 추가 수정 요청 기준: 현재 구현의 연결형 Popup을 기준으로 문서를 통일. 팝업 dialog 이름은 aria-label → aria-labelledby → 고유 trigger ID 순으로 연결. Command 검색·listbox 이름과 인스턴스 ID는 독립. 과거 검증 기록은 당시 결과이며 현재 검증은 [추가 수정 보고서](../guidelines/publisher-additional-review-2026-10-10.md)를 따릅니다. + 현재 HM 구현의 기준 문서. 공통 원칙은 `docs/guidelines/프로젝트-지침.md`(§6, §7), Token 구조 원칙은 `테마-토큰-가이드.md`를 따른다. 이 문서는 Combobox 고유의 API·Token·상태·결정만 기록한다. ## 1. 상태와 성격 @@ -17,7 +21,7 @@ - **참고 근거**: 외부 ui_viz Reference (ZIP 미포함)의 기존 테스트 규격에는 Combobox가 없다(Select와 Search Field는 다른 컴포넌트). 참고할 수치가 없었다. - **Component로 만든 이유**: 검색 가능한 단일 선택은 서비스에서 반복되고(trigger 텍스트, 검색, 선택 표시, 닫힘, focus 복귀가 동일) 최소 API(`options`)로 충분해서 typed wrapper를 제공한다. 더 복잡한 항목(그룹, 아이콘 등)은 소비자가 `Command`를 직접 조합한다. -- **구현 방식**: Popover(Portal, collision, Escape, focus 복귀)와 Command(검색, 필터링, 키보드, listbox ARIA)를 그대로 조합한다. Select의 구현이나 연결형 윤곽을 복제하지 않았다. +- **구현 방식**: Popover(Portal, collision, Escape, focus 복귀)와 Command(검색, 필터링, 키보드, listbox ARIA)를 그대로 조합한다. Select 구현을 복제하지 않고 Popover + Command를 유지하며 현재 연결형 윤곽을 사용한다. - **보류**: §11. ## 3. Select와의 구분 @@ -26,7 +30,7 @@ |---|---|---| | 용도 | 정해진 짧은 목록에서 선택, 검색 불필요 | 검색/필터가 필요한 선택 | | 구현 | Radix Select | Popover + Command(cmdk) | -| 목록 | trigger에 붙은 연결형 윤곽, 선택 항목은 면으로 표시 | 독립 floating 패널(검색 Input + 목록), 선택 항목은 Accent 45% 면 + check 아이콘 | +| 목록 | trigger에 붙은 연결형 윤곽, 선택 항목은 면으로 표시 | trigger 연결형 패널(검색 Input + 목록), 선택 항목은 Accent 45% 면 + check 아이콘 | | 접근성 모델 | Radix Select(listbox) | `role=combobox` trigger + 패널 안 cmdk combobox/listbox | ## 4. 기본 규격 (현재 HM 규격) @@ -35,8 +39,8 @@ |---|---| | Size | `large`(기본, 44px, 16 / 24), `medium`(34px, 14 / 20) — Select와 같은 두 크기 | | Trigger | 폭 100%(부모 폭), padding-x 12px, 1px `--hm-sem-stroke-input-default`, radius 4px, 흰 면, 오른쪽 chevron 18px | -| 값 / placeholder | 선택된 option의 label, 없으면 placeholder("선택", `--hm-sem-fg-placeholder`), 길면 말줄임 | -| Popup | padding 0, 폭 = trigger 폭(`--radix-popover-trigger-width`), 독립 4px gap. Focus Light 2 border, input radius 4px(네 모서리), shadow `0 2px 24px black 12%` | +| 값 / placeholder | 선택된 option의 label, 없으면 placeholder("선택", `--hm-sem-fg-secondary`), 길면 말줄임 | +| Popup | padding 0, 폭 = trigger 폭(`--radix-popover-trigger-width`), sideOffset=-1로 1px 연결 경계 공유. Focus border, 안쪽 두 모서리 radius는 기존 Input token, shadow `0 2px 24px black 12%` | | 검색 | Command Input(placeholder 기본 "검색...") | | 목록 | Command List(최대 280px), Item 높이 36px·padding-x 12px·radius 0·weight 400. Large 16/24, Medium 14/20. 선택 check 16px, 긴 label은 말줄임 | | Empty | "결과가 없습니다."(`emptyText`) | @@ -59,7 +63,7 @@ export: `Combobox`, types `ComboboxOption`(`{ value, label, disabled? }`), `Comb - `options`(필수), `value`/`defaultValue`/`onValueChange(value)`, `placeholder`, `searchPlaceholder`, `emptyText`, `size`, `disabled`, 그리고 `button` props(`id`, `aria-label`, `className`…). - 필터링: option의 `value`와 `label`(keywords)에 cmdk 기본 fuzzy 필터를 쓴다. - 이미 선택된 option을 다시 선택해도 값은 유지된다(해제 UI는 없다). -- **접근성**: Trigger는 `role="combobox"`, `aria-haspopup="listbox"`, `aria-expanded`. 패널 안 Command Input이 `role=combobox`(`aria-controls` → `role=listbox`), option은 `role=option`. 이름은 `