From 09321e7d1491df3ef8358e566fffb57761992eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=88=98=ED=98=84?= Date: Sun, 11 Oct 2026 15:20:13 +0900 Subject: [PATCH] docs(storybook): simplify Foundation Color to brand palettes --- docs/guidelines/brand-color-steps.md | 4 +- docs/guidelines/runtime-theme-contract.md | 4 +- ...-orange-background-gradients-2026-10-11.md | 2 +- ...dation-color-display-cleanup-2026-10-11.md | 57 ++++++++ .../foundation-color-storybook-2026-10-11.md | 2 + src/foundation/color.stories.tsx | 137 +++--------------- 6 files changed, 85 insertions(+), 121 deletions(-) create mode 100644 docs/reports/foundation-color-display-cleanup-2026-10-11.md diff --git a/docs/guidelines/brand-color-steps.md b/docs/guidelines/brand-color-steps.md index 227a1ef..08201fb 100644 --- a/docs/guidelines/brand-color-steps.md +++ b/docs/guidelines/brand-color-steps.md @@ -33,4 +33,6 @@ Orange Dark 1·2의 원본 Needs Review 상태와 [이전 검수 보고서](../r ## 호환성과 Storybook -[Color 대표 Story](../../src/foundation/color.stories.tsx)는 브랜드별 8개 순서 슬롯을 제공한다. Green 8/8·Brown 8/8·Orange 8/8 확정이며 Default를 기본색으로 강조한다. Legacy Button 색상은 별도로 표시하고 동일 Key를 계속 사용한다. 이번 두 팔레트 입력은 Color Reference에서 소비하며 기존 Component mapping을 새 값에 연결하지 않는다. 기존 public Key·Property·type·Default·API·schemaVersion 1과 서비스 partial JSON 호환성을 유지한다. [Runtime Theme](runtime-theme.md)의 geometry/상태 권한과 Designer의 기존 17개 상위 역할 입력 UI는 확대하지 않는다. 한맥오렌지의 미확정 브랜드 단계는 없다. +페이지 소개는 `한맥 브랜드 컬러`로 표시한다. 그룹 설명의 단계 순서 나열 문구는 제공하지 않으며, 개별 컬러칩의 단계명과 HEX는 유지한다. + +[Color 대표 Story](../../src/foundation/color.stories.tsx)는 한맥그린·한맥브라운·한맥오렌지 3개 그룹에 각 8개, 총 24개 컬러칩만 표시한다. Green 8/8·Brown 8/8·Orange 8/8 확정이며 단계명·현재 HEX와 Default 기본색 강조를 유지한다. 2026-10-11 최종 표시 요청에 따라 Brown Deep, Orange Light·Mid·Dark·Deep의 별도 표시, Accent·Semantic·Gradients 섹션과 개발용 Token·CSS Variable·Runtime Key 설명은 제거한다. Legacy Button 색상과 Accent·Semantic·Gradient의 실제 Key·값·소비처는 보존한다. 기존 Component mapping을 새 값에 연결하지 않으며 public Key·Property·type·Default·API·schemaVersion 1과 서비스 partial JSON 호환성을 유지한다. [Runtime Theme](runtime-theme.md)의 geometry/상태 권한과 Designer의 기존 17개 상위 역할 입력 UI는 확대하지 않는다. 한맥오렌지의 미확정 브랜드 단계는 없다. [표시 정리 검증 기록](../reports/foundation-color-display-cleanup-2026-10-11.md). diff --git a/docs/guidelines/runtime-theme-contract.md b/docs/guidelines/runtime-theme-contract.md index c3cad55..ca739fc 100644 --- a/docs/guidelines/runtime-theme-contract.md +++ b/docs/guidelines/runtime-theme-contract.md @@ -104,7 +104,7 @@ ## Generic Status Error — Provisional · 2026-10-11 -`color.status.error` → `--hm-sem-status-error` → Default `#B3261E`는 일반 상태 오류의 독립 Semantic Token이다. 사용 가능 상태만 승인되었으며 Confirmed 승격이나 Component 적용은 별도 사용자 결정이다. [Foundation Color](../../src/foundation/color.stories.tsx)는 기존 Semantic 카드와 Computed Property Hook으로 실제 Swatch·HEX·Canonical·CSS Variable·Provisional을 표시한다. [추가 및 선택 검증 기록](../reports/generic-status-error-token-2026-10-11.md). +`color.status.error` → `--hm-sem-status-error` → Default `#B3261E`는 일반 상태 오류의 독립 Semantic Token이다. 사용 가능 상태만 승인되었으며 Confirmed 승격이나 Component 적용은 별도 사용자 결정이다. 2026-10-11 최종 표시 요청으로 [Foundation Color](../../src/foundation/color.stories.tsx)의 Semantic 섹션은 제거하지만 실제 Token과 Provisional 상태는 유지한다. [추가 당시 선택 검증 기록](../reports/generic-status-error-token-2026-10-11.md). Input Error와 Generic Status Error는 별도 역할이다. Input / Alert destructive / Toast Error / Badge destructive 및 `color.action.destructive`의 기존 `#D32F2F` 값·Alias 체인, Dashboard issue `#FF4D4F`는 유지한다. 새 Token을 기존 Component에 자동 연결하지 않는다. @@ -131,4 +131,4 @@ Dashboard의 `active / hold / done / issue` 업무 상태 계약은 `packages/ui 조합 변수 `--hm-sem-bg-brown` / `--hm-sem-bg-orange`는 내부 Derived CSS이며 전체 Gradient 문자열·방향·위치를 Runtime JSON 입력으로 받지 않는다. Runtime에서 한 stop을 바꾸려면 해당 브랜드의 5개 stop을 모두 전달한다. Brown·Orange 두 그룹은 서로 독립적이며 미지정 그룹은 기존 fallback을 따른다. 부분 그룹은 atomic validation으로 거부한다. Global / Scoped 적용과 Reset은 [Runtime Theme](runtime-theme.md)의 기존 계약을 사용한다. -공통 Background는 기존 Green·Brown/Orange Button·Dashboard 배경에 자동 연결하지 않는다. [Foundation Color](../../src/foundation/color.stories.tsx)의 기존 `foundation-color--all`에서 Green·Brown·Orange 공통 Background Gradient, 각 stop Token/CSS 변수·위치·HEX, 미리보기 요소의 computed `background-image`를 표시한다. Button 전용 Gradient 미리보기는 Foundation Color에서 제외하며, 실제 Button의 Gradient CSS·Theme·Token은 유지한다. 과거 미정의 검증 보고서는 당시 이력으로 유지한다. +공통 Background는 기존 Green·Brown/Orange Button·Dashboard 배경에 자동 연결하지 않는다. 2026-10-11 최종 표시 요청으로 [Foundation Color](../../src/foundation/color.stories.tsx)의 기존 `foundation-color--all`은 [브랜드 3개 그룹·24개 컬러칩](brand-color-steps.md#호환성과-storybook)만 표시하며 모든 Gradient 미리보기와 개발용 설명을 제거한다. 실제 공통 Background·Button·Dashboard Gradient의 CSS·Theme·Token과 Runtime 계약은 유지한다. 과거 미정의 및 Gradient 추가 검증 보고서는 당시 이력으로 유지한다. diff --git a/docs/reports/brown-orange-background-gradients-2026-10-11.md b/docs/reports/brown-orange-background-gradients-2026-10-11.md index 2a1472f..25e210d 100644 --- a/docs/reports/brown-orange-background-gradients-2026-10-11.md +++ b/docs/reports/brown-orange-background-gradients-2026-10-11.md @@ -2,7 +2,7 @@ RESULT: PARTIAL. 승인된 두 Gradient의 구현·빌드·브라우저 검증은 PASS다. Guard의 패키지 범위 및 동적 스타일 NOT VERIFIED는 보존한다. Git Commit/Push 결과와 최종 SHA는 작업 최종 응답에서 기록한다. -> 이 문서는 Gradient 추가 당시의 검증 기록이다. 현재 Foundation Color에서는 공통 Background 3개만 미리보기하고 Button 전용 미리보기는 제거했다. 실제 Button·Dashboard Gradient와 당시 검증 결과는 보존한다. [현재 표시 계약](../guidelines/runtime-theme-contract.md)을 따른다. 현재 문서의 개인 식별 정보 정리는 과거 Git 객체나 Commit Author/Committer를 삭제하지 않는다. +> 이 문서는 Gradient 추가 당시의 검증 기록이다. 이후 최종 표시 요청으로 Foundation Color의 Gradients 섹션 전체를 제거했다. 실제 공통 Background·Button·Dashboard Gradient와 당시 검증 결과는 보존한다. [현재 표시 계약](../guidelines/brand-color-steps.md#호환성과-storybook)과 [표시 정리 보고서](foundation-color-display-cleanup-2026-10-11.md)를 따른다. 현재 문서의 개인 식별 정보 정리는 과거 Git 객체나 Commit Author/Committer를 삭제하지 않는다. ## 기준과 범위 diff --git a/docs/reports/foundation-color-display-cleanup-2026-10-11.md b/docs/reports/foundation-color-display-cleanup-2026-10-11.md new file mode 100644 index 0000000..75b7e27 --- /dev/null +++ b/docs/reports/foundation-color-display-cleanup-2026-10-11.md @@ -0,0 +1,57 @@ +# Foundation Color 표시 정리 — 2026-10-11 + +최종 표시 요청과 추가 문구 정리를 함께 반영했다. 실제 정적 Storybook에서 한맥그린·한맥브라운·한맥오렌지 3개 그룹, 각 8개·총 24개 컬러칩을 확인했다. 개별 단계명·현재 HEX·Default 강조를 유지한다. Theme·Token·Gradient·Button·Dashboard 및 AI Guard 구현은 변경하지 않았다. Guard의 원본 NOT VERIFIED는 별도로 보존한다. + +## 기준과 변경 범위 + +- 공식 경로: `D:\디자인시스템\HMDesign-Gitea-Transfer`, 브랜치 `main`, origin `https://gitea.hmac.kr/b25027/HMDesign.git`. +- 시작 Local/Remote SHA: `d6d6eb53afa57cc267e5ff337177e00c7d2e92f2`. 시작 Working Tree는 깨끗했다. +- Scope: STORYBOOK_ONLY / React. 기존 `Foundation/Color`, `All` export 및 `foundation-color--all` ID 유지. 최종 build index의 기존 292개 Story ID 유지. +- 실제 변경 코드는 [Color Story](../../src/foundation/color.stories.tsx) 하나다. [ReferencePage / ReferenceSection / Computed Property Hook](../../src/foundation/reference.tsx), [공통 StoryPage](../../src/components/story-page.tsx), 기존 팔레트 행·Swatch·Default 강조 CSS를 그대로 재사용한다. +- 신규 업무 화면·Navigation·검색·Form control이 없으므로 Pattern / FilterBar / Navigation 선택 및 control Label·disabled/readOnly·focus 복귀 검증은 해당하지 않는다. + +## 최종 표시 + +- 소개: `한맥 브랜드 컬러`. 그룹 설명의 단계 순서 나열 문구는 제거하며 빈 설명 paragraph도 렌더하지 않는다. +- 각 그룹의 단계 순서는 기존 Light 4·Light 3·Light 2·Light 1·Default·Dark 1·Dark 2·Dark 3이며, 개별 칩에 단계명과 HEX를 표시한다. +- Brown Deep 및 Orange Light·Mid·Dark·Deep의 별도 칩, Accent·Semantic·Gradients 섹션 전체, Token 상세와 Canonical Token·CSS Variable·Runtime Key 등 개발용 설명을 제거했다. +- Default 3개 모두 기존 `2px` border와 `font-weight: 700` 강조를 유지한다. 작은 화면의 기본색 문구는 기존 칩 폭에 따라 2줄이며 HEX는 읽을 수 있다. +- 실제 색은 공개 `@hm-design/ui/theme` 계약의 기존 CSS property를 소비한다. Hook이 24개 property의 현재 computed 값을 읽으며 Story에 HEX 원본을 복제하지 않는다. + +## 최종 통합 검증 + +| 항목 | 결과 | +|---|---| +| AI Guard 회귀 | Guard 94/94 + 증거 연결 20/20 + 요구사항 5/5 = **119/119 PASS**, 실패·skip 0. 원본 테스트·Guard 소스 변경 없음 | +| Component 회귀 | `node --test scripts/publisher-components.test.mjs`: **9/9 PASS**. FilterBar, Checkbox/Progress asChild·기본 root·Form, 소비자 style, CENTER DOM props, 반복 popup/페이지 ID, readOnly/disabled 검색 보호의 기존 기능 유지 | +| 타입 검사 | `npm run typecheck` PASS: Theme defaults·패키지 build·root/UI/CENTER 타입 검사. 추가 문구 변경 후 영향받는 root `npm exec -- tsc -b` PASS | +| Storybook build | 최종 `npm exec -- storybook build -o storybook-static --disable-telemetry` PASS. 선행 패키지 build와 Theme defaults 검사는 성공 결과를 재사용. 기존 bundler 성능 안내는 남음 | +| 최종 정적 Storybook | 실제 React 렌더, 3개 그룹·24개 칩, 소개·그룹 문구 제거, 상세 설명 0개, 24개 Swatch/HEX/Default JSON 일치, 콘솔 error 0개 | +| 문서 정합성 | [브랜드 단계](../guidelines/brand-color-steps.md)와 [Runtime 계약](../guidelines/runtime-theme-contract.md)의 현재 표시 안내 갱신. 과거 보고서는 본문 결과를 보존하고 현재 표시 링크만 보완. 내부 문서 링크 검사 및 Git diff 확인 | + +최종 정적 빌드는 `http://127.0.0.1:6322/storybook/iframe.html?id=foundation-color--all&viewMode=story`에서 검사했다. 네 폭 모두 그룹 3개·칩 24개, 행별 칩 8개, 표시 HEX와 computed backgroundColor 및 Default JSON의 24개 값이 일치했다. 단계 순서 설명·개발용 설명·details는 0개다. + +| Viewport | Document clientWidth / scrollWidth | Palette row clientWidth / scrollWidth | 행 clientHeight / scrollHeight | +|---|---|---|---| +| 390 | 390 / 390 | 358 / 936 | 176 / 176 | +| 768 | 768 / 768 | 722 / 936 | 176 / 176 | +| 1280 | 1280 / 1280 | 1203 / 1203 | 156 / 156 | +| 1440 | 1440 / 1440 | 1200 / 1200 | 156 / 156 | + +측정 높이는 1100px이며 문서 clientHeight / scrollHeight는 모두 1100 / 1100이었다. 작은 화면은 기존 `overflow-x: auto` 팔레트 행 안에서 스크롤하며 문서 전체 가로 넘침은 없다. 390px 개발 미리보기에서 첫 행의 끝까지 실제 가로 스크롤하여 Dark 3 접근을 확인했다. 숨김 absolute 알림은 기존 공통 Toaster이며 변경하지 않았다. 실제 스크린리더·실기기 터치 및 모든 Component × viewport 전수 검증은 NOT VERIFIED다. + +## Guard 원본과 별도 Runtime 근거 + +`node scripts/ai-generation-guard.mjs --target . --profile STORYBOOK --scope PARTIAL --files src/foundation/color.stories.tsx --json`을 실행했다. 이후 사용자 추가 요청으로 Story가 바뀌어 해당 범위만 재검사했다. Storybook profile에는 Production 전용 `--result`를 전달하지 않는다. + +최종 원본 `status / staticStatus / runtimeStatus`: **NOT VERIFIED**, FAIL 0. TOKEN-002 5개는 StoryPage className, ColorChip 동적 style/property, 공유 helper의 Specimen 동적 값이다. CSS `styleEvidence`: **STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED**. 결합 `styleVerification`은 생성하지 않았다. 별도 실제 화면에서 ColorChip의 유한 후보 24개·현재 CSS 적용을 확인했지만 미실행 helper 위치나 원본 Guard 판정으로 확장하지 않는다. + +로컬 실행 증거는 `_local/foundation-color-cleanup-20261011/`에 저장했다: `guard-tests.log`, `guard-support-tests.log`, `component-tests.log`, `typecheck.log`, `typecheck-final.log`, `storybook-build-final.log`, `guard-final.json`, `runtime-final.json`, `color-final-{390,768,1280,1440}.png`. 첫 요청 상태의 Guard·Runtime 증거도 별도 파일로 보존했다. + +## 보존과 Git + +시작 SHA와 비교한 `themes/`, `packages/`, `scripts/`, `.storybook/`, Theme entry·Foundation CSS·Dashboard 소스 및 package/lock은 변경 없음. 실제 Palette Key·색상값·공통/전용 Gradient·Component API와 기능을 보존한다. 다른 Story를 추가·교체하지 않았다. + +관련 Story와 문서 6개만 Stage하고 Staged Diff를 검토한다. Commit/Push 직전 원격 main이 시작 SHA 그대로인지 확인하며 일반 Push를 수행한다. 최종 Commit SHA·Local/Remote 일치·Working Tree 및 작업 완료 시간은 Push 후 최종 응답에서 기록한다. + +Started At: 2026-10-11 15:10:48 KST. Estimated Duration: 최초 약 25분. 완료 시각·실제 소요·차이는 최종 상태 확인 뒤 기록한다. diff --git a/docs/reports/foundation-color-storybook-2026-10-11.md b/docs/reports/foundation-color-storybook-2026-10-11.md index 146e919..247e6e8 100644 --- a/docs/reports/foundation-color-storybook-2026-10-11.md +++ b/docs/reports/foundation-color-storybook-2026-10-11.md @@ -1,5 +1,7 @@ # Foundation Color 현행 Token 표시 확장 — 2026-10-11 +> 표시 확장 당시의 검증 기록이다. 이후 최종 요청으로 Foundation Color는 3개 브랜드 그룹·24개 컬러칩만 표시한다. 아래 당시 결과는 보존하며 현재 표시와 검증은 [표시 정리 보고서](foundation-color-display-cleanup-2026-10-11.md)를 따른다. + RESULT: PARTIAL. 요청한 표시 확장과 선택 검증은 완료했다. 현행 프로젝트에 없는 Generic Status Error와 Brown/Orange 공통 Background Gradient는 누락 상태로 표시한다. 기존 자료의 HEX·5-stop 구조를 새 Theme 정의로 만들지 않았다. Guard의 동적 값 미검증도 보존한다. ## 작업 기준과 범위 diff --git a/src/foundation/color.stories.tsx b/src/foundation/color.stories.tsx index 5dcfa01..0fb22f6 100644 --- a/src/foundation/color.stories.tsx +++ b/src/foundation/color.stories.tsx @@ -1,49 +1,21 @@ import type { Meta, StoryObj } from "@storybook/react-vite" -import { useEffect, useRef, useState } from "react" import { runtimeThemeContract, type RuntimeThemeToken } from "@hm-design/ui/theme" import { ReferencePage, ReferenceSection, useResolvedProperties } from "./reference" -// Orange Dark 1/2/3 use the latest user-confirmed values (2026-10-11). +// Keep the confirmed eight stages; presentation changes never alter Theme values. const stages = ["light-4", "light-3", "light-2", "light-1", "default", "dark-1", "dark-2", "dark-3"] as const const palettes = [ - { name: "한맥그린", prefix: "green", confirmed: [...stages], legacy: [] }, - { name: "한맥브라운", prefix: "brown", confirmed: [...stages], legacy: ["deep"] }, - { name: "한맥오렌지", prefix: "orange", confirmed: [...stages], legacy: ["light", "mid", "dark", "deep"] }, -] -const stageLabel = (stage: string) => stage === "default" ? "Default · 기본색" : stage.replaceAll("-", " ").replace(/^./, letter => letter.toUpperCase()) -const semanticGroups = [ - { name: "Input-family Outline", colors: [ - { name: "Input Default Outline", token: "color.stroke.input.default", status: "Confirmed", note: "Input / Textarea / Select / Search Form 기본·Disabled 윤곽. 다른 역할의 동일 HEX와 통합하지 않습니다." }, - { name: "Input Focus / Open Outline", token: "color.stroke.input.focus", status: "Provisional · 시험 적용", note: "현재 Light 2 시험 적용값. 과거 Green Default로 되돌리지 않으며 사용자 최종 확정 전입니다." }, - { name: "Input Error Outline", token: "color.action.destructive", property: "--hm-input-error-border-color", status: "Confirmed · 현행 mapping", legacy: "color.stroke.input.error", note: "현행 Input / Textarea / Select 오류 윤곽은 Action Destructive를 소비합니다. 독립 Input Error canonical은 공개 계약에 없습니다. Generic Status Error와 별도 역할입니다." }, - ] }, - { name: "Status", colors: [ - { name: "Generic Status Error", token: "color.status.error", status: "Provisional", note: "일반 상태 오류를 위한 독립 Semantic Token입니다. 사용 가능 상태만 승인되었으며 기존 Input / Alert / Toast / Badge 오류색과 Action Destructive mapping은 유지합니다. 실제 Component 적용은 별도 사용자 결정이 필요합니다." }, - ] }, - { name: "Action", colors: [ - { name: "Primary Action", token: "color.action.primary", status: "Confirmed", note: "확인·저장 등 주요 행동. Gradient 배경과는 별도 역할입니다." }, - ] }, - { name: "Background", colors: [ - { name: "Content Background", token: "color.bg.content", status: "Confirmed · Application Context", note: "업무 콘텐츠 배경. Marketing Context는 TBD입니다." }, - { name: "Surface Background", token: "color.bg.surface", status: "Confirmed", note: "Card / Panel / Input / Select / Textarea / Table 표면." }, - ] }, - { name: "Foreground", colors: [ - { name: "Primary Foreground", token: "color.fg.primary", status: "Confirmed", foreground: true, note: "본문·제목·입력값에 사용하는 주요 텍스트." }, - ] }, -] satisfies { name: string; colors: ColorReference[] }[] -type ColorReference = { name: string; token?: RuntimeThemeToken; property?: string; status: string; note: string; legacy?: string; foreground?: boolean } -const gradients = [ - { name: "HM Green Background", property: "--hm-sem-bg-brand", stopPrefix: undefined, legacy: "gradient.green.background", note: "현행 공통 브랜드 배경 · Confirmed. 공개 stop 계약은 color.bg.brand.stop.*이며 방향·위치는 현행 CSS에서 읽습니다. Dashboard의 Header / Compact 배경은 별도 정의입니다." }, - { name: "HM Brown Background", property: "--hm-sem-bg-brown", stopPrefix: "color.bg.brown.stop", legacy: "gradient.brown.background", note: "공통 Brown Background · Confirmed. 확정 Brown Palette를 참조하는 독립 5-stop 배경입니다. Runtime JSON은 5개 stop 전체를 지정하며 Button·Dashboard와 별도 역할입니다." }, - { name: "Orange Background", property: "--hm-sem-bg-orange", stopPrefix: "color.bg.orange.stop", legacy: "gradient.orange.background", note: "공통 Orange Background · Confirmed. 확정 Orange Palette를 참조하는 독립 5-stop 배경입니다. Runtime JSON은 5개 stop 전체를 지정하며 Button·Dashboard와 별도 역할입니다." }, -] -const colorProperties = [...new Set([ - ...Object.values(runtimeThemeContract).filter(definition => definition.type === "color").map(definition => definition.property), - "--hm-input-error-border-color", - ...gradients.map(gradient => gradient.property), -])] + { name: "한맥그린", prefix: "green" }, + { name: "한맥브라운", prefix: "brown" }, + { name: "한맥오렌지", prefix: "orange" }, +] as const +type ColorStage = typeof stages[number] +type PalettePrefix = typeof palettes[number]["prefix"] +const stageLabel = (stage: ColorStage) => stage === "default" ? "Default · 기본색" : stage.replaceAll("-", " ").replace(/^./, letter => letter.toUpperCase()) +const paletteToken = (prefix: PalettePrefix, stage: ColorStage) => `color.palette.${prefix}-${stage}` as RuntimeThemeToken +const colorProperties = palettes.flatMap(palette => stages.map(stage => runtimeThemeContract[paletteToken(palette.prefix, stage)].property)) -// Read the current CSS value, never the legacy document HEX. Keep unsupported values visible. +// Read the current CSS value rather than duplicating Theme HEX values. function colorHex(value: string | undefined) { if (value === undefined) return "확인 중" if (!value) return "미정의" @@ -51,91 +23,22 @@ function colorHex(value: string | undefined) { const rgb = value.match(/^rgb\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)\s*\)$/) return rgb ? `#${rgb.slice(1).map(channel => Number(channel).toString(16).padStart(2, "0")).join("").toUpperCase()}` : value } -function gradientParts(value: string | undefined) { - // Split only top-level commas: rgb()/color() channels must stay together. - if (!value?.startsWith("linear-gradient(") || !value.endsWith(")")) return undefined - let depth = 0 - const parts = value.slice("linear-gradient(".length, -1).split("").reduce((result, char) => { - if (char === "(") depth++ - if (char === ")") depth-- - if (char === "," && depth === 0) result.push("") - else result[result.length - 1] += char - return result - }, [""]).map(part => part.trim()) - const [direction, ...stops] = parts - const parsed = stops.map(stop => stop.match(/^(.*)\s+(-?[\d.]+%)$/)) - if (!parsed.length || parsed.some(stop => !stop)) return undefined - return { direction, stops: parsed.map(stop => ({ color: stop![1], position: stop![2] })) } -} -function ColorChip({ prefix, stage, confirmed = true, values }: { prefix: string; stage: string; confirmed?: boolean; values: Record }) { - const token = `color.palette.${prefix}-${stage}` as RuntimeThemeToken - const definition = confirmed ? runtimeThemeContract[token] : undefined - return
-
-

{stageLabel(stage)}

{definition ? colorHex(values[definition.property]) : "미확정"}

- {definition &&
{paletteStatus(stage)} · Token
Canonical Token
{token}
CSS Variable
{definition.property}
} +function ColorChip({ prefix, stage, values }: { prefix: PalettePrefix; stage: ColorStage; values: Record }) { + const token = paletteToken(prefix, stage) + const definition = runtimeThemeContract[token] + return
+
+

{stageLabel(stage)}

{colorHex(values[definition.property])}

} -const paletteStatus = (stage: string) => stages.includes(stage as typeof stages[number]) ? "Confirmed" : "Confirmed · 기존 Button 전용" -function SemanticColor({ item, values }: { item: ColorReference; values: Record }) { - const definition = item.token ? runtimeThemeContract[item.token] : undefined - const property = item.property ?? definition?.property - return
- {property &&
} -

{item.name}

{property ? colorHex(values[property]) : "미정의 · HEX 없음"}

-

{item.status}

{item.note}

- {item.foreground && property &&

한맥 프로젝트 · Hanmac Project · 0123456789

} -
Canonical Token
{item.token ?? "미정의"}
CSS Variable
{property ?? "미정의"}
- {item.property && definition && <>
현재 Semantic 연결
{definition.property}
} - {item.legacy && <>
기존 참고 명칭
{item.legacy}
} -
-
-} -function GradientPreview({ name, property, stopPrefix, values }: { name: string; property: string; stopPrefix?: string; values: Record }) { - const preview = useRef(null) - const [backgroundImage, setBackgroundImage] = useState() - useEffect(() => { - if (preview.current) setBackgroundImage(getComputedStyle(preview.current).backgroundImage) - }, [property, values]) - const value = values[property] - const parts = gradientParts(value) - return <> -
-
현재 CSS Variable · Derived Gradient
{property}
현재 Theme 적용 결과 · Computed CSS Variable
{value ?? "확인 중"}
실제 Computed background-image
{backgroundImage ?? "확인 중"}
방향
{parts?.direction ?? "NOT VERIFIED"}
- {parts ?
{parts.stops.map((stop, index) => { - const token = stopPrefix ? `${stopPrefix}.${stop.position.replace("%", "")}` as RuntimeThemeToken : undefined - const definition = token ? runtimeThemeContract[token] : undefined - return
-

{stop.position}

{colorHex(stop.color)}

- {definition &&
Canonical Token
{token}
CSS Variable
{definition.property}
} -
- })}
:

{value === undefined ? "Stop 확인 중" : "Stop 미정의 또는 해석 불가 · NOT VERIFIED"}

} - -} function ColorReferencePage() { const values = useResolvedProperties(colorProperties) - return - {palettes.map(palette => -
{stages.map(stage => )}
- {palette.legacy.length > 0 && <> -

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

-
{palette.legacy.map(stage => )}
- } + return + {palettes.map(palette => +
{stages.map(stage => )}
)} - -
-
- - {semanticGroups.map(group =>

{group.name}

{group.colors.map(item => )}
)} -
- -
{gradients.map(gradient =>
-

{gradient.name}

{gradient.note}

기존 참고 명칭: {gradient.legacy}

- -
)}
-
} const meta = { title: "Foundation/Color", parameters: { layout: "fullscreen" } } satisfies Meta