feat(sw): implement SW header design system

This commit is contained in:
shkim4 committed 2026-10-16 18:08:53 +09:00
1 parent 9407c8567f
commit fc26afd440
107 files changed
+40063 -1

No files matched your search

@@ -0,0 +1,47 @@
{
"mode": "baseline",
"browser": "155.0.8059.79",
"checks": [
{
"name": "no browser errors",
"status": "PASS",
"data": []
}
],
"errors": [],
"styles": {},
"storyIds": [
"sw-header--overview",
"sw-header--default",
"sw-header--controls",
"sw-header--interactive-states",
"sw-header--responsive",
"sw-header--window-move-motion",
"sw-header--composed-controls",
"sw-header--logo-slot",
"sw-header--document-dropdown",
"sw-header--layer-dropdown",
"sw-header--color-dropdown",
"sw-header--line-style-dropdown",
"sw-header--text-dimension-style-dropdown",
"sw-header--responsive-more",
"sw-header--text-stroke",
"sw-header--layer-detail-input",
"sw-header--dynamic-data",
"sw-header--property-panel",
"sw-header--property-panel-variants",
"sw-header--k-guide",
"sw-header--additional-hover",
"sw-header--property-bar",
"sw-header--dynamic-controls",
"sw-header--colorize",
"sw-header--composition",
"sw-header--property-panel-none",
"sw-header--property-panel-all",
"sw-header--property-panel-circle",
"sw-header--property-panel-expanded",
"sw-header--property-panel-compare",
"sw-header--property-shape-slot"
],
"status": "PASS"
}
@@ -0,0 +1 @@
["sw-header--overview","sw-header--default","sw-header--controls","sw-header--interactive-states","sw-header--responsive","sw-header--window-move-motion","sw-header--composed-controls","sw-header--logo-slot","sw-header--document-dropdown","sw-header--layer-dropdown","sw-header--color-dropdown","sw-header--line-style-dropdown","sw-header--text-dimension-style-dropdown","sw-header--responsive-more","sw-header--text-stroke","sw-header--layer-detail-input","sw-header--dynamic-data","sw-header--property-panel","sw-header--property-panel-variants","sw-header--k-guide","sw-header--additional-hover","sw-header--property-bar","sw-header--dynamic-controls","sw-header--colorize","sw-header--composition","sw-header--property-panel-none","sw-header--property-panel-all","sw-header--property-panel-circle","sw-header--property-panel-expanded","sw-header--property-panel-compare","sw-header--property-shape-slot"]
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,14 @@
{
"mode": "capture",
"browser": "155.0.8059.79",
"checks": [
{
"name": "no browser errors",
"status": "PASS",
"data": []
}
],
"errors": [],
"styles": {},
"status": "PASS"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

File diff suppressed because it is too large. Load diff
@@ -0,0 +1,114 @@
{
"mode": "preservation",
"browser": "155.0.8059.79",
"checks": [
{
"name": "required responsive width 390",
"status": "PASS",
"data": {
"height": 49,
"overflow": 0,
"documentOverflow": 0
}
},
{
"name": "required responsive width 768",
"status": "PASS",
"data": {
"height": 49,
"overflow": 0,
"documentOverflow": 0
}
},
{
"name": "required responsive width 1280",
"status": "PASS",
"data": {
"height": 49,
"overflow": 0,
"documentOverflow": 0
}
},
{
"name": "required responsive width 1440",
"status": "PASS",
"data": {
"height": 49,
"overflow": 0,
"documentOverflow": 0
}
},
{
"name": "preserve property panel none",
"status": "PASS",
"data": {
"x": 360,
"y": 204.5,
"width": 314,
"height": 256
}
},
{
"name": "preserve property panel all",
"status": "PASS",
"data": {
"x": 360,
"y": 204.5,
"width": 314,
"height": 361
}
},
{
"name": "preserve property panel circle",
"status": "PASS",
"data": {
"x": 360,
"y": 204.5,
"width": 314,
"height": 658
}
},
{
"name": "preserve property panel expanded",
"status": "PASS",
"data": {
"x": 360,
"y": 204.5,
"width": 314,
"height": 754
}
},
{
"name": "preserve window motion 1400ms",
"status": "PASS"
},
{
"name": "window motion leave clears animations",
"status": "PASS"
},
{
"name": "preserve K guide animation",
"status": "PASS"
},
{
"name": "K guide leave closes",
"status": "PASS"
},
{
"name": "preserve Colorize size",
"status": "PASS"
},
{
"name": "preserve editable Input",
"status": "PASS"
},
{
"name": "no browser errors",
"status": "PASS",
"data": []
}
],
"errors": [],
"styles": {},
"status": "PASS"
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,109 @@
# SW Header 추가 수정 검증 — 2026-10-16
현재 요청 A–H의 결과입니다. 기존 통합 작업/과거 PASS 기록을 덮어쓰지 않습니다. CAD 프로그램 통합이나 전체 HMDesign 프로젝트 승인 판정이 아닙니다.
1. **최종 판정: PASS — 요청한 추가 기능과 실행 검증 범위.** PASS-A~I의 기능·빌드·브라우저 조건을 확인했습니다. Guard 원본 NOT VERIFIED와 남은 디자인 차이는 21~22번에 별도로 기록합니다. 이 보고가 미실행 영역을 PASS로 바꾸지 않습니다.
2. **시작 Git 상태.** 절대 경로 `D:\디자인시스템\HMDesign-SW`, Branch `feature/sw-package`, HEAD `9407c8567f57301659e591c03451cca1b828733c`. 기존 상태는 `M package.json`, `M package-lock.json`, `?? packages/sw/`, `?? src/sw/`, `?? docs/sw/`였습니다. 사용자 지정 Worktree/Branch를 사용했고 기존 변경을 보존했습니다. 시작 시 패키지·Story·문서 복사본과 SHA-256을 QA 경로에 남겼습니다.
3. **재사용.** 기존 Dropdown 8종·id 기반 데이터·Color/Number parser·SWHeaderInput·선 패턴·문자/치수 SVG·레이어 상태 아이콘·문서 목록·More 대표 필드·로고 슬롯·창 제어를 사용했습니다. 다른 패키지 소스는 수정하지 않았습니다.
4. **상세 5개 Hover: PASS.** 배경 .15→.25, radius 24→25, Mouse Leave 복원, Hover Callback 없음, 기존 클릭 1회/닫기, focus-visible/disabled 보존. 치수 상세는 실제 Hover 26:10799(기본 아이콘·글자 100%)이며 Variant3를 혼합하지 않았습니다. 다른 4개는 원본 Hover 아이콘·글자 35%입니다.
5. **5개 버튼 크기: PASS.** 실제 브라우저 측정은 레이어 113×24 / 색상 99×24 / 선스타일 128×24 / 문자스타일 142×24 / 치수스타일 142×24px로 각 Figma 기준과 일치합니다. Noto Sans KR에서도 텍스트가 버튼 안에 잘리지 않는 것을 확인했습니다.
6. **ByLayer/ByBlock Hover: PASS.** 82.5×28px, 글자 #FF5C00, 검정 text-shadow 유지. 선택된 버튼에 Hover해도 #62533D 선택 배경과 On Inset Shadow가 남습니다. Mouse Leave에서 전경색 #D4CBBD로 돌아옵니다.
7. **필수 선택: PASS.** 초기 ByLayer, 재클릭 유지·중복 Callback 없음, 다른 모드로 전환, Controlled ByLayer/ByBlock/null 변경, 팔레트/사용자 색상 선택 후 정확히 하나 표시를 확인했습니다. null의 직접 색상 API 의미와 색상값은 유지하고 모드 표시만 마지막 유효 모드를 기억합니다.
8. **레이어 목록 Hover/Selected: PASS.** 갈색 배경·주황 이름을 동일 적용합니다. 다른 행 Hover와 Mouse Leave에서 선택을 유지하고, 실제 선택 Callback을 받은 호스트 데이터 변경으로 선택 강조가 이동합니다. 기존 전구/눈꽃/자물쇠 매핑은 그대로입니다. 제공되지 않은 별도 레이어 상세 편집 창은 만들지 않았습니다.
9. **선/문자/치수 Hover/Selected: PASS.** #62533D / #FF5C00, 선과 치수 미리보기도 동일 강조입니다. CENTER 22·7·22·7 및 HIDDEN 33·33의 원래 패턴, 문자 미리보기와 치수 원본 SVG를 유지했습니다. 상단 필드/문서 선택 정책은 변경하지 않았습니다.
10. **속성창 4개 상태: PASS.** SWHeaderPropertyPanel 하나가 외부 selection/properties/groups/expandedGroupIds에 따라 바뀝니다. Story fixture 측정: 선택 없음 314×256 / 전체 선택 314×361 / 원 1개 314×658 / 모두 펼침 314×754px. 객체 개수·수치·예시 텍스트는 패키지에 고정하지 않았습니다. Dim 없는 non-modal 도구창입니다.
11. **동적 입력/Callback: PASS.** Text/Number/Select/Color/Slider/Computed, id/group/order, validation, unit/annotation, ReadOnly/Disabled, 외부 값 변경, 항목 추가/삭제, 선택 객체 교체, 옵션 삭제를 지원합니다. Enter/Blur 확정·Escape 취소·변경 없는 중복 보고 방지를 확인했습니다. 삭제된 항목이나 이전 선택의 draft는 폐기합니다. 펼침이 속성값을 바꾸지 않으며, 원형 그래픽의 추가 일반 항목도 위치 영역과 겹치지 않습니다. CAD 계산/객체 변경은 호스트 책임입니다.
12. **내부 아이콘: PASS.** Default→Hover→Active→다시 Default 및 호스트 Callback을 확인했습니다. 29×27.13px wrapper, 원본 SVG/Gradient/Active 금색 테두리 사용. Figma SVG export root가 29×28로 반올림되어 root 크기는 변경하지 않았습니다. 기존 CAD 액션 계약은 없어 toolActive/onToolActiveChange/toolLabel을 통해 호스트에 상태만 통지합니다.
13. **닫기 버튼: PASS.** 원본 44×30 SVG의 Default/빨강 Hover·흰 X·클릭 닫기를 확인했습니다. Escape와 속성창 버튼 재클릭도 닫으며, Desktop은 원래 속성창 버튼, Mobile은 More로 Focus를 돌립니다. Header 종료 Callback과 분리했습니다.
14. **K 가이드: PASS.** 기존 40×38 K 버튼 하나를 재사용합니다. 아래 208×149 가이드, 원본 이미지/마스크, 녹색 배경·금색 경계·문구, 511ms 표시 전환, 버튼→팝업 이동 시 Hover 유지, Hover 종료 닫기를 확인했습니다. 기존 640px Breakpoint 아래에서는 기존 More 항목에 같은 가이드를 연결합니다.
15. **K 이미지 반복: PASS.** 단일 원본 이미지의 CSS transform을 연속 보간해 Pan/Zoom합니다. 400ms 지연/1022ms 전환 → 100ms 지연/1022ms 전환 → 150ms 지연/511ms 전환, 마지막 화면 400ms 유지 후 511ms 복귀로 반복합니다. 브라우저 시간 샘플에서 4개 상태·다음 반복과 15개 이상의 중간 transform을 확인했습니다. 재진입 초기화·타이머 정리·Reduced Motion 첫 프레임 정지·More Escape 후 재열기 시 잔상 없음도 확인했습니다.
16. **Text Stroke Width: PASS.** 요청한 일반 선언과 `--hm-sw-header-text-stroke-width` 변수는 제거했습니다. 일반 자기 색 Stroke 중복 선언도 없습니다. 검정 text-shadow·상세 pill의 별도 solid-stroke-width·SVG/Border/선 Stroke는 유지했습니다.
17. **반응형/접근성: PASS(실행 범위).** 1920/1440/1080/900/768/640/390px에서 Header 49px, Header 가로 넘침 0, 속성창/K 가이드 viewport 경계, 내부 스크롤을 확인했습니다. 기존 Breakpoint는 그대로입니다. 실제 label/control id·접근성 이름·dialog 연결·키보드 Enter/Escape·Focus 복귀·ReadOnly/Disabled를 확인했습니다. 직접 Windows 입력기/보조기기 검증은 22번을 따릅니다.
18. **기존 Story/회귀: PASS.** 기존 17개 export를 유지하고 Property Panel / Property Panel Variants / K Guide / Additional Hover 4개를 추가하여 빌드 index 21개입니다. 모든 Story 렌더와 적용되는 Header 높이를 확인했습니다. 추가로 문서 1개·New/Open·색상 입력 검증·사용자 색상 2개 및 Callback·선 스케일/패턴·레이어 3개 Toggle·대표 More 이동을 확인했습니다. 기존 모든 상호작용의 가능한 조합을 전수 검사했다는 뜻은 아닙니다.
19. **빌드/검증: PASS.** 실제 package.json의 `npm.cmd run typecheck:sw`, `build:sw`, Root `typecheck`, Root `build`, `build-storybook` 최종 실행이 모두 exit 0입니다. 통합 브라우저 189/189, 보완 브라우저/소스 보존 45/45, 사용자 색상·More 최종 경계 별도 PASS, pageerror 0. SW 출력은 styles.css 약 82.48kB / index.js 약 674.64kB이며 원본 PNG가 data URI로 포함됩니다. Root의 기존 유형 Tailwind `bg-[var(...)]` 최적화 경고와 500kB chunk 경고, Storybook 번들 크기/시간 안내는 남습니다. 별도 lint 명령은 package.json에 없고 실행하지 않았습니다.
20. **변경 파일.** 시작 baseline 대비 기존 17개 수정 + 새 구현/자산 17개 추가, 이 검증 보고서 1개 추가 = 소스/문서 35개입니다. 기존 root package.json/lock 변경은 이번 작업에서 수정하지 않았습니다. 아래 목록은 생성 dist를 제외합니다.
```text
MOD packages/sw/README.md
MOD packages/sw/src/assets.d.ts
ADD packages/sw/src/assets/k-guide-mask-default.svg
ADD packages/sw/src/assets/k-guide-mask-fit.svg
ADD packages/sw/src/assets/k-guide-mask-pan.svg
ADD packages/sw/src/assets/k-guide.png
ADD packages/sw/src/assets/layer-detail-hover.svg
ADD packages/sw/src/assets/property-circle.svg
ADD packages/sw/src/assets/property-close-hover.svg
ADD packages/sw/src/assets/property-close.svg
ADD packages/sw/src/assets/property-expand.svg
ADD packages/sw/src/assets/property-footer.svg
ADD packages/sw/src/assets/property-title-single.svg
ADD packages/sw/src/assets/property-title.svg
ADD packages/sw/src/assets/property-tool-active.svg
ADD packages/sw/src/assets/property-tool-hover.svg
ADD packages/sw/src/assets/property-tool.svg
MOD packages/sw/src/components/sw-header/index.ts
MOD packages/sw/src/components/sw-header/SWHeader.tsx
MOD packages/sw/src/components/sw-header/types.ts
MOD packages/sw/src/components/sw-header/components/SWHeaderActions.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderColorMenu.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderDropdown.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderInput.tsx
ADD packages/sw/src/components/sw-header/components/SWHeaderKGuide.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderLineStyleMenu.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderMore.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderProperties.tsx
ADD packages/sw/src/components/sw-header/components/SWHeaderPropertyPanel.tsx
MOD packages/sw/src/theme/sw.css
MOD src/sw/sw-header.stories.tsx
MOD src/sw/sw-stories.css
MOD docs/sw/sw-header-design-spec.md
MOD docs/sw/sw-header-usage.md
ADD docs/sw/sw-header-additional-verification-2026-10-16.md
```
21. **Figma와 남은 차이.** 기존 SW의 Noto Sans KR(400/500/700)을 유지해 Dotum 원본과 글자 폭/획/베이스라인은 다릅니다. 폰트 준비 완료 후 선스타일 전체 폭 473.1875px, 첫 열 187.1875px로 Figma 468/182보다 +5.1875px입니다(기존 content 폭 정책 보존). 5개 상세 버튼과 4개 속성창의 외곽 크기는 기준과 일치합니다. 투명도 thumb는 호스트 min/max/value에 비례하므로 Figma 예시의 장식 위치와 다를 수 있으며 임의 수치 변환을 넣지 않았습니다. 내부 도구 SVG root의 28px export 반올림도 12번과 같습니다. 반복 복귀 전환은 Figma 원본에 없어 요청에 따라 추가한 구현값입니다.
22. **NOT VERIFIED.** Guard 원본 status/staticStatus는 NOT VERIFIED, FAIL 0 / 미검증 issue 28입니다. 독립 SW CSS import 해석(ENTRY-002), Story 호스트 native button(REUSE-004), SW token 연결/동적 class/style 해석(TOKEN-001/002)이 남습니다. styleEvidence는 STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED이며 원본 JSON은 변경하지 않았습니다. 실제 브라우저 결과는 별도 증거입니다. HM UI와 독립된 SW 패키지여서 native label/select/range를 사용한 근거는 명세에 기록했습니다. 모든 픽셀/모든 애니메이션 프레임의 자동 이미지 diff, Figma 프로토타입 직접 재생 시각 동기 비교, 실제 Windows 한글 IME, screen reader·색 대비 전수 감사, 실제 CAD·파일/OS 창 동작·운영 배포는 미검증입니다.
23. **종료 Git 상태.** Branch/HEAD는 시작과 같은 `feature/sw-package` / `9407c8567f57301659e591c03451cca1b828733c`. 상태 범주는 기존과 동일한 root 2개 M 및 SW 3개 폴더 Untracked입니다. git diff --check 통과. Stage/Commit/Push/PR/Merge/Publish/배포를 수행하지 않았습니다. 기존 문서/홈/창 제어/레이어 아이콘/필드 소스, 기존 자산 30개, 과거 검증 로그 5개, 창 이동 1400ms CSS 블록은 baseline과 바이트 단위로 동일합니다. 사용자 검토/승인을 기다립니다.
## 증거 위치
QA root: `C:\Users\user\.codex\visualizations\2026\10\16\01a14374-df22-7311-831d-2d3a4c522f95\sw-header`
- `baseline/`, `baseline-sha256.json`, `git-start.txt`: 시작 보존본.
- `figma/`: 직접 확인한 Figma 디자인 context.
- `verify.mjs` / `browser-results.json`: 통합 189개.
- `followup.mjs` / `followup-results.json`: 보완 45개 및 원본 보존.
- `palette-results.json`, `final-edge-results.json`, `line-font-ready.json`: 사용자 색상/More/폰트 준비 후 폭.
- `final-panel-none/all/circle/expanded.png`, `verified-k-guide.png`: 실제 React 렌더.
- `guard.json`: 원본 Guard 판정(덮어쓰지 않음).
- `sw-typecheck-final.log`, `sw-build-final.log`, `root-typecheck-final.log`, `root-build-final.log`, `storybook-build-final.log`: 최종 명령 로그.
- `changed-files.json`: baseline 대비 파일 목록(보고서 추가 전 수집).
## 시간 기록 (Asia/Seoul)
- Started At: 2026-10-16 15:45:05 KST
- Completed At: 2026-10-16 16:17:40 KST
- Actual Duration: 32분 35초
- Estimated Duration: 약 90~130분
- Variance: 예상보다 57분 25초~97분 25초 짧음
@@ -0,0 +1,55 @@
# SW Header 3차 — 구성요소 표시·메뉴 순서·속성창 4상태·형상 Slot 검증
이번 요청의 구현·실행 범위 기록입니다. 이전 검증 문서는 수정하지 않았습니다. Commit/Push/PR/배포는 하지 않았습니다.
## 1. 판정
**PASS(요청 범위의 실행 검증)** — PASS-A~J. 미검증 항목은 7절에 분리했습니다.
## 2. Git
- 시작·종료 동일: `D:\디자인시스템\HMDesign-SW`, `feature/sw-package`, HEAD `9407c8567f57301659e591c03451cca1b828733c`.
- 상태: `M package.json`, `M package-lock.json`(작업 전부터 존재, 이번에 수정하지 않음), `?? docs/sw/`, `?? packages/sw/`, `?? src/sw/`(SW 작업 전체가 아직 미커밋).
## 3. 구현 요약
| 요구 | 구현 |
|---|---|
| 로고 외 개별 제거 | `SWHeader.hiddenControls` (`SWHeaderHideableControlId` = 기존 `SWHeaderControlId` + `home`/`windowMove`/`minimize`/`close`) |
| 로고 필수 | 로고는 대상 id가 아님. `home` 제거 시 `SWHeaderHome interactive={false}` 정적 로고 |
| 유사 메뉴 순서 | `SWHeader.menuOrder` (5개 메뉴끼리만 자리 교환, 지정 우선·누락은 기본 순서·중복/미지 id 무시) |
| More 연동 | 기존 Breakpoint·대표 슬롯 재사용. 객체 영역 요소 제거 시 스크립트가 More/대표 결정, 빈 More 미생성 |
| 속성창 4상태 | 기존 `SWHeaderPropertyPanel` + 데이터. 개별 Story 4개 + 비교 Story 1개 |
| 형상 Slot | `shapeContent` (undefined=Figma 원형, null/false=빈 영역, ReactNode, Render Prop) |
재사용: 기존 `controls` 필터·More·대표 슬롯 로직, `SWHeaderCommitPresence`(제거 시 draft Callback 방지), SWHeaderInput, Property 타입·Fixture, 하위 공개 컴포넌트(선택적 show Props만 추가, 기본 true).
## 4. 실행 검증 (Playwright Chromium + Storybook dev 6024, dist 재빌드 후)
- **기본 디자인 보존:** 변경 전/후 `Default`(1920 Frame)·`Controls`(viewport) Story의 Header 하위 98개 요소 위치·크기·display·visibility·color·background를 1920/1800/1440/1180/1080/900/768/640/520/390px에서 비교 — **차이 0**.
- **구성 11종 × 7폭(1920~390) = 77건 PASS:** 기본 전체, 구성 A/B/C, 파일 목록·레이어·색상·스타일 그룹 제거, 여러 메뉴 제거, 순서 변경, 숨김+순서 변경. 각 건에서 Header 49px, 로고 32×17 표시, 제거 요소 DOM 부재, DOM 메뉴 순서 = 기대 순서, Header 밖 노출/가로 스크롤 없음, More 열었을 때 빈 목록 없음, 제거 요소가 More에 없음.
- 최초 실행 3건 FAIL(여러 메뉴 제거 @900/768/640): 최소화 제거로 짧아진 창 영역 밖으로 투명한 창 이동 Tooltip이 넘침 → `data-reduced` 창 영역 clip + 24px 이동 영역 Tooltip 좌측 표시로 수정 후 재실행 0 FAIL.
- **동작 25건 PASS:** 열린 Dropdown 숨김 시 제거, 재표시 값 유지·닫힘, 순서 반전 렌더링·Callback 없음·값 유지, 재정렬 후 Dropdown 열기·Escape Focus 복귀·Tab 순서, 중복/알 수 없는 id 처리, More→문자스타일 대표 슬롯, 대표 메뉴 제거 시 레이어 복귀, 레이어 제거 시 색상이 대표, More 제외/재표시 접근, 속성창 숨김 시 Popup 닫힘·Callback 없음, 정적 로고(클릭 Callback 없음)·홈 재표시 Callback, 로고만 구성(More·창 영역 없음), 콘솔 오류 0.
- 1건 기록: "색상 숨김 Callback 없음" 검사에서 `onColorMenuOpenChange(false)`가 기록됨. 체크박스 클릭이 Dropdown 바깥 클릭이 되어 **숨기기 전에** 기존 바깥 클릭 닫힘 계약이 실행된 것이며, 숨김 자체가 만든 Callback이 아님(코드상 제거는 내부 상태만 정리).
- **속성창 24건 PASS:** 개별 Story 실측 314×256 / 314×361 / 314×658 / 314×754(Figma와 일치), 내용 잘림 없음. Enter 확정·Escape 취소·잘못된 숫자 거부. 기본 형상 영역 208×208 @(39,26), 그룹 260px. 비교 Story 4개 동시 렌더·실측 표시, 1440/768/390px 줄바꿈·페이지 가로 스크롤 없음. 형상 Slot 기본/비움/외부 컴포넌트/교체/Render Prop context, 비움·교체 시 패널 높이·라벨 위치 동일, 형상 그룹 항목 추가·삭제 반영. Header 내 속성창 열기·닫기 및 `onPropertyPanelOpenChange`·`onPropertyPanelToggle` 보존.
- 자동 검사 1건(외부 지름·반지름 변경)은 측정이 재렌더 전에 실행된 타이밍 문제였고, Browser pane에서 재확인해 입력값 10.0000 / 5.0000 반영을 확인.
- **Header 내 속성창 경계:** 1920/1440/1080/900/768/640/390px에서 패널이 Viewport 안(좁은 폭은 기존 More 경유).
- **Story 회귀:** 기존 24개 + 신규 7개 = 31개 전부 렌더, page/console 오류 0.
## 5. 빌드 (순차, exit 0)
`npm run typecheck:sw` → `build:sw` → `typecheck`(root, workspaces) → `build`(root) → `build-storybook`(31개 SW Story 포함). 경고는 기존 Tailwind `bg-[var(...)]` CSS 최적화 경고와 chunk 크기 경고뿐이며 신규 경고 없음.
## 6. Figma와 남은 차이
- 형상 라벨 x 위치 1~2px(원주 113 vs 111.5, 지름 100 vs 102, 반지름 154 vs 152.5, 면적 100 vs 98.5)와 지름 y 110 vs 108은 기존 구현값을 유지(이번 범위에서 변경 안 함).
- 로고만 구성에서 녹색 객체 영역 배경은 Header 표면으로 유지(구현 판단).
- 부분 제거 시 상세보기 블록·창 영역 폭은 Figma에 없는 구현값(auto).
- `26:11338` 색상 돋보기 Overlay는 요청대로 미구현(호스트 연동 대상).
## 7. NOT VERIFIED
- 실제 CAD 호스트 앱 연동, 실제 형상 렌더러 연동.
- 스크린리더 등 보조기기, Firefox/Safari, 터치 기기.
- 창 이동 Tooltip의 1080px 이상 Hover 애니메이션 중 축소된 창 영역에서의 잘림 정도(시각 확인 안 함).
- Figma 픽셀 오버레이 비교(크기·위치 수치 비교만 수행).
@@ -0,0 +1,213 @@
# SW Header 추가 디자인 5건 — 최종 검증 기록 (2026-10-16)
## 1. 판정
**PASS — 이번 요청의 수정 및 아래 명시된 Chrome 실행 검증 범위.** 전체 HMDesign, CAD 호스트 연동, 모든 브라우저 또는 Figma 전체 픽셀 일치에 대한 승인은 아니다. Guard의 `NOT VERIFIED`는 별도로 유지한다.
작업 범위는 기존 SW 패키지와 기존 Storybook 예제(BOTH)다. 신규 업무 화면·Navigation·Story는 추가하지 않았다. Commit / Push / PR / Merge / Publish / 배포는 하지 않았다.
## 2. 시작 Branch / HEAD / Git Status
- 경로: `D:\디자인시스템\HMDesign-SW`
- Branch: `feature/sw-package`
- HEAD: `9407c8567f57301659e591c03451cca1b828733c`
- origin: `https://gitea.hmac.kr/b25027/HMDesign.git`
- 기존 상태: `M package.json`, `M package-lock.json`, `?? docs/sw/`, `?? packages/sw/`, `?? src/sw/`.
- 이번 요청에 명시된 작업 경로·브랜치·Commit/Push 금지가 일반 main/공식 작업 폴더 지침보다 우선한다. 원격 동기화나 Git 이력 변경은 하지 않았다.
## 3. 이전 작업 이어서 완료한 내용과 보존 근거
기존 구현을 유지하고 5건의 원인에 필요한 부분만 수정했다. 시작 상태의 `packages/sw`, `src/sw`, `docs/sw`, root manifest/lock을 별도 QA baseline에 복사하고 소스·문서 SHA-256을 기록했다.
- 기존 `hiddenControls`, `menuOrder`, More 및 대표 슬롯, 속성Bar, 속성창 데이터·입력·Callback·형상 Slot 구현을 재사용했다.
- 기존 `docs/sw` 문서 10개(명세·사용법·검증 기록 포함), 기존 SVG/이미지, `package.json`, `package-lock.json`은 시작 baseline과 바이트가 같다.
- `SWHeader.tsx`, PropertyPanel/PropertyBar, ColorMenu, KGuide, WindowControls, 공개 export 등 나머지 소스는 수정하지 않았다. 공개 Props의 이름·타입·Callback 시그니처도 그대로다(`types.ts`는 설명 주석만 수정).
- 이전 Motion·Colorize·ByLayer/ByBlock·Text Stroke 정책은 소스 보존 근거다. 이번에 Motion 전체 프레임이나 CAD 동작을 새로 검증한 것으로 표현하지 않는다.
- 다른 디자인 시스템 패키지 소스, DWG/DXF Viewer, 기존 `_local` 파일은 수정하지 않았다. 빌드 산출물은 각 기존 빌드 명령으로 재생성했다.
## 4. 추가 수정 5건 결과
- **A PASS:** 선스타일 메뉴와 Dropdown의 실제 접촉 구간 1px만 검정색으로 수정.
- **B PASS:** 치수스타일 고정 Story 두 개만 오른쪽 끝을 필드와 일치시킴. 동적 위치 계산은 수정하지 않음.
- **C PASS:** 속성창 헤더의 원본 대각선·그림자를 가리던 레이어 순서 복원, 도구 버튼 위치와 제목 자간 보정. 네 상태의 디자인 차이 유지.
- **D PASS:** Figma Hover `26:10799`를 재확인하고 치수 전용 Hover 규칙을 명시. 호스트 상세 Callback 없는 경우에도 Hover 가능하게 수정.
- **E PASS:** Callback 부재와 Disabled를 분리. 일반 레이어 상세 Hover 및 명시적 Disabled 예제를 구분하고 기존 Callback 유지.
## 5. 선스타일 연결 영역 검정색
원인은 `.hm-sw-header__line-menu::before`의 노란 Gradient ring이 필드와 접하는 상단에도 그려지는 것이었다. 기존 ring은 유지하고 기존 `::after`를 접촉 구간에만 배치했다. 색은 실제 선언된 `--hm-sw-header-layer-menu-border-color`(`#000`)를 재사용한다.
공통 Dropdown의 `panelStyle.left`에서 접촉 시작점을 구하고 필드의 실제 폭을 ResizeObserver로 추적한다. More에서 필드가 축소되어도 284px을 강제로 칠하지 않는다. 공통 Dropdown Position/Align은 수정하지 않았다.
브라우저 결과:
- Expanded 접촉 구간의 PNG RGB 실측: `0,0,0`.
- 상단의 접촉하지 않는 좌우 부분·좌우/하단 테두리: 기존 노란색 유지.
- 접촉 구간 아래 전체 Dropdown PNG 비교: 변경 픽셀 **0**. 선 Preview, 항목 정렬, Hover/Selected 행도 그대로다.
- Default → Hover → Mouse Leave의 필드 색상·ring 복귀와 Selected 행 강조를 확인했다. 제목/값의 기존 노란색을 유지한다.
- 1920/1440/1280/768/390px에서 검정 구간이 실제 필드 폭 밖으로 나가지 않음을 확인했다.
## 6. 치수스타일 고정 Story 위치
대상은 `치수스타일 Expanded (Figma 26:11242)`와 `치수스타일 행·상세 버튼 Hover (고정)`뿐이다.
원인은 1920px 고정 Header를 좁은 Story scroller에서 렌더링할 때 공통 경계 보정이 실제 화면 폭을 기준으로 목록을 왼쪽으로 옮기는 것이었다. 두 `MenuFrame`에만 `fixedDimension`을 지정하고 Story CSS로 `left:auto; right:0`을 적용했다. 고정 상태의 ring도 동일한 필드 기준으로 맞췄다.
- 1440px 화면의 수정 전 두 예제: 필드 오른쪽 `1641px`, Dropdown 오른쪽 `1439px` — **202px 어긋남**.
- 수정 후 두 예제: 필드/Dropdown 오른쪽 모두 `1641px` — **차이 0px**.
- Figma 필드 폭 164px, 목록 폭 164px, 필드 하단에서 목록 시작이라는 상대 위치를 유지한다.
- 1920px specimen을 가로 스크롤한 뒤에도 필드와 목록 오른쪽 끝이 일치함을 확인했다. 화면 폭에 맞춰 고정 Header 자체를 축소하지 않았다.
## 7. 동적 Dropdown 위치 불변
`useSWHeaderDropdown` 및 Text/Dimension의 공통 Position/Align을 수정하지 않았다. `선택 Callback`의 동적 치수 Dropdown 수정 전후 실측:
- 1440px 화면: 필드 `x=1156, width=164`, Dropdown `x=1156, width=164`, 오른쪽 모두 `1320` — 전후 동일.
- 2000px 화면: 필드/Dropdown `x=1037, width=164`, 오른쪽 `1201` — 전후 동일.
- 고정 Story 전용 CSS는 동적 예제에 적용되지 않는다.
## 8. 속성창 네 상태 헤더 Figma 비교
[Figma 속성창 원본](https://www.figma.com/design/rj6wpsfk24lOWvWiVRAANP/이지빔-SW?node-id=26-11822&m=dev)을 get_design_context(스크린샷 포함)와 읽기 전용 Plugin API로 직접 확인했다. 원본 디자인은 수정하지 않았다.
- `26:11886` 선택 없음: **314×256px**, 녹색 Gradient Union, 추가 도구 아이콘 있음.
- `26:11823` 전체 11: **314×361px**, 녹색 Gradient Union, 추가 도구 아이콘 있음.
- `26:11932` 원 1개: **314×658px**, 갈색 Union(`#211D13`), 추가 도구 아이콘 없음.
- `26:12035` 모두 펼침: **314×754px**, 원 1개와 같은 갈색 헤더, 추가 도구 아이콘 없음.
- **닫기 버튼은 Figma 네 상태 모두 있다.** 단일 원 상태의 닫기 기능을 제거하지 않았다.
원본 Union SVG를 새로 내려받아 기존 파일과 SHA-256을 비교했으며 동일했다. 디자인 차이는 잘못된 자산이 아니라 닫기 버튼 배경이 Union 오른쪽 대각선을 덮는 paint order였다. Union을 해당 배경 위에 배치하고 제목·도구 버튼은 그 위에 유지했다. Union은 `pointer-events:none`이어서 닫기 버튼을 차단하지 않는다.
공통 실측(패널 좌상단 기준):
- 헤더 내부 영역 `(1,1)`, **312×30px**. 외곽 금색 1px·radius 4px 및 본문 경계/그림자 보존.
- 제목 `x=8, y=8`, 14px, 기존 Noto Sans KR·700 유지, Figma 자간 **0.56px(0.04em)** 적용.
- 추가 도구 버튼 `(241,2)`, **29×27.125px**(Figma 27.129px의 브라우저 반올림). SVG root 29×28은 보존.
- 닫기 `(269,1)`, **44×30px**. 원본 Default/Hover 자산과 클릭 기능 유지.
- 본문/전체 높이 네 상태 모두 시작 baseline과 동일.
네 패널 PNG를 직접 검토했다. 제목과 도구 클릭, 상단 X/키보드 Escape 닫기, Focus 복귀, Enter 확정·Escape 취소 및 Property Callback을 Chrome에서 확인했다. 형상 Slot·4개 상태 비교 Story도 유지했다.
## 9. 치수스타일 상세 Hover
Figma `26:10796` Default와 `26:10799` Hover를 직접 확인했다. `26:10802` Variant3는 혼합하지 않는다.
- Default: **142×24px**, 배경 `rgba(197,177,158,.15)`, radius 24px.
- Hover: 배경 `rgba(197,177,158,.25)`, radius 25px, **기본 아이콘·문자 opacity 1**.
- Mouse Leave: Default 배경·radius 복귀.
- Callback이 있는 동적 예제는 시작 시점에도 위 Hover 값과 일치했다. 이를 다른 상세 버튼/Variant3와 같게 바꾸지 않았다. 치수 전용 규칙을 명시하고, Callback 없는 Dynamic Data에서 자동 disabled가 되던 경로를 제거했다.
- Callback 없는 경우 Hover는 동작하지만 CAD 상세 기능을 만들어 실행하지 않는다. Callback이 있는 클릭은 기존 `onDimensionStyleDetail` **1회** 후 닫힘을 확인했다.
- 색상/선/문자 상세 버튼의 기존 규칙은 수정하지 않았다.
## 10. 레이어 상세 Hover 및 Disabled 원인
실제 원인은 `disabled={!onDetail}`였다. Hover CSS는 존재했지만 `:hover:not(:disabled)`에서 제외됐다. pointer-events나 상위 컨테이너가 원인은 아니었다.
- 일반 상태는 Callback 없어도 활성 Hover. 기본 크기 **113×24px** 유지(3배 Story 실측 339×72).
- Default .15/radius 24 → Hover .25/radius 25 → Mouse Leave Default 복귀.
- Hover 아이콘·글자 35%는 Figma `26:10771`대로 유지.
- 기존 메뉴의 명시적 `disabled` 계약은 유지한다. Story의 Disabled specimen은 이제 호스트가 실제 `fieldset disabled`를 명시한다. Callback 부재를 Disabled 예시로 사용하지 않는다.
- Callback 연결 예제에서 `onLayerDetail` 1회 후 목록 닫힘 확인. 연결되지 않은 CAD 기능·상세창은 추가하지 않았다.
## 11. 기본 디자인 10개 폭 비교
수정 전/후 Default(1920px Frame)와 Controls(viewport)의 Header 및 하위 DOM 총 **99개(루트 1 + 하위 98)**를 비교했다.
- 폭: **1920,1800,1440,1180,1080,900,768,640,520,390px**.
- 2개 Story × 10폭 = **20건**, 위치·크기·display·visibility·color·background 차이 **0**.
- 후속 좁은 화면 보정은 열린 선스타일 Dropdown의 접촉선 폭만 변경한다. 닫힌 기본 Header에는 적용되지 않으며 해당 5개 폭을 별도 재검증했다.
## 12. 메뉴 구성 및 기능 검증
**11개 구성 × 7폭(1920/1440/1080/900/768/640/390) = 77건 PASS.** 기본 전체, A/B/C, 파일·레이어·색상·스타일 그룹·여러 메뉴 제거, 순서 변경, 숨김+순서 변경.
각 구성의 로고 32×17 유지, Header 49px, DOM 메뉴 순서, 가로 overflow 없음, More 목록이 비지 않음, 숨긴 메뉴가 More에 남지 않음을 확인했다.
추가 실행: More→문자스타일 대표 슬롯, 대표 제거→레이어 복귀, 메뉴 숨김/재표시, Dropdown 바깥 클릭, 속성창 열기·닫기/Focus 복귀, 상세 Callback, 형상 Slot 기본/비움/외부 컴포넌트/교체/Render Prop, 형상 속성 추가·삭제/외부 값 반영. 4개 패널 비교는 1440/1280/768/390px에서 가로 페이지 overflow 없이 표시됐다.
Outside Click과 React 타이밍 구분:
- Checkbox를 실제 포인터로 클릭하면 기존 Dropdown의 바깥 pointerdown이 먼저 닫힘 Callback을 발생시킨다. 별도로 checkbox의 DOM click만 호출해 숨길 때는 합성 닫힘 Callback이 없음을 확인했다. 실제 기능 오류로 분류하지 않는다.
- 형상 지름·반지름 외부 변경은 React 렌더링 완료 조건을 기다린 뒤 `10.0000 / 5.0000`을 확인했다. 재렌더 이전 즉시 읽기를 성공/실패 근거로 쓰지 않았다.
- 테스트의 잘못된 Story ID와 다른 예제 로그 선택을 수정했다. 해당 실패는 제품 코드 오류가 아니며 수정된 검사와 오류를 분리한 재실행 결과를 보존한다.
## 13. Storybook 전체 렌더링
기존 **31개 Story** 유지. 실제 Chrome에서 31/31 렌더링 및 보이는 이미지 로딩 확인. 잘못된 Story ID에서 발생한 오류를 분리하기 위한 독립 재실행에서 **page error 0 / console error 0 / HTTP error 0**.
Story를 새로 추가하거나 기존 대표 Story를 교체하지 않았다.
## 14. SW / Root Typecheck 및 Build
최종 소스 기준 다음 명령 모두 **exit 0**:
- `npm.cmd run typecheck:sw`
- `npm.cmd run build:sw`
- `npm.cmd run typecheck` (Root·workspaces)
- `npm.cmd run build`
- `npm.cmd run build-storybook`
실제 소스가 다시 변경된 연결선 보정 이후 영향을 받는 검증을 재실행했다. 조건이 바뀌지 않은 77개 구성·기본 Geometry 검사는 반복하지 않았다. CSS 최적화의 기존 Tailwind arbitrary value 경고와 chunk 크기 경고는 남아 있다. 빌드 실패는 없다.
## 15. 이번 변경 파일
시작 baseline 대비 수정 **7개**, 새 보고서 **1개**:
1. `packages/sw/src/components/sw-header/components/SWHeaderLayerMenu.tsx` — Callback 없는 레이어 상세 자동 Disabled 제거.
2. `packages/sw/src/components/sw-header/components/SWHeaderLineStyleMenu.tsx` — 접촉선 시작점·실제 필드 폭 전달/추적.
3. `packages/sw/src/components/sw-header/components/SWHeaderStyleListMenus.tsx` — 치수 상세 Callback 없는 Hover 허용.
4. `packages/sw/src/components/sw-header/types.ts` — 위 Callback의 설명 주석만 정정.
5. `packages/sw/src/theme/sw.css` — 접촉선, 치수 전용 Hover, 속성창 paint order/도구 위치/제목 자간.
6. `src/sw/sw-header.stories.tsx` — 고정 치수 두 예제 범위 지정, 레이어의 명시적 Disabled 예제.
7. `src/sw/sw-stories.css` — 위 고정 Story와 Disabled specimen 컨테이너 스타일.
8. 이 검증 문서.
미커밋 SW 폴더 전체를 이번에 새로 작성한 것으로 집계하지 않는다. root manifest/lock의 기존 변경도 이번 변경 파일에 포함하지 않는다. 시작 baseline 대비 실제 diff를 읽고 수정 범위를 검토했다.
## 16. 남은 디자인 차이
- 기존 정책의 **Noto Sans KR**(Figma Dotum 대신)·400/500/700을 유지했다. 제목을 포함한 글리프 모양·문자 폭은 Figma와 완전 동일하지 않다.
- 기존 선스타일 열의 Noto 내용 폭, 형상 라벨 위치 등의 과거 문서상 차이는 이번 범위 밖으로 유지한다. 본문·형상 위치를 임의 변경하지 않았다.
- Figma 픽셀 전체 일치로 주장하지 않는다. 이번에 PNG의 변경 픽셀 0을 확인한 범위는 선스타일 접촉선 이외 Dropdown 보존 비교다.
## 17. NOT VERIFIED 및 Guard
Guard는 Storybook PARTIAL로 변경 파일을 검사하고, 마지막 연결선 소스 변경 후 해당 두 파일만 PARTIAL 재검사했다. **FAIL 0**, 원본 `status/staticStatus/runtimeStatus`는 **NOT VERIFIED**다. 이를 브라우저 PASS로 덮어쓰지 않았다.
원인: Guard가 독립 `@hm-design/sw/styles.css` 및 SW 패키지 구현 graph를 HM UI 소비 화면과 같이 해석하지 못하는 ENTRY/SCOPE 항목, Storybook 예제의 native controls·동적 class/style·token 근거 항목. SW build와 실제 컴포넌트 렌더링은 별도 실행 증거다.
CSS 원본 근거도 그대로 유지: **STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED**. 내부 SW CSS를 모두 검사해 승인한 결과가 아니며 `checks=[]`인 정적 scope 한계가 있다. 이번 수정의 색·크기·상태는 위 개별 Figma/브라우저 검증으로만 판단한다.
이번 미검증: 실제 CAD 호스트/형상 렌더러, Firefox/Safari, 터치, 스크린리더, 전체 화면 픽셀 overlay, 기존 K/창 이동 Motion 전체 프레임 재검사. 과거 검증 기록은 보존한다.
## 18. 종료 Branch / HEAD / Git Status
- Branch: `feature/sw-package`
- HEAD: `9407c8567f57301659e591c03451cca1b828733c` (시작과 동일).
- 상태: `M package.json`, `M package-lock.json`, `?? docs/sw/`, `?? packages/sw/`, `?? src/sw/`.
- Staged Diff 없음. 기존 파일 삭제/초기화 없음. Commit / Push / PR / Merge / Publish / 배포 없음.
## 실행 근거 및 재현
QA Root: `C:\Users\user\.codex\visualizations\2026\10\16\01a143c9-30e2-7c42-aa06-c658d456a984\sw-header`.
- `baseline/`, `baseline-sha256.json`, `git-start.txt`: 복구·변경 전 근거.
- `before/capture.json`, `after/capture.json`: 10폭 Geometry 및 네 패널 실측.
- `before/probe.json`, `after/probe.json`: 고정/동적 치수 위치와 Hover.
- `verification.json`: 최초 통합 검사(테스트 수정 전 실패 기록 포함).
- `verification-followup.json`, `verification-A.json`, `verification-E.json`: 연결선·레이어의 영향 범위 재검사.
- `verification-all SW.json`: 실제 31개 Story의 독립 오류 검사.
- `edges-results.json`: 닫힌 선스타일 상태, 5개 폭 접촉선, 고정 Story 스크롤 검증(8 PASS / 0 FAIL).
- `build-results-final.json`, Typecheck/Build `.log`: 최종 5개 명령 exit 0.
- `guard.json`, `guard-final-affected.json`: 덮어쓰지 않은 원본 NOT VERIFIED.
- `panel-{none,all,circle,expanded}-final.png`, `line-seam-final.png`, `dimension-detail-hover.png`, `layer-detail-hover.png`, `fixed-dimension-scrolled.png`: Chrome 캡처.
재현 스크립트 `capture.mjs`, `probe.mjs`, `verify.mjs`, `edges.mjs`, `builds.mjs`는 QA Root에 있다. 이번 작업에서는 로컬 Storybook `127.0.0.1:6043`과 기존 Playwright/Chrome을 사용했다. 재현 시 같은 소스·dist 및 서버 포트를 확인한다.
## 시간 기록 (Asia/Seoul)
- Started At: 2026-10-16 17:17:10 KST
- Completed At: 2026-10-16 17:40:21 KST
- Actual Duration: 23분 11초
- Estimated Duration: 약 30~50분 (분석 5~10 / 수정 15~25 / 통합 검증 10~15분)
- Variance: 최초 예상 범위보다 6분 49초~26분 49초 짧음
+681
View File
@@ -0,0 +1,681 @@
# SW Header 디자인 명세
> **상세 버튼 공통화 기준(2026-10-16, 이번 요청):** 상세 5개는 기존 `SWHeaderMenuDetail`을 공유하며 **현재 문자스타일 상세**를 우선합니다. Default는 배경 `rgba(197,177,158,.15)`·radius 24·글자 35%·아이콘 SVG 20%, Hover는 배경 .25·radius 25·글자 35%·아이콘 SVG 35%입니다. Disabled는 Default 외형과 클릭 차단을 사용합니다. Callback이 없어도 Hover되며 클릭은 무동작입니다. 치수 상세의 글자 100% Hover와 Callback 부재에 따른 비활성 설명은 이 기준으로 대체합니다. 기존 폭 113/99/128/142/142px·높이 24px, 제거된 Text Stroke 선언을 유지합니다. 과거 검증 문서는 당시 기록으로 보존합니다. [이번 검증 기록](./sw-header-detail-buttons-verification-2026-10-16.md).
> **3차 기준(2026-10-16):** 문서 끝 [3차 기준](#3차-기준--구성요소-표시메뉴-순서속성창-상태형상-slot-2026-10-16)이 구성요소 표시·메뉴 순서·속성창 상태·형상 Slot에 우선합니다. [3차 검증 기록](./sw-header-composition-shape-verification-2026-10-16.md).
> **후속 5개 요구사항(2026-10-16):** 이 문서 끝의 후속 기준이 속성Bar·메뉴 구성·Colorize·Text Stroke 비교 정리에 우선합니다. 이전 `solid-stroke-width` 보존 설명은 역사적 기록이며 최신 구현에는 적용하지 않습니다. [새 검증 기록](./sw-header-followup-five-verification-2026-10-16.md).
> **현재 추가 기준:** 이 문서의 과거 Hover 없음·선택 글자만 노랑·일반 자기 색 Stroke 내용보다 아래 [추가 수정 기준](#추가-수정-기준--속성창k-가이드-2026-10-16)이 우선합니다. 과거 검증 기록은 당시 결과로 보존합니다.
> 상태: `@hm-design/sw` 0.1.0 구현 기준(2026-10-16). 코드: `packages/sw/src/components/sw-header/`, CSS: `packages/sw/src/theme/sw.css`, Story: `SW / Header`.
> 값 표기: **확인** = Figma MCP(`get_metadata`·`get_design_context`) 또는 사용자 확정 CSS에서 직접 확인, **구현값** = Figma에 없어 구현에서 정한 값, **추정** = 확인 근거 없음.
## 1. 개요
SW Header는 여러 설치형 SW에서 공통으로 쓰는 49px 헤더입니다(Figma 기준 화면은 이지빔 SW). 로고 / 파일명 / 객체 수정 영역 / 창 영역의 4개 면으로 구성됩니다. `@hm-design/ui`와 독립된 `@hm-design/sw` 패키지에서 관리하며, SW 전용 토큰(`--hm-sw-header-*`)만 사용합니다.
우선순위: ① 사용자 확정 CSS·선택·반응형 정책 → ② Figma 원본 → ③ 기존 DWG/DXF 뷰어 구현(`D:\디자인시스템\테스트\dwg-dxf-viewer-hm`) → ④ HMDesign 공통 규칙(패키지·타입·Storybook·접근성).
## 2. Figma 원본
| 항목 | 값 |
|---|---|
| File | `rj6wpsfk24lOWvWiVRAANP` (이지빔 SW) |
| Node | `26:8944` `header_green` (instance) |
| 크기 | 1920 × 49 (내부 면 높이 48) — 확인 |
| 접근 | Figma MCP로 metadata·design context·screenshot 확인(2026-10-16) |
## 3. 레이어 구성
표시 레이어(확인). 같은 내용의 `header` frame이 두 번 겹쳐 있으며(`26:10337`, `26:10372`), 두 번째 frame에만 `Rectangle 6671`(x0, 310 × 48, 파일명 면과 같은 gradient)이 홈 뒤에 깔립니다. 구현은 한 번만 렌더링하고 Rectangle 6671은 홈 영역 0–65px의 배경으로 재현합니다.
| Figma 레이어 | x / y | W × H | 구현 컴포넌트 |
|---|---|---|---|
| `header/btn_home` | 0 / 0 | 80 × 48 | `SWHeaderHome` |
| `header/document_tit` | 65 / 0 | 310 × 48 | `SWHeaderDocumentTitle` (문서 Dropdown 포함, §5.1) |
| `Rectangle 7428` (객체 수정 면) | 377 / 0 | 1301 × 48 | `.hm-sw-header__object-area` |
| `Frame 427319899` 그리기모드 | 377 / 5 | 90 × 38 | `.hm-sw-header__mode` |
| `header/menu/레이어` | 467 / 5 | 234 × 38 | `SWHeaderLayerControl` (`layers` 전달 시 `SWHeaderLayerMenu`, 레이어 Dropdown §5.2) |
| `header/menu/색상` | 705 / 5 | 192 × 38 | `SWHeaderColorControl` |
| `header/menu/선스타일` | 901 / 5 | 284 × 38 | `SWHeaderLineStyle` |
| `header/menu/문자스타일` | 1189 / 5 | 164 × 38 | `SWHeaderTextStyle` |
| `header/menu/치수스타일` | 1357 / 5 | 164 × 38 | `SWHeaderDimensionStyle` |
| `Frame 427319953` 상세보기 | 1521 / 6 | 157 × 38 | `SWHeaderProperties` |
| `Rectangle 6669` 창 영역 | 1680 / 0 | 240 × 48 | `.hm-sw-header__window` |
| `Group 2913` 인쇄·가이드 pill | 1683 / 3 | 85 × 42 | `SWHeaderActions` |
| `창이동 motion` | 1768 / 0 | 88 × 48 | `SWHeaderWindowControls` |
| `header/btn_최소최대화`, `btn_종료` | 1856 / 0 | 32 × 48 각 | `SWHeaderWindowControls` |
숨김 레이어(확인, 구현 제외): `Ellipse`, `Component 761`, `Component 762`, `창이동`(65 × 48), `Component 724/mouse`.
Figma `get_design_context`에는 드롭다운 행(`HeaderDropdownBrownRow`), `ByLayer/ByBlock`, `Colorize`, 상세 pill(`레이어 상세` 등), GIF 가이드 카드도 포함되어 있으나 헤더에 표시되지 않는 하위 메뉴·팝업 구성요소입니다. 최초 범위(헤더 1개)에서는 구현하지 않았습니다. 이 중 레이어 Dropdown(`HeaderDropdownBrownRow`, `레이어 상세` pill)은 2026-10-16 추가 요청으로 구현했습니다(§5.2). 색상·선스타일·문자스타일·치수스타일 Dropdown과 ByLayer/ByBlock·Colorize·상세 pill·Input은 같은 날 후속 요청으로 구현했습니다(§5.3–§5.5). 각 `… 상세` 팝업의 내부 화면은 Figma 전달 전이라 범위 밖입니다.
## 4. 전체 레이아웃
`flex` 한 줄, 면 사이 2px. 1920px에서 Figma 좌표와 동일합니다(브라우저 측정, §15).
```
[홈 80(-17 겹침)] [파일명 310] 2 [객체 수정 1301: 그리기모드 90 | 레이어 234 · 색상 192 · 선 284 · 문자 164 · 치수 164 (gap 4) | 상세보기 157] 2 [창 240: pill 85 · 창 이동 88 · 최소화 32 · 종료 32]
```
- 홈은 80px 박스(overflow clip)이고 오른쪽 15px이 파일명 면 위에 겹칩니다. 파일명 글자는 x=70에서 시작하며 홈 박스의 투명 부분으로 보입니다.
- 객체 수정 면 안의 그리기모드 셀과 상세보기 블록은 Figma처럼 면의 바깥 테두리 위치(377 / 1678)에 맞춥니다(1px 음수 margin).
- 내부 필드 높이 38, 위 5px(테두리 1 + padding 4).
## 5. 컴포넌트별 내부
| 영역 | 구성(확인) |
|---|---|
| 홈 | 배경 SVG 92 × 72(위치 −8, −12, 그림자 포함 영역) + 로고 영역 32 × 17(위치 12, 16, Figma `bi_egbim` frame). 로고 이미지는 §9.1 |
| 파일명 | 글자 왼쪽 5px, 화살표 7 × 4(`#47AF91`, 오른쪽 12px), 글자–화살표 15px. 클릭 시 문서 Dropdown(§5.1) |
| 레이어 | 아이콘 3개(20 × 20, gap 0) · gap 8 · 라벨/값 |
| 색상 | 칩 60 × 20(검정 바탕, 안쪽 58 × 18 흰 1px 테두리, 기본 `#FFE500`) · gap 12 · 라벨/값 |
| 선스타일 | 선 미리보기 10 × 4 × 5(gap 2, 흰 바탕·검정 1px) · gap 12 · 선스타일 / 가중치(38) / 스케일(38), gap 12, 가운데 정렬 |
| 문자스타일 | 글꼴명 16px 700 · gap 12 · 라벨/값 |
| 치수스타일 | 치수 아이콘 60 × 15 · gap 12 · 라벨/값 |
| 상세보기 | 위 19px: `상세보기` 12px / 아래 19px: `속성Bar` · 구분선 1 × 12(검정, `1px 0 2px rgba(255,255,255,.25)`) · `속성창` |
| 인쇄·가이드 | pill 85 × 42(`rgba(0,0,0,.8)`, 1px `#092920`, radius 4, inset `0 2px 2px rgba(0,0,0,.25)`), 버튼 40 × 38 두 개 gap 1. 인쇄 버튼은 파일명 gradient·1px `#08251C`·radius 2 |
| 창 이동 | 2px 줄무늬 4px 간격(`#1C6A51` 32%, x3–85, y6, 높이 37) + 아이콘 32 × 30(위 13) |
| 최소화 | 10 × 2 막대 `#ADB0AF` 가운데 |
| 종료 | SVG 32 × 48 |
### 5.1 문서 Dropdown (Figma `header/document_tit` 26:10662)
Figma의 `hover` Variant는 목록이 펼쳐진 모습을 정의합니다. 구현은 **클릭으로 열고 닫으며** 마우스 Hover만으로는 열지 않습니다. 파일명 Hover는 글자만 `#FFC600`으로 바꾸며 노란 테두리는 표시하지 않습니다(§11.1).
| 항목 | Figma(확인) | 구현 |
|---|---|---|
| 열린 파일명 | 글자 `#FFC600`, 화살표 노란색 위쪽(`Polygon 353`) | 같음. 화살표는 CSS 삼각형을 180° 회전 |
| 위치 | 파일명(310 × 48) 바로 아래, `right: 0` | 헤더 아래로 겹치는 Overlay(헤더 49px 유지). 1920에서 바깥 박스 x 63–375(Figma와 같음). 좁은 폭에서도 화면 안에 있도록 왼쪽 기준 정렬 + `max-width: 100cqw − 71px` |
| 크기 | 항목 310 + 테두리 1px × 2, 예시 4개 = 목록 128 + 하단 40 | 바깥 312 × 170(4개). 목록 최대 320px 후 내부 세로 스크롤 |
| 배경 · 테두리 | `#103127`, 1px `#041611`, 하단 radius 4, `0 2px 4px rgba(0,0,0,.25)` | 같음 |
| 안쪽 그림자 | 목록·하단 영역 각각 `inset 0 4px 8px rgba(0,0,0,.4)` | 같음 |
| 문서 항목 | 310 × 32, padding 8, gap 8, 아이콘 29 × 12(`icon_dualmonitor`), 14px `#F0F7F5` | 같음(글자 700, 말줄임). Hover: Figma `header/document_tit/list`(26:10680) 그대로 검정 40% 배경 오버레이(`#103127` 위 합성), 글자·아이콘 변화 없음 |
| 하단 | 높이 40, padding 8, 위 구분선 `rgba(194,217,210,.2)` | 같음 |
| New / Open | `+ New` 54 × 24, `Open` 47 × 24, 배경 `rgba(182,208,201,.2)`, radius 2, padding 5 / 6, 14px 검정 70%, 양 끝 정렬 | 같은 값. Noto Sans KR로 55.4 / 50px(글자를 자르지 않음). Hover: Figma 26:10762 / 26:10805 그대로 배경 `rgba(182,208,201,.3)`, 글자 검정 100%. 크기·padding·radius 변화 없음 |
- 데이터 없음: `documents=[]`이면 목록 없이 하단만 표시합니다. `documents`를 전달하지 않으면 Dropdown 없이 기존 파일명 버튼입니다(하위 호환).
- 패키지에는 문서명·파일 형식을 넣지 않습니다(Figma 예시 `Drawing1.grm`은 Storybook 테스트 데이터로도 쓰지 않음). 아이콘은 기본값으로 Figma `icon_dualmonitor`를 쓰고 항목별(`icon`)·전체(`documentIcon`)로 바꿀 수 있습니다.
- 동작: 클릭/Enter/Space로 열고 닫기, ↓로 열면서 첫 항목 Focus, ↓↑ 이동, Escape·바깥 클릭으로 닫기(Focus는 파일명으로 복귀), Tab으로 목록 밖으로 나가면 닫기(Focus 갇힘 없음). 파일명 버튼은 `aria-expanded` / `aria-controls`로 목록(`role="group"`, `aria-label="문서 목록"`)과 연결됩니다.
#### 5.1.1 현재 문서 · 문서 닫기 (사용자 추가 요구, Figma에 없음)
2026-10-16 사용자 최종 지시. 위 Figma 확인 항목과 구분해 기록합니다.
| 항목 | 정책 |
|---|---|
| 목록 순서 | `documents` 배열 순서를 그대로 표시합니다. 현재 문서를 앞으로 옮기지 않으며, 다른 문서를 선택해도 순서·위치가 바뀌지 않습니다. |
| 현재 문서(`activeDocumentId`) | 문서명 글자만 `#FFC600`. 나머지 문서는 `#F0F7F5`. 테두리·배경·아이콘 변화 없음(다색 아이콘은 레이어 상태 아이콘과 같은 이유로 유지). 버튼에 `aria-current="true"` |
| Hover와 선택 | Hover는 배경(검정 40%, Figma 26:10680), 선택은 글자색이라 동시에 적용돼도 구분됩니다. |
| X(문서 닫기) | `onDocumentClose`를 주면 **모든 행** 오른쪽 끝(24 × 24, 오른쪽 4px)에 X가 있습니다. 평상시 숨김(opacity 0, 자리는 항상 확보)이며, **마우스를 올린 행** 또는 **키보드 Focus가 있는 행**에만 표시됩니다(선택 여부와 무관). Dropdown이 아니라 그 문서를 닫는 버튼이며 이름은 `문서 닫기`, 툴팁은 `<문서명> 닫기` |
| X 모양 | 8 × 8 선 아이콘, `rgba(240,247,245,.7)`. X 자체 Hover: 배경 `rgba(182,208,201,.2)`, 아이콘 `#F0F7F5`(구현값). 노란 테두리 없음 |
| 배치 | 행 높이 32px 유지. 문서명은 X 자리 앞에서 말줄임(1920에서 문서명 오른쪽 끝 338 < X 시작 346). X 표시·숨김으로 아이콘·문서명이 움직이지 않음 |
| 동작 | X는 선택 버튼과 형제 버튼이라 `onDocumentSelect`가 함께 호출되지 않습니다. X 후 Dropdown은 열린 채로 두고 Focus는 목록 첫 항목으로 옮깁니다(행이 사라져도 Focus 유실 없음). 실제 문서 닫기·목록 갱신·다음 선택 문서 결정은 호스트가 합니다. |
- 접근성 확인 필요: Figma New / Open 글자색(검정 70%)은 어두운 배경에서 대비가 낮습니다. Figma대로 구현했으며 변경 여부는 디자인 결정 사항입니다.
### 5.2 레이어 Dropdown (Figma `header/menu/레이어` 26:10940, 2026-10-16)
Figma 기준 5개 Node: ① `header/menu/레이어` 26:10940(default 26:10941 / hover 26:10952 / Variant3 = 펼침 26:10963) ② `icon_전구` 26:10810 ③ `icon_눈꽃` 26:10855 ④ `icon_자물쇠` 26:10912(각 20 × 20, Variant 4개) ⑤ `header/dropdown/brown/row` 26:10687(Default 26:10688 / Hover 26:10695). 모두 Figma MCP `get_design_context`·screenshot으로 확인했습니다.
`layers`를 전달하면 레이어 필드가 Dropdown 버튼이 됩니다(`SWHeaderLayerMenu`). 전달하지 않으면 기존 필드 버튼(`onLayerOpen`)과 DOM·외형이 같습니다(하위 호환). 필드 Hover는 기존 노란 Gradient Border + 글자 `#FFC600`이며 **클릭으로만 열립니다**.
| 항목 | Figma(확인) | 구현 |
|---|---|---|
| 필드 | 234 × 38, radius 3, 기존 gradient·그림자, 아이콘 3개 + `레이어` 라벨/값 | 기존 필드 그대로. 1920에서 x 467(브라우저 466.7) |
| 펼친 필드(Variant3) | 테두리 `#081813`, 위쪽만 radius 3, 라벨·값 `#FFC600` | 같음(`data-expanded`, `aria-expanded`) |
| 펼침 외곽선 | `#A67D03`(=노란 테두리 토큰) 1px, 236 × 320, 위치 −1 / −1, radius 4 | 기존 `--hm-sw-header-hover-border-gradient`로 필드+목록 바깥 1px 링. 크기 변화 없음 |
| 목록 위치 | 필드 바로 아래, 같은 왼쪽 | 필드 아래 Overlay(`top: 100%`), 헤더 49px·다른 필드 위치 불변 |
| 목록 | 234 × 280(행 30 × 8 + 하단 40), `#27241D`, 검정 1px(inside), 하단 radius 3, `0 4px 8px rgba(16,48,38,.25)` | 같음. 테두리는 overlay라 행이 234 × 30 그대로 |
| 행 | 234 × 30, padding 12, 아이콘 20 × 3(gap 0) · 8 · 견본 16 · 8 · 이름(14, `#F6F4F2`) | 같음(이름 Noto Sans KR 14 / 700 / 0.04em, 말줄임). 1920 측정: 아이콘 x+12, 견본 x+80, 이름 x+104(+1 padding) |
| 색상 견본 | 16 × 16 검정 radius 2, 안쪽 1px 위치 흰색 + 1px `#C9C9C9` radius 1 | 같음. 안쪽 색은 `layer.color`(기본 `#FFFFFF`) |
| 행 Hover | 배경 `#62533D`, 글자 `#FF5C00` | 같음. 이 목록 전용(다른 컨트롤의 노란 Hover와 별개) |
| 아이콘 Hover | 전구: 검정 25% 배경 radius 2 / 눈꽃·자물쇠: Hover SVG(같은 아이콘 + 검정 25% 타일) | 같음. 눈꽃·자물쇠는 Figma Hover SVG를 그대로 겹쳐 표시 |
| Off → Off Hover | Smart Animate, ease-out 300ms | `transition: 300ms ease-out`(Off만). On Hover는 즉시. Reduced Motion 시 전환 없음 |
| 하단 | 높이 40, padding 8 / 12, 1px `rgba(197,177,158,.15)`(inside), 오른쪽 정렬 | 같음 |
| 레이어 상세 | pill radius 24, padding 5 / 10, gap 4, 배경 `rgba(197,177,158,.15)`, 아이콘 14(`ico_layer`), 글자 14 흰색 opacity .35 | 같음. 글자 폭 Noto 69.3px(Figma Dotum 75px), 버튼 107.3 × 24 |
| 목록 높이 상한 | 예시 8행 | **구현값** 240px(8행). 넘치면 목록만 세로 스크롤, 하단 고정 |
#### 5.2.1 아이콘 On/Off와 데이터 의미
| 아이콘 | 컬러(Default/Variant2) | 회색(Variant3/Variant4) | 데이터 |
|---|---|---|---|
| 전구 | `visible !== false` | `visible === false` | `onLayerVisibilityChange(layer, nextVisible)` |
| 눈꽃 | `frozen !== true` | `frozen === true` | `onLayerFreezeChange(layer, nextFrozen)` |
| 자물쇠 | `locked !== true` | `locked === true` | `onLayerLockChange(layer, nextLocked)` |
- **확정 매핑(2026-10-16 사용자 확정)**: 전구 컬러 `visible=true` / 회색 `visible=false`, 눈꽃 컬러 `frozen=false`(동결 해제) / 회색 `frozen=true`(동결), 자물쇠 컬러 `locked=false`(잠금 해제) / 회색 `locked=true`(잠금). 클릭하면 해당 boolean의 반전값을 Callback으로 전달합니다(전구 `!visible`, 눈꽃 `!frozen`, 자물쇠 `!locked`). Figma 원본 SVG와 Default/Hover 디자인은 그대로입니다. 구현 위치는 `layerIconOn()`(`SWHeaderLayerIcons.tsx`) 한 곳입니다.
- `aria-pressed`는 아이콘 색이 아닌 데이터 의미(표시됨 / 동결됨 / 잠김)를 따릅니다. 이름은 `<레이어명> 표시|동결|잠금`.
- 아이콘은 데이터만 반영합니다. 클릭 시 Callback으로 다음 값을 알리고, 실제 변경·도면 반영은 호스트가 `layers`를 갱신해 수행합니다. Hover는 CSS이며 데이터를 바꾸지 않습니다.
- Callback이 없는 아이콘은 버튼이 아닌 상태 표시(`role="img"`, `<이름> 표시 켜짐/꺼짐`)이며 Hover·클릭 반응이 없습니다.
- 필드의 아이콘 3개는 현재 레이어(`activeLayerId`) 상태를 표시합니다(필드 안 아이콘은 장식, 버튼 아님). 현재 레이어가 없으면 모두 컬러(기존과 같음).
#### 5.2.2 선택 · 이벤트 · 키보드
| 동작 | 결과 |
|---|---|
| 필드 클릭 / Enter / Space | 열기·닫기(`onLayerOpen`도 매 클릭 호출, `onLayerMenuOpenChange`) |
| 행 선택(견본·이름 버튼) | `onLayerSelect` 1회 → 목록 닫힘, Focus는 필드로 복귀(문서 Dropdown과 같음) |
| 전구 / 눈꽃 / 자물쇠 | 해당 Callback 1회, `onLayerSelect` 호출 없음, 목록 유지, Focus는 아이콘에 유지 |
| 레이어 상세 | `onLayerDetail` 1회 후 목록 닫힘. Callback이 없으면 Figma 외형 그대로 `disabled`(추가 흐림 없음), 임의 팝업 없음 |
| 바깥 pointerdown · Escape · Tab으로 벗어남 | 닫힘(Escape는 필드로 Focus 복귀). 다른 메뉴(문서·더보기)를 누르면 바깥 클릭으로 닫혀 Overlay가 겹치지 않음 |
| ↓ (닫힘) | 열고 현재 레이어(없으면 첫 행) 이름 버튼에 Focus |
| ↓ ↑ (열림) | 행 이동, 같은 열(전구·눈꽃·자물쇠·이름) 유지, 하단 버튼 포함 순환 |
| ← → | 행 안에서 아이콘·이름 사이 이동 |
- 행은 `<li>` 안에 아이콘 버튼 3개와 선택 버튼 1개를 형제로 둡니다(중첩 버튼 없음). 따라서 아이콘 클릭이 행 선택으로 전파되지 않습니다.
- 현재 레이어(**사용자 규칙 확장, Figma 없음**): 문서 Dropdown과 같이 목록 순서를 유지하고 이름만 `#FFC600`(Hover 중에도 유지, 배경만 갈색). 버튼에 `aria-current="true"`.
- `disabled` 레이어는 선택·아이콘 버튼 모두 비활성(기존 `opacity .4`), 행 Hover 없음.
- 데이터 없음: `layers=[]`이면 목록 없이 하단(레이어 상세)만 표시하고 필드는 `layer.name`을 표시합니다.
#### 5.2.3 반응형
목록은 234px를 유지하고 필드 왼쪽에 맞춰 엽니다. 헤더(또는 화면) 오른쪽을 넘으면 넘는 만큼 왼쪽으로 이동하고(1px 여백), 이때 노란 외곽선은 목록만 감쌉니다(`data-shifted`). 520 미만의 필드 아이콘 숨김·레이어명 말줄임 등 기존 단계는 그대로입니다. 측정 결과는 [레이어 Dropdown 검증 기록](./sw-header-layer-dropdown-verification-2026-10-16.md)에 있습니다.
### 5.3 색상 · 선스타일 · 문자스타일 · 치수스타일 Dropdown (2026-10-16)
Figma 8개 노드(모두 MCP `get_design_context` + Plugin API로 확인): ① `header/menu/색상` 26:10986(Default 26:10987 / hover 26:10996 / 펼침 26:11005) ② `header/menu/색상_addUserColor` 26:11036(26:11037 / 26:11046 / 26:11055) ③ `ByLayer` 26:12666(Default 26:12667 / hover 26:12669 / on 26:12671) ④ `ByBlock` 26:12659(26:12660 / 26:12662 / 26:12664) ⑤ `header/menu/선스타일` 26:11104(26:11105 / 26:11124 / 26:11143) ⑥ `header/menu/문자스타일` 26:11181(26:11182 / 26:11191 / 26:11200) ⑦ `header/menu/치수스타일` 26:11215(26:11216 / 26:11229 / 26:11242) ⑧ `header/dropdown/brown/row` 26:10702(Default 26:10703 / Hover 26:10706). 보조: 행 Variant(26:10713 / 26:10748 / 26:10755 / 26:10718 / 26:10729), `input` 26:11656, `header/menu/색상/input` 26:11308, `btn_상세/*` 26:10774 / 26:10781 / 26:10788 / 26:10795, `btn_colorize` 26:11302.
데이터 Props(`colors`, `lineTypes`/`lineWeights`, `textStyles`, `dimensionStyles`)를 주면 각 필드가 Dropdown 버튼이 됩니다(`SWHeaderColorMenu`, `SWHeaderLineStyleMenu`, `SWHeaderTextStyleMenu`, `SWHeaderDimensionStyleMenu`). 주지 않으면 기존 필드 버튼과 외형·동작이 같습니다(하위 호환). 동작 계약은 레이어 Dropdown(§5.2.2)과 같습니다: 클릭/Enter/Space로 열고 닫기(Hover로 열지 않음), ↓로 열며 현재 항목(없으면 첫 항목) Focus, ↓↑ 이동, Escape·바깥 pointerdown·Tab 이탈로 닫기, 한 번에 하나만 열림, 헤더 49px·다른 필드 위치 불변, 목록은 헤더 밖으로 넘치면 안쪽으로 이동.
| 항목 | Figma(확인) | 구현 |
|---|---|---|
| 펼친 필드(Variant3) | 테두리 `#081813`, 위쪽만 radius 3, 라벨·값 `#FFC600`, 선/글꼴/치수 미리보기 `#FFC600` | 같음(`data-expanded`). 노란 링: 색상·문자·치수는 필드+목록 1px(194 × 198 / 166 × 140), 선스타일은 필드 링 286 × 39 + drop_wrap 링 |
| 공통 면 | `#27241D`, 검정 1px(inside), 하단 radius 3 | 같음(레이어 Dropdown 토큰 재사용, 테두리는 overlay) |
| 색상 목록 | 192 × 158(사용자 색상 2개 192 × 182), top 38, padding-top 10, gap 10, 가운데 정렬 | 같음. 브라우저 측정 ByLayer 그룹 (12.5, 10) 167 × 30, 팔레트 (12, 50) 168 × 22, 도구 (15, 82) 162 × 26, 하단 y 118 × 40 |
| ByLayer / ByBlock | 각 82.5 × 28, 검정 167 × 30 타일(p 1, radius 4). Default `rgba(98,83,61,.3)` + inner shadow(ByLayer `2px -2px 4px` · `-5px 2px 4px`, ByBlock `-2px -2px 4px` · `6px 2px 4px`, `rgba(0,0,0,.6)`), on `#62533D` + `1px 2px 2px rgba(217,207,187,.25)` · `-1px -2px 2px rgba(0,0,0,.4)`, 글자 `#D4CBBD` | 같음. inner shadow는 Figma처럼 글자 위 overlay. `aria-pressed`, 상호 배타 표시는 `colorMode` |
| 색상 칩 | 22 × 22: 검정 20 radius 2 + 18 견본, 1px `#C9C9C9`, radius 1. 기본 7색 `space-between`, 사용자 색상 gap 2 | 같음. 사용자 색상은 줄바꿈, 팔레트+4행(118px) 초과 시 팔레트만 세로 스크롤(구현값). 현재 색과 같은 칩 `aria-pressed`(시각 표시 없음, Figma 없음) |
| 색상 추출 | 24 × 24, `#C5B19E → #5F554C`, 1px 검정 40%, radius 3, drop-shadow `0 2px 2px rgba(0,0,0,.4)`, 아이콘 18(`colorize`) | 같음(`color-pick.svg` = Figma 원본). `onColorPick` 후 닫힘. 실제 추출은 호스트 |
| 색상 입력 | 130 × 26, 칩(p 2) + 값(px 8, 빨강 `#E60403`). hover 1px `rgba(197,177,158,.2)`, 편집 시 값 영역 검정 + 왼쪽 구분선 | 같음(`SWHeaderInput` 사용). `r,g,b`(0–255) 또는 `#rrggbb` 검증, 잘못된 값 복원. "컬러칩 클릭"(색 선택 팝업)은 Figma에 "추후 디자인 진행예정" → 미구현 |
| 선스타일 목록 | drop_wrap 468 × 192(x −92, y 37, 1px `#A67D03` inside), drop 466 × 190, 열 182 / 142 / 142(구분선 `rgba(197,177,158,.15)`) | 같은 구조. **차이**: 선 종류 열은 Noto Sans KR에서 "Continuous"(약 88px)가 잘리지 않도록 최소 182 · 최대 240px로 늘어나 1920에서 473.2 × 192(x −94) |
| 선 종류 행(26:10702) | 182 × 30, padding 12, gap 8, 미리보기 70 × 2(`#F6F4F2` + 검정 1px outside), Hover `#62533D` · 선·글자 `#FF5C00` | 같음(1920 측정 미리보기 x+12, y 14). CENTER 22·7·22·7, HIDDEN 33·33(`swHeaderLinePatterns`), 패턴은 데이터 `pattern` |
| 가중치 · 스케일 | 142 × 30 행, ByDefault 값 오른쪽, User·스케일은 빨간 수정 가능 글자(input) | 같음. User(`editable`)와 스케일은 `SWHeaderInput`(가중치 0 이상, 스케일 0 초과) |
| 문자스타일 목록 | 164, 행 30(글꼴 16 + 이름 14, gap 12), 하단 40(px 10, 테두리 .25, 왼쪽 정렬) | 같음. **차이**: 글꼴 칸 38px(Dotum) → Noto "Arial" 42px라 최대 56px까지 내용 폭. Figma Variant3의 상세 버튼은 hover Variant(.25)로 배치되어 있으나 Hover 정책상 Default(.15)로 표시 |
| 치수스타일 목록 | 164, 행 30(미리보기 60 × 15 + 이름, gap 12), 하단 40(px 10, 테두리 .25, 오른쪽 정렬) | 같음. 행 미리보기는 Figma 행 자산 `menu-dimension-row.svg`(`#F6F4F2`)와 Hover `menu-dimension-row-hover.svg`(`#FF5C00`, foreground fill 1곳만 다름) |
| 하단 상세 버튼 | `btn_상세/색상·선스타일·문자스타일·치수스타일`: 아이콘 14, gap 4, padding 5 / 10, `rgba(197,177,158,.15)`, radius 24, 글자 35% | 레이어 상세와 같은 스타일. Callback 없으면 Figma 외형 그대로 비활성. 상세 팝업은 미구현 |
- 선택: 색상·선스타일은 여러 속성을 연속 변경하도록 선택 후에도 열린 상태를 유지하고(구현 결정), 문자·치수스타일은 행 선택 후 닫힙니다. 각 열은 자기 속성의 Callback만 호출합니다(선 종류 → `onLineTypeSelect`만, 가중치·스케일 유지).
- 현재 항목(`active*Id`, 미지정 시 이름 일치)은 레이어 목록과 같이 이름만 `#FFC600`이며 `aria-current="true"`. Hover(배경)와 구분됩니다.
- **Hover 정책(2026-10-16 사용자 확정)**: Dropdown 안 독립 버튼(ByLayer·ByBlock·색상 칩·사용자 색상·색상 추출·`… 상세`)은 배경·글자·아이콘·테두리·그림자·밝기·크기·위치 변화가 없습니다. Figma Hover Variant(ByLayer 주황 글자, 상세 .25 등)는 비교 자료입니다. 클릭·On·`:focus-visible`은 유지합니다. **예외**: 목록 행(레이어·선 종류·가중치·스케일·문자·치수)의 Figma 행 Hover와 Input의 Figma Hover(수정 가능 표시)는 유지합니다. 문서 Dropdown Hover는 변경하지 않았습니다.
- 동적 데이터: 모든 목록은 `id` 기반 배열 Props이며 추가·삭제·이름/값 변경·재정렬에 소스 수정이 필요 없습니다. 현재 항목이 삭제되면 선택 표시가 없어지고 필드는 값 Props를 표시합니다. 긴 목록은 Dropdown 안에서만 스크롤합니다(행 240px).
- 좁은 헤더: 목록은 헤더 안쪽으로 이동하고, 헤더 폭 480 미만에서는 선스타일 3열을 세로로 쌓습니다(가로 스크롤·화면 밖 이탈 없음, 필요 시 세로 스크롤).
### 5.4 반응형 More — 대표 필드 전환 (2026-10-16)
대표 슬롯 = 객체 수정 영역의 첫 필드 칸(레이어 필드 자리). 선택 근거: 1920 배열의 첫 칸이자 모든 Breakpoint에서 끝까지 남는 칸이며, 로고·파일명·창 제어 영역과 분리되어 있습니다.
- 더보기에서 필드 항목(색상·선/문자/치수스타일)을 고르면 그 필드가 대표 칸(`order: -1`)에 표시되고 Dropdown이 열리며 Focus가 필드로 이동합니다. 레이어는 그동안 더보기(tier `slot`)로 옮겨지고, 더보기 → 레이어로 복귀합니다. 화면에 보이는 필드는 더보기에서 빠집니다.
- 전환 상태는 `SWHeader` 내부 UI 상태(`data-promoted`)이며 데이터와 분리됩니다. 필드 클릭과 같은 `on*Open` 1회 + `on*MenuOpenChange(true)`만 호출하고 데이터 Callback은 호출하지 않습니다. 값·다른 필드 상태는 그대로입니다.
- CSS는 해당 필드가 더보기에 있는 폭(선/문자/치수 < 1800, 색상 < 900)에서만 전환을 적용하므로 1920 배열과 기존 Breakpoint는 바뀌지 않습니다. 대표 필드는 남는 폭에 맞춰 줄어들 수 있고(값 말줄임), 객체 수정 영역 밖으로 넘치지 않습니다.
- 검증(16개 폭 1920–390): [검증 기록](./sw-header-dropdowns-stroke-input-verification-2026-10-16.md).
### 5.5 레이어 상세 버튼 · 수정 가능한 Input (2026-10-16)
- `btn_상세/레이어` 26:10767(Default 26:10768 / Hover 26:10771): 기존 구현(§5.2)이 Default와 같습니다 — 아이콘 14(`layer-detail.svg` = Figma `ico_layer` Default, Hover 아이콘은 opacity .35로 다름), gap 4, padding 5 / 10, `rgba(197,177,158,.15)`, radius 24, 글자 35% + 흰색 0.4px Stroke. Figma 113 × 24는 Dotum 기준이고 Noto Sans KR에서 107.3 × 24(기존 APPROVED DIFFERENCE). **Hover(.25 · radius 25)는 사용자 정책상 적용하지 않으며** Storybook에 Figma 참조 이미지로만 둡니다. Callback은 기존 `onLayerDetail`을 재사용하고 상세 팝업은 만들지 않습니다.
- `input` 26:11656(Default 26:11657 58 × 20 / hover 26:11659 58 × 24 / on 26:11661 58 × 24) → 공개 `SWHeaderInput`: padding 2 / 4 / 2 / 6, radius 2, 14px `#E60403` + 검정 1.3px Text Stroke. Hover 1px `rgba(212,203,189,.25)`, 편집(Focus) `#0F0D0A` + 1px `.3`; 테두리가 padding 1px을 대신해 글자 위치는 그대로입니다. 폭은 값 길이(`4.0526` = 58px, 브라우저 측정 일치).
- `cursor` 26:11664(1 × 14 `#E60403`, Variant2 투명): 네이티브 caret을 `caret-color: #E60403`으로 표시하고 깜박임은 브라우저 기본입니다. 가짜 커서를 만들지 않아 커서 위치·선택·IME가 기본 동작합니다. caret 높이는 글꼴 metrics를 따르므로 14px과 정확히 같다고 보증하지 않습니다(NOT VERIFIED).
- 동작: 편집 중 임시값(`onValueChange`)과 확정값(`value`)을 분리합니다. Enter·Focus 이동·감싼 Dropdown 닫힘에서 `parse`(기본: `valueType` text = 비어 있지 않은 텍스트, number = 소수) 통과 + 변경 시에만 `onCommit`, 잘못된 값은 확정값으로 복원, Escape는 편집 취소(`onCancel`, 첫 Escape는 Dropdown을 닫지 않음), IME 조합 중 Enter는 확정하지 않음. `readOnly`(또는 `onCommit` 없음)·`disabled` 지원.
## 6. Typography
| 용도 | Figma | 구현 |
|---|---|---|
| 파일명 | Dotum 14, tracking 0.84px | Noto Sans KR 14 / 700 / 0.06em, `#F0F7F5` |
| 그리기모드·필드 값·토글 | Dotum 14(bold 스타일명), tracking 0.56px | Noto Sans KR 14 / 700 / 0.04em, `#F6F4F2` |
| 필드 라벨·상세보기 | Dotum 12, tracking 0.24px | Noto Sans KR 12 / 400 / 0.02em |
| 문자스타일 글꼴명 | Dotum 16 | Noto Sans KR 16 / 700 |
- Font Weight는 400 / 700만 사용합니다(브라우저 측정 확인). 600·750·800 없음.
- ~~Figma 글자는 검정·밝은색 두 레이어를 겹친 구조입니다. 사용자 요청에 따라 글자 외곽선·겹침 효과를 넣지 않고 밝은색 한 겹만 사용합니다.~~ (과거 기준 보존. 2026-10-16 후속 요청으로 §6.1 Text Stroke를 복원했습니다.)
- 폰트 파일은 패키지에 포함하지 않습니다. 소비 프로젝트가 Noto Sans KR을 로드하며, 없으면 `Malgun Gothic` → `Apple SD Gothic Neo` → `sans-serif`로 대체됩니다.
### 6.1 Text Stroke (2026-10-16)
Figma Plugin API로 26:8944와 메뉴 8개·문서·레이어 Component Set의 모든 TEXT 노드(Variant 포함, 숨김 제외)를 조사했습니다. 모든 Stroke는 **OUTSIDE**입니다.
| 영역 | Figma 구조 | 구현 |
|---|---|---|
| 14px 값(파일명, 그리기모드, 레이어·색상·선/문자/치수 값, 가중치·스케일 값, 속성Bar·속성창, 문서·레이어 목록 이름, 새 Dropdown 행·ByLayer/ByBlock) | 같은 글자 2층: 아래층 검정 Stroke 1.3px, 위층 채움 + 같은 색 Stroke 0.3px | `text-shadow` 16방향 1.3px 검정 원(`--hm-sw-header-text-outline`) + `-webkit-text-stroke-width: 0.6px`(가운데 정렬 → 바깥 0.3px, `currentColor`) |
| 16px 글꼴 미리보기(필드·문자스타일 행) | 14px과 같음(1.3 + 0.3) | 같음 |
| 12px 라벨(레이어·색상·선스타일·가중치·스케일·문자스타일·치수스타일·상세보기) | 채움 + 검정 Stroke 1px | `--hm-sw-header-label-outline`(1px 검정 16방향), 자기 색 Stroke 없음 |
| 수정 가능 글자(색상·가중치·스케일·상세 Input) | 아래층 검정 1.3px + 위층 `#E60403` 0.3px | 14px과 같음(`SWHeaderInput`) |
| `… 상세` pill(레이어·색상·선/문자/치수) | 흰색 + 흰색 Stroke 0.4px, 검정 없음, 35% | `-webkit-text-stroke-width: 0.8px`(바깥 0.4px) |
| `+ New` / `Open` | 검정 70% + 검정 0.4px | **미적용**(2026-10-16 추가 요청: 두 버튼은 기존 상태 유지) |
| 더보기 메뉴, 창 이동 Tooltip | Figma에 없음 / Stroke 없음 | 미적용 |
- `-webkit-text-stroke` 단독은 획 중심 기준이라 글자 안쪽을 깎으므로 검정 외곽선은 `text-shadow`로 그립니다(글자 뒤, 글자 모양을 덮지 않음). Hover·Selected·행 Hover는 채움색만 바뀌고 외곽선은 검정 그대로입니다. Input의 caret·선택 영역은 shadow 위에 그려져 가려지지 않습니다.
- 말줄임 텍스트(`overflow`)는 `overflow: clip` + `overflow-clip-margin: 2px`로 외곽선이 상자 경계에서 잘리지 않게 했습니다(`overflow-clip-margin` 미지원 Safari 등에서는 가장자리 1px 내외가 잘릴 수 있음 — NOT VERIFIED).
- 글꼴·크기·자간·기준선·Font Weight(400/700)는 바꾸지 않았습니다. Figma는 Dotum Regular + Stroke로 굵기를 표현하고 구현은 Noto Sans KR 700 + 같은 Stroke이므로, 구현 글자가 Figma보다 약간 굵게 보입니다(남은 차이, `--hm-sw-header-text-stroke-width`로 조정 가능).
- 레이어 Dropdown 이름·레이어 상세에도 같은 기준이 적용됩니다(CSS만, 레이어 컴포넌트·매핑 코드는 변경 없음).
## 7. Color · Gradient
| 토큰 | 값 | 근거 |
|---|---|---|
| `--hm-sw-header-background` | `#060606` | Figma header frame |
| `--hm-sw-header-text-color` | `#F6F4F2` | Figma `--text-brown100` |
| `--hm-sw-header-title-color` | `#F0F7F5` | Figma `--text-green100` |
| `--hm-sw-header-arrow-color` | `#47AF91` | Figma `--solid-arrow` |
| `--hm-sw-header-selected-color` | `#FFC600` | 사용자 확정 선택 상태 |
| `--hm-sw-header-focus-color` | `#F0F7F5` | 구현값(Figma 없음) |
| `--hm-sw-header-hover` | `rgba(255,255,255,0.08)` | 구현값. 현재 최소화·종료 버튼과 더보기 메뉴 항목에만 사용 |
| `--hm-sw-header-hover-border-gradient` | 180deg `#A67D03` 0% → `#F2CC0F` 16.41% → `#FADD4F` 37.19% → `#F2CC0F` 62.03% → `#926E03` 87.88% | Figma `header/btn_home` hover 테두리(26:10655) |
| `--hm-sw-header-document-background` | 사용자 확정 180deg gradient(§10 B) | 사용자 확정 = Figma 동일 |
| `--hm-sw-header-object-background` | 사용자 확정 0deg gradient(§10 A) | 사용자 확정 = Figma 동일 |
| `--hm-sw-header-property-background` | 사용자 확정 반투명 검정 gradient(§10 C) | 사용자 확정 = Figma 동일 |
| `--hm-sw-header-pill-background` | `rgba(0,0,0,0.8)` | Figma `Rectangle 6959` |
| `--hm-sw-header-stripe-color` | `rgba(28,106,81,0.32)` | Figma 창 이동 줄무늬 |
| `--hm-sw-header-window-icon-color` | `#ADB0AF` | Figma 최소화 막대 |
창 영역(`Rectangle 6669`)과 인쇄 버튼은 파일명 gradient·테두리를 Figma 그대로 재사용합니다.
## 8. Border · Radius · Shadow
| 면 | Border | Radius | Shadow |
|---|---|---|---|
| 파일명 | 위·오른쪽·아래 1px `#0E4C3A`, 왼쪽 0 | 0 | none |
| 객체 수정 | 1px `#135643` | 6px | none |
| 속성 필드(5개 메뉴, 더보기 버튼) | 1px `#08251C` | 3px | `0 1px 4px 0 rgba(9,39,30,.7)` |
| 창 영역 | 1px `#0E4C3A`(4면, pseudo) | 0 | none |
| 인쇄·가이드 pill | 1px `#092920` | 4px | inset `0 2px 2px rgba(0,0,0,.25)` |
## 9. 아이콘 · Assets
Figma MCP에서 내려받은 원본 SVG 13개(종료 hover 포함)와 Hover 색 Variant 2개, 총 15개를 `packages/sw/src/assets/`에 두고 build 시 data URI로 `dist/index.js`에 포함합니다. 임시 Figma URL 참조는 없습니다(`dist` 검색 0건). 다색 SVG 원본을 수정·재작성하지 않았습니다. Hover 색 Variant(`print-hover.svg`, `popup-guide-hover.svg`)는 원본을 복사해 `fill="#B6D0C9"` 1곳만 `#FFC600`으로 바꾼 파일이며 path·mask·filter·gradient는 원본과 같습니다(§11.2).
| 파일 | Figma 요소 | 렌더링 크기 |
|---|---|---|
| `home-bg.svg` | `Rectangle 7508` 홈 배경(Default) | 92 × 72 |
| `home-bg-hover.svg` | `Rectangle 7509` 홈 배경(hover, 노란 gradient 테두리) | 92 × 72 |
| `document-icon.svg` | `icon_dualmonitor` 문서 Dropdown 기본 아이콘(29 × 12 영역, 그림자 포함) | 37 × 19(위치 −4, −3) |
| `layer-union.svg`, `layer-ellipse.svg` | 레이어 아이콘 1(합성) | 14 × 18, 14 × 14 |
| `layer-state-b.svg`, `layer-state-a.svg` | 레이어 아이콘 2, 3 = `icon_눈꽃` / `icon_자물쇠` Default(컬러) | 20 × 20 |
| `layer-ellipse-off.svg` | `icon_전구` Variant3/4 회색 전구알(26:10833, union·막대는 Default와 같음) | 14 × 14 |
| `layer-freeze-hover.svg`, `layer-freeze-off.svg`, `layer-freeze-off-hover.svg` | `icon_눈꽃` Variant2 / 3 / 4(26:10870 / 26:10884 / 26:10898) | 20 × 20 |
| `layer-lock-hover.svg`, `layer-lock-off.svg`, `layer-lock-off-hover.svg` | `icon_자물쇠` Variant2 / 3 / 4(26:10919 / 26:10925 / 26:10931) | 20 × 20 |
| `layer-detail.svg` | `ico_layer` 레이어 상세 아이콘(26:1474) | 14 × 14 |
레이어 Dropdown(§5.2) 자산 8개는 2026-10-16 Figma MCP에서 내려받은 원본입니다. 기존 `layer-union/ellipse/state-a/state-b`는 Figma 원본과 mask id 외 동일함을 비교해 그대로 재사용했습니다. Hover SVG는 Default 아이콘 + 검정 25% 20 × 20 타일(radius 2)이며 그 외 차이가 없음을 확인했습니다.
| `menu-dimension-ico.svg` | 치수 아이콘 | 60 × 15 |
| `menu-dimension-row.svg`, `menu-dimension-row-hover.svg` | 치수스타일 Dropdown 행 미리보기 Default(`#F6F4F2`) / Hover(`#FF5C00`)(26:10729, 2026-10-16 Figma 원본, foreground fill 1곳만 다름) | 60 × 15 |
| `color-pick.svg` | `btn_colorize` 아이콘 `colorize`(26:11303, 2026-10-16 Figma 원본) | 18 × 18 |
| `print.svg` | 인쇄 | 24 × 22.9(버튼 inset 20.1/20/19.62%) |
| `print-hover.svg` | 인쇄 Hover(원본의 `#B6D0C9` → `#FFC600`만 변경, Figma Variant 없음) | 24 × 22.9(같은 위치) |
| `popup-guide.svg` | `꾸러미ver2/팝업가이드` | 40 × 38 |
| `popup-guide-hover.svg` | 팝업 가이드 Hover(원본의 `#B6D0C9` → `#FFC600`만 변경, Figma Variant 없음) | 40 × 38(같은 위치) |
| `window-motion.svg` | 창 이동 아이콘 | 32 × 30 |
| `close.svg` | `header/btn_종료` Default(26:12162) | 32 × 48 |
| `close-hover.svg` | `header/btn_종료` Variant2 = hover(26:12166): `#E40000` 32 × 48 + 흰 X, X path는 Default와 같음 | 32 × 48 |
파일명 화살표(Figma `Polygon 352`, 단색)는 선택 시 색을 바꾸기 위해 SVG 대신 CSS 삼각형(7 × 4)으로 그립니다. 기존 뷰어의 임시 `HM` 텍스트 로고는 사용하지 않았습니다.
### 9.1 로고 (확정 디자인 아님)
SW Header는 여러 SW 프로그램이 공통으로 사용합니다. 따라서 **로고 영역의 배치는 공통 디자인이고, 로고 이미지 자체는 사용하는 SW 프로그램이 결정하는 항목**입니다. 패키지는 특정 제품 로고·브랜드명·파일 경로를 포함하지 않습니다.
| 항목 | 기준 | 구분 |
|---|---|---|
| 로고 영역 | 홈 버튼(80 × 48) 안 x 12 / y 16, 32 × 17 (Figma `bi_egbim` frame). 토큰 `--hm-sw-header-logo-width` / `-logo-height` | 공통 디자인(확정) |
| 홈 배경 | `home-bg.svg` 사선 녹색 면 + 0–65px 파일명 gradient 바탕 | 공통 디자인(확정) |
| 로고 이미지 | `logoSrc`(이미지 URL) 또는 `logo`(React 요소)로 각 프로그램이 전달. 비율이 달라도 영역 안에 `contain`으로 맞추고 넘치지 않음 | 프로그램별 결정 |
| 로고 미전달 | 로고를 그리지 않음. 홈 배경·파일명·객체 수정 영역 위치는 그대로 | 공통 동작 |
| 접근성 | `logoAlt` 전달 시 홈 버튼 이름 "<logoAlt> 홈", 없으면 "홈" | 공통 동작 |
- Figma 26:8944의 이지빔 로고(`bi_egbim`)는 기준 화면의 예시이며 공통 컴포넌트에 넣지 않습니다(2026-10-16 사용자 지시로 패키지에서 제거).
- Storybook 기본 화면의 로고는 임시 자산 `src/sw/assets/sw-header-logo-placeholder.svg`(브랜드명 없는 점선 사각형 도형)입니다. Storybook 전용 파일이며 패키지에 포함되지 않고, 어떤 제품의 공식 로고도 아닙니다.
## 10. 사용자 확정 CSS
신규 패키지에서는 기존 뷰어 class 대신 SW 전용 class와 토큰을 씁니다. 값은 그대로입니다.
| 기존 뷰어 class | 신규 class | 토큰 |
|---|---|---|
| A `.cad-toolbar-object-area` | `.hm-sw-header__object-area` | `--hm-sw-header-object-background`, `-object-border-color`, `-object-radius` |
| B `.cad-toolbar-document` | `.hm-sw-header__document` | `--hm-sw-header-document-background`, `-document-border-color` |
| C `.cad-toolbar-property-field` | `.hm-sw-header__field` | `--hm-sw-header-property-background`, `-property-border-color`, `-property-radius`, `-property-shadow` |
```css
/* A. 객체 수정 */
border-radius: 6px; border: 1px solid #135643; box-shadow: none;
background: linear-gradient(0deg, #144838 0%, #1A4536 6%, #1F4134 10%, #10392D 18%, #296B55 72%, #1F5A47 83%, #144838 92%, #124133 100%);
/* B. 파일명 */
border-radius: 0; border-top/right/bottom: 1px solid #0E4C3A; border-left: 0; box-shadow: none;
background: linear-gradient(180deg, #081813 0%, #09271E 8.69%, #0B3227 25.43%, #0E3D2F 51.25%, #0B3227 80.03%, #0A2B21 89.76%, #081813 100%);
/* C. 속성 입력 */
border-radius: 3px; border: 1px solid #08251C; box-shadow: 0 1px 4px 0 rgba(9, 39, 30, 0.70);
background: linear-gradient(0deg, rgba(0,0,0,.45) 0%, rgba(0,0,0,.45) 26.41%, rgba(0,0,0,.40) 70%, rgba(0,0,0,.45) 100%);
```
세 값은 Figma `get_design_context`의 해당 레이어 값과도 일치합니다(Figma 26.407% → 확정 26.41%). C의 반투명 검정은 A 위에서 합성되는 것이 의도입니다.
## 11. 선택 · Focus · Hover · Disabled
| 상태 | 표현 | 근거 |
|---|---|---|
| 선택(`activeMenu`, `propertyBarOpen`, `propertyPanelOpen`, 더보기 열림) | 텍스트·단색 아이콘(파일명 화살표, 더보기 점)만 `#FFC600`. 배경·테두리·그림자 변화 없음 | 사용자 확정 |
| Focus(키보드) | 2px `#F0F7F5` outline, offset −2px. 선택과 별도 | 정책(선택과 구분) + 구현값 |
| Hover | 노란 gradient 테두리(§11.1) 유지. 5개 필드는 텍스트·지정 스타일 미리보기도 `#FFC600`(§11.5). 배경·크기·padding·radius·그림자 변화 없음. 인쇄·팝업 가이드와 최소화·종료는 기존 §11.2 유지 | Figma 홈 버튼 hover(26:10655) + 사용자 추가 §11.5, Figma 26:12156 / 26:12161 |
| Selected + Hover | 선택 글자색 `#FFC600` 유지 + Hover 테두리. 두 표현이 다른 속성이라 함께 보임 | 정의 |
| Focus + Hover | Focus 윤곽선(2px `#F0F7F5`)이 Hover 테두리 위에 그려짐 | 정의 |
| Disabled(`disabled`) | 메뉴·속성·인쇄/가이드 opacity .4, 커서 기본, Hover 테두리·아이콘 색 변화 없음. 창 제어는 유지 | 구현값 |
레이어 상태 아이콘 3개는 원본 다색 SVG(노랑 전구·하늘색·자물쇠)로, 선택 시 `#FFC600`으로 바꾸지 않습니다. 레이어 상태 의미를 보존하기 위한 의도적 차이입니다(§15).
### 11.1 Hover 테두리 (공통 상호작용 규칙)
**Figma에서 확인한 것**(`header/btn_home`, node 26:10655, Variant `Default` / `hover`, 80 × 48): Hover는 배경 SVG의 1px 테두리만 녹색 gradient에서 노란 gradient로 바꿉니다. 채움·그림자·크기·로고 위치는 Default와 같습니다(두 SVG를 비교해 확인). 방향은 위 → 아래(`y1 12 → y2 60`)이며 stop은 0% `#A67D03`, 16.41% `#F2CC0F`, 37.19% `#FADD4F`, 62.03% `#F2CC0F`, 87.88% `#926E03`(이후 100%까지 같은 색)입니다.
**구현 적용 범위**(Figma 홈 버튼 규칙을 다른 버튼에 확장한 구현 결정):
| 대상 | 방식 |
|---|---|
| 홈 | Figma hover SVG(`home-bg-hover.svg`)를 Default 위에 겹쳐 사선 외곽선에 정확히 표시 |
| 레이어·색상·선스타일·문자스타일·치수스타일, 더보기 버튼 | 1px 테두리 위치, radius 3px |
| 인쇄 | 1px 테두리 위치, radius 2px. 아이콘 색 변화는 §11.2 |
| 팝업 가이드 | SVG 안의 1px 외곽선 위치, radius 2px. 아이콘 색 변화는 §11.2 |
파일명(문서 Dropdown 버튼)은 예외입니다(2026-10-16 사용자 지시): Hover 시 노란 테두리 없이 **파일명 글자만 `#FFC600`**으로 바꾸고, 녹색 테두리·배경 gradient·초록 화살표는 그대로 둡니다. Dropdown이 열린 동안에는 글자 `#FFC600`과 노란 위쪽 화살표를 유지하고, 닫힌 뒤 Hover가 풀리면 Default로 돌아갑니다. 키보드 Focus 윤곽선은 별도로 유지합니다.
문서 Dropdown 안의 목록 항목과 `+ New` / `Open`도 노란 테두리를 쓰지 않고 각자의 Figma hover를 따릅니다(§5.1): 목록 항목은 검정 40% 배경, `+ New` / `Open`은 배경 20% → 30%·글자 70% → 100%. 속성Bar · 속성창도 예외입니다(2026-10-16 사용자 지시): Hover 시 **글자만 `#FFC600`**이고 노란 테두리·배경·그림자가 없습니다. 선택(`propertyBarOpen` / `propertyPanelOpen`)도 글자 `#FFC600`이며 선택된 토글에 Hover해도 테두리가 생기지 않습니다. 위의 '상세보기' 안내 글자는 바뀌지 않고, 키보드 Focus 윤곽선은 그대로입니다.
즉 **파일명·속성Bar·속성창 Hover(글자 노랑) · 목록 항목 Hover(검정 40% 배경) · 하단 버튼 Hover(배경·글자 진하게)는 서로 다른 정책**이며, 노란 gradient 테두리는 위 표의 버튼(홈, 5개 속성 필드, 더보기, 인쇄, 팝업 가이드)에만 적용합니다.
제외: 그리기모드(클릭 대상이 아닌 텍스트), 창 이동(드래그 영역), 최소화·종료(외곽선이 없는 창 제어 버튼, Figma 전용 hover를 따름 — §11.2), 더보기 메뉴 안 항목(팝업 목록, 기존 배경 Hover 유지), 비활성 버튼.
구현: 토큰 `--hm-sw-header-hover-border-gradient`, `--hm-sw-header-hover-border-width`(1px). 대상 버튼의 `::after`가 기존 테두리 위치에 gradient 링(mask)을 그리며, 레이아웃·클릭 영역은 바뀌지 않습니다. gradient는 각 버튼 높이 기준 위 → 아래로 적용됩니다. 이전 구현값(필드·토글 배경 겹침, 인쇄·가이드 `brightness`)은 Figma 기준에 맞춰 제거했습니다.
### 11.2 창 제어 · 바로가기 아이콘 Hover (2026-10-16)
**Figma에서 확인한 것**(`get_design_context` / `get_metadata`):
| 버튼 | Figma | Default | Hover(Variant2) |
|---|---|---|---|
| 최소화 | `header/btn_최소최대화` 26:12156(Default 26:12157, Variant2 26:12159) | 32 × 48, 배경 없음, 막대 10 × 2 `#ADB0AF` | 배경 `rgba(0,0,0,.2)`, 막대 `#FFFFFF`(크기·위치 같음) |
| 종료 | `header/btn_종료` 26:12161(Default 26:12162, Variant2 26:12166) | 32 × 48 SVG, X `#ADB0AF` | 같은 SVG 구조에 `#E40000` 32 × 48 사각형 + X `white`(X path 동일) |
Figma 헤더(26:8944)에서 최소화·종료 frame(`Frame 427319960`)은 창 영역 배경 `Rectangle 6669`(1px 테두리)보다 위에 있습니다. 그래서 Hover 배경은 창 영역 테두리 위까지 32 × 48 전체를 덮습니다. 구현은 `.hm-sw-header__window-buttons`를 `position: relative`로 두어 창 영역 `::before` 테두리 위에 그립니다. Default는 버튼이 투명하므로 이전과 같습니다.
**구현**:
| 버튼 | 방식 |
|---|---|
| 최소화 | `:hover` 배경 `--hm-sw-header-window-button-hover-background`(`rgba(0,0,0,.2)`), 막대 `--hm-sw-header-window-icon-hover-color`(`#FFF`) |
| 종료 | Figma hover SVG(`close-hover.svg`)를 Default SVG와 같은 칸(`grid-area: 1 / 1`)에 두고 Hover 때만 보이게 전환. Figma X 형태 그대로 |
| 인쇄 · 팝업 가이드 | 기존 노란 gradient 테두리(§11.1) 유지 + Hover 때 `*-hover.svg`로 전환. 아이콘의 `#B6D0C9` 채움만 `#FFC600`이고 검정 외곽선(mask)·그림자(filter)·팝업 가이드 녹색 gradient 타일·테두리는 그대로 |
- 전환은 `.hm-sw-header__icon` / `.hm-sw-header__icon-hover` 두 `<img>`의 `visibility`로 합니다. 같은 위치·크기라 레이아웃·클릭 영역이 바뀌지 않고, 두 이미지가 겹쳐 그려지지 않아 그림자가 두 번 칠해지지 않습니다.
- 외부 `<img>` SVG는 CSS로 특정 fill만 바꿀 수 없어 로컬 SVG Variant를 둡니다. 전체 SVG에 CSS `filter`를 쓰지 않습니다.
- 최소화·종료에는 노란 테두리·그림자·크기 변화가 없습니다. 비활성(`disabled`) 인쇄·팝업 가이드는 아이콘 색이 바뀌지 않습니다. 키보드 Focus 윤곽선은 그대로이며 Focus만으로는 Hover 표현을 하지 않습니다(§11.1과 같음).
- 종료는 `onClose`만 호출합니다. 실제 창 닫기는 호스트가 구현하며 Storybook은 창을 닫지 않습니다.
- 인쇄 · 팝업 가이드 Hover 아이콘 색은 Figma Variant가 없는 사용자 지시(2026-10-16) 값입니다.
### 11.3 창 이동 Hover Motion — Figma 26:10220 (2026-10-16)
> **GIF 전달 전 구현의 보존 기록입니다. 현재 움직임·반복·좌표는 아래 §11.4를 우선합니다.** Figma 원본의 자산·Tooltip 크기와 색상 근거는 계속 유효합니다.
기준: [창이동 motion](https://www.figma.com/design/rj6wpsfk24lOWvWiVRAANP/이지빔-SW?node-id=26-10220&m=dev). 기존 Default SVG와 `div` / `onPointerDown` 드래그 계약은 유지합니다. 아래 좌표는 88 × 48px 창 이동 영역의 왼쪽 위 기준이며 SVG stroke의 바깥 여백과 구분합니다.
- Default `26:10221`: 아이콘 32 × 30px, (28, 13).
- Hover `26:10258`: 창 24 × 18.857px, (32, 15); 커서의 회전 외곽 박스 15.106 × 15.568px, (52, 13); Tooltip (68, 13).
- Hover2 `26:10295`: 창 (59, 12); 커서 (72, 10); Tooltip (89, 11). 창·커서 크기는 Hover와 같습니다.
- Tooltip `26:215`: `창 이동`, 45 × 17px, 검정 80% 배경, 흰색 12px/400 글자, radius 2px, padding 2px 6px. 색상은 `--hm-sw-header-tooltip-background` / `--hm-sw-header-tooltip-color`입니다. 글꼴은 기존 SW의 Noto Sans KR 정책을 따릅니다(Figma는 Inter).
- 기존 `window-motion.svg`는 유지하고, Hover용 `window-motion-window.svg`(28 × 22.8574), `window-motion-cursor.svg`(13.0291 × 16.7588)를 사용합니다. 2026-10-16 원본 비교에서 Hover 자산 두 개는 일치했고 Default는 SVG 마스크 식별자만 달랐습니다.
Figma Prototype reactions를 직접 확인한 전환:
1. Default → Hover: `ON_HOVER`, `SMART_ANIMATE`, `GENTLE`, 1.0220937728881836초.
2. Hover → Hover2: `AFTER_TIMEOUT` 0.001초, `SMART_ANIMATE`, `QUICK`, 0.744255542755127초.
3. Hover2 → Default: `AFTER_TIMEOUT` 0.001초, `DISSOLVE`, `BOUNCY`, 0.9582165479660034초.
구현은 약 2.7265658초의 CSS Keyframes 4개를 사용합니다. 포인터 진입당 1회 재생하고, Hover를 유지하면 Default에서 멈춥니다. 포인터가 나가면 즉시 Default로 복귀하며 재진입 시 처음부터 시작합니다. Figma에 전환 고리는 있으나 Default로 돌아온 뒤 포인터 유지 중 자동 재시작하는지는 Prototype player에서 실재생하지 않았으므로 **NOT VERIFIED**입니다. 현재 1회 정책은 변경하지 않았습니다.
GENTLE / QUICK / BOUNCY는 기존 코드의 spring 표본을 CSS `linear()`로 근사한 값입니다. 키프레임에 timing function을 직접 선언하며 QUICK의 overshoot와 DISSOLVE의 opacity 혼합을 유지합니다. BOUNCY는 opacity 범위에 맞춘 단조 곡선입니다. Figma API는 preset 이름과 시간을 반환했지만 수치 곡선은 반환하지 않았으므로 Figma player와 중간 프레임이 완전히 같다고 보증하지 않습니다. `linear()` 미지원 브라우저의 선형 fallback은 실기기 **NOT VERIFIED**입니다.
드래그 영역은 기존 `role="presentation"` div와 `onWindowMoveStart(event)`를 유지합니다. 모든 장식 자식과 Tooltip은 `pointer-events: none`으로 포인터를 가로채지 않습니다. Tooltip은 Figma 좌표대로 영역 밖으로 나와 최소화와 종료 왼쪽 일부에 시각적으로 겹칩니다. 이를 겹침 없음으로 판정하지 않습니다. 브라우저 hit test와 버튼 Callback은 통과했으며 실제 네이티브 창 이동·최소화·종료는 호스트 검증이 필요합니다.
반응형은 기존 container query를 유지합니다. 헤더 폭 1080px 이상은 88px 영역과 전체 모션, 640–1079px는 24px 영역에서 그림을 숨기고 정적 Tooltip만 (영역 폭 + 1px, 11px)에 표시, 640px 미만은 영역 전체를 숨깁니다. 헤더 높이는 49px입니다.
`prefers-reduced-motion: reduce`에서는 4개 애니메이션을 끄고 기본 아이콘을 유지하며 Hover 동안 정적 Tooltip을 표시합니다. 24px 단계의 아이콘 숨김 규칙은 그대로입니다. 2026-10-16 검증에서 기존 `> *` 해제 규칙의 낮은 specificity 때문에 애니메이션이 유지되는 오류를 발견해, 레이어별 Hover 선택자와 동일 specificity인 `:is(...)` 선택자로 수정했습니다. 재생 도중 reduce 전환도 즉시 정지합니다.
창 이동 div 자체는 기존대로 Tab 대상이 아니며 Focus Tooltip이나 키보드 창 이동을 제공하지 않습니다. 최소화·종료의 접근성 이름, Tab 이동과 Focus 윤곽선은 유지합니다. 키보드 창 이동 대안과 실제 보조기술 검증은 호스트 과제이며 접근성 전체 PASS를 뜻하지 않습니다.
브라우저 검증·빌드·Guard 한계·기존 작업 보존 근거는 [최종 검증 기록](./sw-header-window-motion-verification-2026-10-16.md)에 별도로 남겼습니다. 아래 기존 반응형 검증 기록은 당시 결과로 보존합니다.
### 11.4 현재 창 이동 Motion — ani_hover_move.gif 우선 (2026-10-16)
> 아래는 원본 GIF의 경로·1640ms 분석 기록입니다. 후속 사용자 요청으로 현재 시간만 1400ms로 단축했으며 현재 시간과 필드 Hover 정책은 §11.5를 우선합니다. 경로·자산 근거는 계속 유효합니다.
사용자 제공 `D:\디자인시스템\ani_hover_move.gif`를 실제 움직임의 최우선 기준으로 적용했습니다. 원본은 640 × 192px, 41프레임 × 40ms = 1640ms, `loop=0`(무한 반복)입니다. SHA-256: `c7d22819cfb8f8cd8593684ce1e5c676a545fa09fa9fc7573d59f0f237d28026`. GIF의 높이 192 / Header bar 높이 48 = 4배이며, 창의 녹색 부분 폭 96 / 4 = 기존 SVG의 녹색 폭 24px도 일치합니다. 이 비율로 상대 이동량을 환산하고 GIF 자체는 제품 자산으로 넣지 않았습니다.
현재 우선순위는 **움직임·등장 순서·시간·유지·반복 = GIF**, **88 × 48 영역·녹색 줄무늬·SVG·Tooltip 규격 = SW/Figma**입니다. 과거 §11.3의 2.727초/1회/Smart Animate spring은 현재 동작이 아닙니다.
1. Hover 전: 기존 `window-motion.svg` 32 × 30px, (28, 13). 변경하지 않았습니다.
2. 0–520ms: 같은 창·커서를 분리 SVG로 표시합니다. 창은 제자리, 커서는 오른쪽 아래에서 위로 12.5px 상승하며 왼쪽 0.75px의 작은 이동이 있습니다. 창 크기를 줄이거나 커서를 위에서 fade-in하지 않습니다.
3. 520ms: `창 이동` 글자가 등장합니다. GIF 13번 프레임과 같은 시점이며 이전에는 숨깁니다.
4. 약 600–1040ms: 창·커서·문구가 오른쪽 위로 함께 이동합니다. 창은 시작점 대비 +28 / −4.5px, 커서는 최초 위치 대비 +27.25 / −17px입니다. 40ms별 표본 사이를 CSS로 보간해 GIF의 가속·감속을 유지하고 추가 spring overshoot는 넣지 않습니다.
5. 1040–1640ms: 끝 자세를 600ms 유지합니다. 1640ms에서 즉시 첫 프레임으로 돌아가 Hover 동안 반복합니다. GIF에 없는 복귀 dissolve는 추가하지 않았습니다.
6. Hover 해제: 애니메이션 제거, 기존 Default SVG로 즉시 복귀. 재진입 시 새 사이클을 시작합니다.
분리된 창은 원본 `window-motion-window.svg`의 28 × 22.8574px 크기로 (28, 13)에 표시합니다. 커서의 회전 wrapper는 15.106 × 15.568px, 시작 (44.25, 26.25)이며 SVG 원본 13.0291 × 16.7588px와 −40° 회전은 유지합니다. Tooltip 시작 box는 (60.75, 14.5), 끝 box는 (88.75, 10)입니다. wrapper/box 좌표와 색이 있는 실제 glyph 좌표는 구분합니다.
Tooltip의 45 × 17px, 검정 80% 배경, 흰색 12px/400 글자, radius 2px, padding 2px 6px는 유지합니다. Figma 선언에 맞춰 Tooltip만 `Inter`를 우선하고 기존 SW 글꼴을 fallback으로 사용합니다. 새 폰트 파일이나 의존성은 추가하지 않았으며, 한글 또는 Inter 미설치 환경에서는 기존 Noto Sans KR 등이 표시됩니다. 문구와 커서 사이의 보이는 간격은 GIF의 약 9px를 기준으로 맞췄습니다.
기존 div / `role="presentation"` / `onPointerDown={onWindowMoveStart}`와 공개 Props는 변경하지 않았습니다. 장식의 `pointer-events: none`도 유지합니다. 포인터를 누르는 `:active` 동안 3개 CSS 애니메이션을 일시정지하고 Tooltip을 숨기며, 새로운 포인터 캡처·전역 이벤트·preventDefault는 추가하지 않습니다. 포인터 캡처 및 실제 창 이동은 기존대로 호스트 책임입니다.
1080px 미만 24px 단계는 그림을 숨기고 정적 Tooltip만, 640px 미만은 영역 전체를 숨기는 기존 정책을 유지합니다. Reduced Motion에서는 3개 애니메이션을 끄고 기존 Default SVG와 정적 Tooltip을 표시합니다. 재생 중 reduce 전환도 적용됩니다. 창 이동의 기존 키보드 미지원과 실제 호스트 통합 검증 한계는 §11.3과 같습니다.
프레임별 비교·최종 빌드·반응형·Drag·보존 근거는 [GIF 기준 검증 기록](./sw-header-window-motion-gif-verification-2026-10-16.md)에 기록합니다. 이전 검증 기록은 당시 상태로 보존합니다.
### 11.5 현재 추가 기준 — 1400ms 및 그룹 필드 Hover (2026-10-16)
후속 사용자 요청으로 창 이동의 전체 주기를 **1640 → 1400ms**로 단축했습니다. 세 애니메이션의 duration만 1.4초로 바꿨고 Keyframes의 모든 좌표·백분율·보간은 동일합니다. 시간 비율은 1400/1640(35/41), 주기 단축은 약 14.63%이며 재생 속도 기준으로는 약 17.14% 빠릅니다. 원본 GIF와 다른 사용자 승인 시간 변경입니다.
- 문구 등장: 520 → 약 443.90ms.
- 이동 완료: 1040 → 약 887.80ms.
- 끝 자세 유지: 600 → 약 512.20ms.
- 반복: 1640 → 1400ms. 오른쪽 28px / 위쪽 4.5px, 커서 상승과 상대 경로는 그대로입니다.
- 기존 SVG·줄무늬·Tooltip 디자인·Hover 해제·Drag 중 일시정지·Reduced Motion·88px/24px/숨김·49px 높이를 유지합니다. 패키지는 호스트의 Pointer Capture를 방해하거나 별도 캡처를 추가하지 않습니다.
그룹 필드(`SWHeaderLayerControl`, `SWHeaderColorControl`, `SWHeaderLineStyle`, `SWHeaderTextStyle`, `SWHeaderDimensionStyle`)는 **각 버튼 전체를 Hover**하면 내부 라벨·값을 `--hm-sw-header-selected-color`(기본 `#FFC600`)로 표시합니다. 선스타일의 가중치·스케일과 문자스타일의 폰트 이름 미리보기도 포함합니다. 공용 컴포넌트의 실제 동작이므로 Full Header와 Composed Controls에 같은 규칙을 적용하며 Story 전용 색 흉내는 사용하지 않습니다.
기존 노란 Gradient Hover Border는 유지합니다. 선스타일의 5개 미리보기 조각은 흰색 채움만 노란색으로 바꾸며 검정 1px border·형태·간격은 그대로입니다. 문자 미리보기는 SVG가 아닌 기존 폰트 이름 텍스트이며 색만 바꿉니다. 원래 별도 검정 Stroke/그림자가 없으므로 새 외곽선을 추가하지 않습니다.
치수 미리보기는 기존 `menu-dimension-ico.svg`를 보존하고 `menu-dimension-ico-hover.svg`를 추가합니다. 실제 foreground path의 `fill="white"` 1곳만 `#FFC600`으로 바꿉니다. mask용 흰색 rect, 검정 외곽선 path, mask 참조·좌표·60 × 15px 크기는 유지합니다. 두 이미지는 같은 60 × 15px wrapper 안에서 visibility만 전환하며 CSS filter는 사용하지 않습니다.
색상 견본의 실제 `swatch`, 흰색/검정 chip border, 레이어 상태 SVG·막대 색은 바꾸지 않습니다. Hover 이탈 시 원래 색으로 돌아가고 Selected의 기존 텍스트 색은 유지합니다. Disabled는 새 Hover 텍스트·미리보기·Border 효과를 적용하지 않으며 Disabled+Selected도 기존 색을 유지합니다. 미리보기의 선택 단독 상태를 새로 확장하지 않습니다.
**상세보기는 제외**합니다. `속성Bar` / `속성창`은 기존처럼 해당 버튼의 텍스트만 노란색이고, 상세보기 caption·검정 divider·배경·그림자는 유지하며 노란 Hover Border를 추가하지 않습니다. 홈·문서 Dropdown·인쇄/가이드·최소화/종료는 이번 추가 기준으로 변경하지 않습니다.
Composed Controls의 기존 레이어·색상·상세보기 예제는 보존하고, 기존 공개 선/문자/치수 컴포넌트 예제와 5개 필드의 고정 Hover 비교를 추가했습니다. 검증과 파일별 근거는 [속도·Composed Hover 검증 기록](./sw-header-speed-composed-hover-verification-2026-10-16.md)에 남깁니다.
## 12. 반응형
2026-10-16 사용자 지시로 기존 1180 / 900 / 560 기준 대신 측정값으로 단계를 다시 정했습니다. 모든 단계를 헤더 폭 기준 container query(`container: hm-sw-header / inline-size`)로 처리하며 헤더 가로 스크롤은 만들지 않습니다.
원칙: 5개 필드는 어느 폭에서도 Figma 폭(234 / 192 / 284 / 164 / 164)을 유지합니다. Noto Sans KR로 측정한 필드 내용 폭이 231 / 168 / 278 / 142 / 163px로 Figma 폭과 거의 같아, 필드를 줄이면 값만 잘리기 때문입니다. 공간이 부족하면 기능을 더보기로 옮기고, 문서명만 말줄임합니다.
필요 폭 = 고정부(홈 63 + 간격 6 + 창 영역) + 객체 수정 면 + 문서명.
| 헤더 폭 | 동작 | 객체 수정 면 / 창 영역 | 문서명 폭 |
|---|---|---|---|
| ≥ 1800 | Figma 기본 레이아웃(1920과 동일 구성) | 1301 / 240 | 1920: 310 → 1800: 190 |
| < 1800 | 선·문자·치수 스타일 → 더보기 | 719 / 240 | 120–310 |
| < 1180 | 그리기모드 라벨 숨김 | 633 / 240 | 120–310 |
| < 1080 | 속성Bar·속성창 → 더보기, 창 이동 88 → 24px | 480 / 176 | 120–310 |
| < 900 | 색상 → 더보기 | 284 / 176 | 96–310 |
| < 640 | 인쇄·팝업 가이드 → 더보기, 창 이동 숨김 | 284 / 64 | 96–310 |
| < 520 | 레이어 상태 아이콘 숨김, 레이어 필드만 줄어들며 레이어명 말줄임 | — | 96 |
- 1800 기준: 5개 필드를 모두 표시할 때 문서명이 190px 이상 남는 최소 폭입니다(1610 + 190). 1770은 문서명 160px로 표시는 가능하지만 사용자 제안(약 1800)과 문서명 가독성을 고려해 1800을 채택했습니다.
- 1180 / 1080 / 900 / 640은 각 단계에서 문서명이 최소 폭(120 또는 96px)에 닿기 직전의 폭입니다. 기존 1180 기준은 "그리기모드 라벨 숨김"으로, 900 기준은 "색상 이동"으로 역할을 바꿨습니다. 기존 560 화면 폭 규칙은 헤더 폭 640 규칙으로 대체했습니다(헤더 폭 ≤ 화면 폭이므로 화면 560 미만에서도 항상 적용됨). 원 규칙의 "파일 열기·전체 보기 글자 숨김"은 Figma 헤더에 해당 도구가 없어 적용 대상이 없습니다.
- 문서명 폭은 `clamp(최소, 100cqw − 나머지 고정폭, 310px)`입니다. 문서명은 말줄임, 글자 크기는 줄이지 않습니다.
- 더보기 메뉴는 옮겨진 기능만 표시하며, 선택(`#FFC600`)·체크 상태와 Props·Callback 동작은 헤더에 있을 때와 같습니다.
검증(2026-10-16, Storybook `SW / Header / Responsive`, 18개 폭): 1920 / 1800 / 1799 / 1770 / 1600 / 1440 / 1280 / 1180 / 1179 / 1080 / 1079 / 900 / 899 / 768 / 640 / 639 / 560 / 390에서 헤더 가로 스크롤 0, 요소 겹침 0, 객체 수정 면 밖으로 넘침 0. 필드 값·라벨 잘림은 390px의 레이어명 1건뿐입니다. 각 폭에서 더보기 항목 선택 시 Callback이 호출되고, 선택 상태가 반영되며, 메뉴가 닫히는 것을 확인했습니다(속성Bar·속성창 체크 전환, 인쇄·팝업 가이드 Callback 포함). 실제 Viewport 1799 / 1079 / 390에서도 같은 결과입니다.
## 13. 컴포넌트 분리 기준
- 공개: `SWHeader`(전체), `SWHeaderHome`, `SWHeaderDocumentTitle`, `SWHeaderLayerControl`, `SWHeaderLayerMenu`(레이어 필드 + Dropdown, §5.2), `SWHeaderColorMenu` · `SWHeaderLineStyleMenu` · `SWHeaderTextStyleMenu` · `SWHeaderDimensionStyleMenu`(§5.3), `SWHeaderLinePreview` · `swHeaderLinePatterns`, `SWHeaderInput` · `parseSWHeaderColor` · `parseSWHeaderNumber`(§5.5), `SWHeaderColorControl`, `SWHeaderLineStyle`, `SWHeaderTextStyle`, `SWHeaderDimensionStyle`, `SWHeaderProperties`, `SWHeaderActions`, `SWHeaderWindowControls`, `SWTheme`(단독 사용 시 토큰 범위).
- 내부: `SWHeaderField`/`SWHeaderFieldText`(5개 메뉴 공통 면·라벨/값), `SWHeaderMore`(더보기), `SWHeaderDocumentItem`(문서 Dropdown 행: 선택 버튼 + 문서 닫기 버튼). 문서 Dropdown은 `SWHeaderDocumentTitle`이 소유하며 `documents`·Callback으로 다른 SW에서도 그대로 재사용합니다. 같은 필드 스타일을 메뉴별로 중복 구현하지 않습니다.
- 데이터는 Props, 사용자 동작은 Callback입니다. 파일 열기·창 이동·최소화·종료·도면 처리는 패키지 밖(호스트)에서 구현합니다.
## 14. SW 전용 토큰 목록
`.hm-sw-header`와 `.hm-sw-theme`(= `<SWTheme>`)에 정의합니다. `@hm-design/ui` 토큰을 참조·변경하지 않습니다.
| 토큰 | 기본값 | 사용처 |
|---|---|---|
| `--hm-sw-header-height` / `-bar-height` / `-field-height` | 49px / 48px / 38px | 헤더 / 면 / 필드 |
| `--hm-sw-header-logo-width` / `-logo-height` | 32px / 17px | 로고 영역(공통 배치, 로고 이미지는 프로그램별) |
| `--hm-sw-header-gap` | 4px | 필드 간격 |
| `--hm-sw-header-font-family` / `-font-size` / `-label-size` | Noto Sans KR… / 14px / 12px | 전체 / 값 / 라벨 |
| `--hm-sw-header-background` | `#060606` | 헤더 바탕 |
| `--hm-sw-header-text-color` / `-title-color` | `#F6F4F2` / `#F0F7F5` | 일반 / 파일명 |
| `--hm-sw-header-selected-color` | `#FFC600` | 선택 |
| `--hm-sw-header-focus-color` | `#F0F7F5` | Focus |
| `--hm-sw-header-arrow-color` | `#47AF91` | 파일명 화살표 |
| `--hm-sw-header-hover` | `rgba(255,255,255,.08)` | 더보기 메뉴 항목 Hover 배경 |
| `--hm-sw-header-hover-border-gradient` / `-hover-border-width` | Figma 홈 hover gradient / 1px | Hover 테두리(§11.1) |
| `--hm-sw-header-document-menu-width` / `-document-menu-background` / `-document-menu-border-color` / `-document-menu-border-width` | 310px / `#103127` / `#041611` / 1px | 문서 Dropdown(§5.1) |
| `--hm-sw-header-document-menu-shadow` / `-document-menu-inset-shadow` / `-document-menu-divider-color` | `0 2px 4px rgba(0,0,0,.25)` / `inset 0 4px 8px rgba(0,0,0,.4)` / `rgba(194,217,210,.2)` | 문서 Dropdown(§5.1) |
| `--hm-sw-header-document-list-max-height` | 320px | 목록 내부 스크롤 시작 높이(구현값) |
| `--hm-sw-header-document-action-background` / `-document-action-color` | `rgba(182,208,201,.2)` / `rgba(0,0,0,.7)` | New / Open Default(§5.1) |
| `--hm-sw-header-document-action-hover-background` / `-document-action-hover-color` | `rgba(182,208,201,.3)` / `#000` | New / Open Hover(Figma 26:10762 / 26:10805) |
| `--hm-sw-header-document-item-hover-background` | `rgba(0,0,0,.4)` | 문서 목록 항목 Hover(Figma 26:10680) |
| `--hm-sw-header-document-close-color` / `-document-close-hover-background` / `-document-close-hover-color` | `rgba(240,247,245,.7)` / `rgba(182,208,201,.2)` / `#F0F7F5` | 문서 닫기 X(사용자 추가, 구현값, §5.1.1) |
| `--hm-sw-header-layer-menu-width` / `-layer-menu-background` / `-layer-menu-border-color` / `-layer-menu-radius` / `-layer-menu-shadow` | 234px / `#27241D` / `#000` / 3px / `0 4px 8px rgba(16,48,38,.25)` | 레이어 Dropdown(§5.2) |
| `--hm-sw-header-layer-expanded-border-color` | `#081813` | 펼친 레이어 필드 테두리(Variant3) |
| `--hm-sw-header-layer-list-max-height` | 240px | 레이어 목록 내부 스크롤 시작 높이(구현값, 8행) |
| `--hm-sw-header-layer-row-hover-background` / `-layer-row-hover-color` | `#62533D` / `#FF5C00` | 레이어 행 Hover(26:10695) |
| `--hm-sw-header-layer-icon-hover-background` | `rgba(0,0,0,.25)` | 전구 Hover(눈꽃·자물쇠는 Hover SVG) |
| `--hm-sw-header-layer-chip-border-color` | `#C9C9C9` | 레이어 색상 견본 테두리 |
| `--hm-sw-header-layer-footer-border-color` / `-layer-detail-background` / `-layer-detail-color` / `-layer-detail-text-opacity` | `rgba(197,177,158,.15)` / `rgba(197,177,158,.15)` / `#FFF` / .35 | 레이어 상세 영역·버튼 |
| `--hm-sw-header-document-background` / `-document-border-color` | §10 B / `#0E4C3A` | 파일명, 홈 뒤, 창 영역, 인쇄 버튼 |
| `--hm-sw-header-object-background` / `-object-border-color` / `-object-radius` | §10 A / `#135643` / 6px | 객체 수정 면, 더보기 메뉴 테두리 |
| `--hm-sw-header-property-background` / `-property-border-color` / `-property-radius` / `-property-shadow` | §10 C / `#08251C` / 3px / §10 C | 5개 필드, 더보기 버튼 |
| `--hm-sw-header-pill-background` / `-pill-border-color` | `rgba(0,0,0,.8)` / `#092920` | 인쇄·가이드 pill |
| `--hm-sw-header-stripe-color` | `rgba(28,106,81,.32)` | 창 이동 줄무늬 |
| `--hm-sw-header-window-icon-color` | `#ADB0AF` | 최소화 막대 |
| `--hm-sw-header-window-button-hover-background` / `-window-icon-hover-color` | `rgba(0,0,0,.2)` / `#FFF` | 최소화 Hover(Figma 26:12159). 종료 Hover는 Figma SVG(`close-hover.svg`) |
| `--hm-sw-header-text-outline-color` / `-text-outline` / `-label-outline` | `#000` / 1.3px 16방향 / 1px 16방향 | Text Stroke 검정 외곽선(§6.1) |
| `--hm-sw-header-text-stroke-width` / `-solid-stroke-width` / `-text-outline-room` | 0.6px / 0.8px / 2px | 자기 색 Stroke(바깥 0.3 / 0.4px), 말줄임 상자 외곽선 여유 |
| `--hm-sw-header-menu-divider-color` / `-menu-divider-strong-color` | `rgba(197,177,158,.15)` / `.25` | 새 Dropdown 열 구분선·하단 테두리(§5.3) |
| `--hm-sw-header-menu-list-max-height` / `-color-palette-max-height` | 240px / 118px | 긴 목록·사용자 색상 내부 스크롤 시작(구현값) |
| `--hm-sw-header-color-mode-color` / `-color-mode-background` / `-color-mode-on-background` | `#D4CBBD` / `rgba(98,83,61,.3)` / `#62533D` | ByLayer / ByBlock |
| `--hm-sw-header-color-pick-background` / `-color-chip-border-color` / `-color-input-border-color` | `#C5B19E → #5F554C` / `#C9C9C9` / `rgba(197,177,158,.2)` | 색상 추출, 22px 칩, 색상 입력 Hover·구분선 |
| `--hm-sw-header-input-color` / `-input-hover-border-color` / `-input-editing-background` / `-input-editing-border-color` | `#E60403` / `rgba(212,203,189,.25)` / `#0F0D0A` / `rgba(212,203,189,.3)` | `SWHeaderInput`(§5.5), caret 색 포함 |
## 15. Figma 비교 결과와 의도적 차이
1920 × 49, Storybook `SW / Header / Default`에서 `getBoundingClientRect`로 측정했습니다.
| 항목 | 판정 | 내용 |
|---|---|---|
| 전체 크기 49 / 면 48 | MATCH | |
| 면 좌표·폭(홈 0/80, 파일명 65/310, 객체 377/1301, 창 1680/240) | MATCH | |
| 필드 좌표·폭(467/234, 705/192, 901/284, 1189/164, 1357/164, 상세보기 1521/157 y6) | MATCH | |
| 인쇄·가이드 pill(1683/85, y3), 창 이동(1768/88), 최소화·종료(1856, 1888 / 32) | MATCH | |
| 사용자 확정 3개 면(gradient·border·radius·shadow) | MATCH | computed style 확인 |
| Assets 12개 위치·크기, 임시 URL 없음 | MATCH | |
| 문서 Dropdown(위치 x 63–375 / y 48, 312 × 170, 항목 32, 하단 40, 색·테두리·radius·그림자, 열린 상태 노란 글자·위 화살표) | MATCH | 2026-10-16 Storybook `Document Dropdown`에서 측정. §5.1 |
| 문서 Dropdown 열기 방식(Figma `hover` Variant → 클릭) | APPROVED DIFFERENCE | 사용자 지시 |
| 레이어 Dropdown(필드 234 × 38, 목록 234 × 280, 행 30, 아이콘 20, 견본 16, `#27241D`·`#62533D`·`#FF5C00`, 아이콘 Hover, 레이어 상세, 펼침 외곽선) | MATCH | 2026-10-16 Storybook `Layer Dropdown`에서 측정. §5.2 |
| 레이어 Dropdown 열기 방식(Figma hover → Variant3 → 클릭) | MATCH | 사용자 지시와 Figma 흐름 동일: Hover는 테두리만, 클릭 시 펼침 |
| 레이어 상세 버튼 폭(Figma 글자 75 → 69.3, 버튼 107.3) | APPROVED DIFFERENCE | 글꼴 차이(Dotum → Noto Sans KR) |
| 눈꽃·자물쇠 컬러 = 동결 해제·잠금 해제 | APPROVED(사용자 확정 2026-10-16) | Figma에는 의미 정의 없음, 사용자 지시로 확정(§5.2.1). 실제 호스트 반영은 NOT VERIFIED |
| 현재 레이어 이름 `#FFC600`, 목록 높이 상한 240px, 좁은 폭 왼쪽 이동 | 구현값(Figma 없음) | 문서 Dropdown 규칙 확장 / 8행 기준 / 화면 밖 넘침 방지 |
| New / Open 버튼 폭 54 / 47 → 55.4 / 50 | APPROVED DIFFERENCE | 글꼴 차이(Dotum → Noto Sans KR) |
| 로고 이미지 | APPROVED DIFFERENCE | Figma 이지빔 로고 대신 프로그램별 로고(`logoSrc`/`logo`). 로고 영역 위치·크기는 MATCH. §9.1 |
| 글꼴 Dotum → Noto Sans KR, Bold 표기 → 700 | APPROVED DIFFERENCE | 글자 폭이 넓어 값 텍스트 위치가 수 px 다름 |
| 글자 겹침 레이어(검정+밝은색) → 단일 밝은색 | ~~APPROVED DIFFERENCE~~ → 2026-10-16 복원 | §6.1 Text Stroke. 남은 차이: Noto 700 + Stroke가 Dotum + Stroke보다 약간 굵음 |
| 색상·선스타일·문자·치수 Dropdown 구조·크기·색 | MATCH (2026-10-16 측정) | §5.3. 차이: 선 종류 열 182 → 187.2(Noto 글자 폭), 문자스타일 글꼴 칸 38 → 내용 폭, 상세 pill 글자 폭 |
| Dropdown 독립 버튼 Hover | APPROVED DIFFERENCE | Figma Hover Variant 있음, 사용자 정책으로 Hover 효과 없음(§5.3) |
| 레이어 상세 버튼 Hover(.25 / radius 25) | APPROVED DIFFERENCE | 같은 정책(§5.5) |
| Input Default / Hover / 편집 / 커서 | MATCH (58 × 20 / 58 × 24 / 58 × 24, caret `#E60403`) | caret 높이 14px은 NOT VERIFIED(네이티브) |
| 선택 시 레이어 상태 아이콘 색 유지 | APPROVED DIFFERENCE 후보 | 다색 상태 아이콘 의미 보존. 확정 필요 |
| 파일명 화살표 SVG → CSS 삼각형 | APPROVED DIFFERENCE 후보 | 선택 색 전환용, 크기·색 동일 |
| 홈 버튼 Hover | MATCH | Figma hover SVG 그대로 사용(26:10655) |
| 최소화 Hover(배경 검정 20%, 막대 흰색, 32 × 48) | MATCH | Figma 26:12159 값과 computed style 비교(§11.2) |
| 종료 Hover(`#E40000` 32 × 48, 흰 X) | MATCH | Figma 26:12166 SVG 그대로 사용(§11.2) |
| 인쇄·팝업 가이드 Hover 아이콘 `#B6D0C9` → `#FFC600` | APPROVED DIFFERENCE(확장) | Figma Variant 없음. 2026-10-16 사용자 지시, 해당 fill만 바꾼 로컬 SVG(§11.2) |
| 다른 버튼 Hover 테두리 | APPROVED DIFFERENCE(확장) | Figma에는 홈 버튼 hover만 있음. 같은 gradient를 각 버튼 형태·radius에 맞춰 적용(2026-10-16 사용자 지시). §11.1 |
| Focus·Disabled 표현 | NOT VERIFIED | Figma에 Variant 없음, 구현값 |
| 반응형 단계(1800 / 1180 / 1080 / 900 / 640 / 520) | MATCH(정책) | 2026-10-16 사용자 지시에 따라 측정값으로 재설정, 필드 값 잘림 없음(390 레이어명 제외). §12 |
| 픽셀 단위 이미지 diff | NOT VERIFIED | Figma 스크린샷은 1024px 축소본만 확보, 육안·좌표 비교만 수행 |
## 16. AI 재현 규칙
SW Header는 Figma Node 26:8944를 기준으로 구현한다. 사용자가 확정한 CSS와 반응형 정책을 우선하며, SW 전용 디자인 토큰을 사용한다. HMDesign 공통 UI의 전역 스타일을 변경하지 않는다. 동일한 디자인을 재현할 때 전체 헤더를 이미지로 대체하지 않고 재사용 가능한 컴포넌트로 구성한다.
추가 규칙:
1. 새 화면에서는 `@hm-design/sw`의 `SWHeader`를 그대로 쓰고 값·Callback만 연결한다. 헤더 DOM이나 class를 복사하지 않는다.
2. 하위 컨트롤만 쓸 때는 `<SWTheme>` 안에 공개 하위 컴포넌트를 둔다. 소비 코드에서 `hm-sw-*` class를 직접 붙이지 않는다.
3. 색·gradient는 `--hm-sw-header-*` 토큰으로만 바꾼다. `@hm-design/ui` 토큰과 섞거나 같은 HEX라는 이유로 합치지 않는다.
4. Font Weight는 400 / 700만 쓴다(500은 허용되나 현재 미사용). 글자 외곽선은 Figma에 Stroke가 있는 텍스트에만 §6.1 토큰으로 적용한다(2026-10-16, 이전 "외곽선 없음" 규칙 대체). `font-weight`로 외곽선을 흉내 내지 않는다.
10. Dropdown 안 독립 버튼은 Hover 효과를 넣지 않는다. 목록 행은 Figma 행 Hover, Input은 Figma Hover를 따른다(§5.3, §5.5). Dropdown 항목은 패키지에 고정하지 않고 `id` 기반 배열 Props로 받는다.
5. 선택은 글자·단색 아이콘 `#FFC600`만 바꾸고 배경·테두리·그림자를 추가하지 않는다. Focus 표시를 지우지 않는다.
6. 기능이 화면에서 빠질 때는 반드시 더보기로 옮긴다. 필드를 줄여 값을 자르지 말고 기능을 옮긴다. 헤더 가로 스크롤을 만들지 않는다.
7. Figma Assets는 `packages/sw/src/assets/`의 원본 SVG를 쓴다. 임시 Figma URL·스크린샷 이미지를 쓰지 않는다.
8. 로고는 확정 디자인이 아니다. 공통 컴포넌트에 특정 SW 로고·브랜드명·파일 경로를 넣지 않고, 각 프로그램이 `logoSrc` 또는 `logo`와 `logoAlt`로 전달한다. 로고 영역의 위치·크기는 바꾸지 않는다.
9. Hover 테두리는 §11.1의 노란 gradient 1px을 사용한다. 배경·크기·그림자를 바꾸지 않고, 새 버튼도 같은 토큰과 대상 목록 규칙을 따른다. 텍스트·아이콘 예외는 §11.1·§11.2·§11.5를 따른다(5개 필드의 텍스트와 지정 미리보기는 §11.5, 최소화·종료는 Figma 전용 hover, 인쇄·팝업 가이드는 기존 테두리 + 지정 fill 변경). SVG는 원본의 해당 foreground fill만 바꾼 로컬 Variant를 사용하며 CSS filter를 쓰지 않는다.
## 17. 향후 공통화 검토 참고
이번 단계에서는 통합하지 않습니다. 비교 시 참고합니다.
| 구분 | 항목 |
|---|---|
| SW 고유(분리 유지) | 3개 확정 gradient 면, 홈·창 이동·pill 구성, 선택 `#FFC600`(배경 없음), 49px 헤더 구조 |
| 유사하지만 의미 다름 | 선택 노랑 `#FFC600`(HM 팝업 제목 노랑과 같은 값이나 역할 다름), 녹색 계열(HM `green-*` palette와 값 비교 필요) |
| 공통화 후보 | Noto Sans KR·400/500/700 정책, 더보기 메뉴(HM `DropdownMenu`와 기능 동일 — 스타일은 SW 전용), Focus 정책, 말줄임·no-scroll 반응형 원칙 |
| 비교 필요 | 필드 38px vs HM control height, radius 3/6 vs HM radius scale, Hover 색 |
## 추가 수정 기준 — 속성창·K 가이드 (2026-10-16)
사용자의 SW Header 추가 요청 A–H가 같은 날짜의 이전 규칙보다 우선합니다. 이 절은 과거 PASS 기록을 재판정하거나 덮어쓰지 않습니다.
- 상세 5개는 높이 24px, 고정 폭 레이어 113 / 색상 99 / 선 128 / 문자·치수 142px입니다. Default 배경 `rgba(197,177,158,.15)`·radius 24 → Hover .25·radius 25, 아이콘 14·gap 4·가로 padding 10을 유지합니다. 레이어·색상·선·문자는 원본 Hover 아이콘으로 바뀌고 글자는 35%입니다.
- 치수 상세의 실제 Hover `26:10799`는 기본 아이콘·글자 100%입니다. `26:10802` Variant3(Hover 아이콘·글자 35%)와 혼합하지 않습니다. Hover는 Callback을 호출하지 않고 기존 클릭·disabled·focus-visible을 유지합니다.
- ByLayer/ByBlock은 Hover 글자 `#FF5C00`, 선택 배경 `#62533D`·기존 Inset Shadow를 유지합니다. 항상 하나만 표시되고 같은 모드 재클릭은 Callback을 호출하지 않습니다. 처음 모드를 지정하지 않으면 ByLayer입니다.
- `colorMode=null`의 기존 직접 색상 API 의미는 유지합니다. 필수 모드 **표시**는 마지막 유효한 모드를 기억하며 팔레트 선택·직접 입력으로 지워지지 않습니다. 모드 표시를 색상 데이터 변경으로 해석하지 않습니다.
- 레이어·선·문자·치수 목록의 Hover/Selected는 배경 `#62533D`·글자/선 `#FF5C00`로 동일합니다. 치수는 기존 Hover SVG를 씁니다. 마우스가 다른 행으로 이동해도 선택 행 강조는 남고 Props의 id 변경으로 이동합니다. 문서 목록과 상단 필드의 기존 노랑 정책은 유지합니다.
- 지정된 일반 `-webkit-text-stroke-width: var(--hm-sw-header-text-stroke-width)` 선언과 미사용 변수는 제거했습니다. 검정 `text-shadow`, 상세 pill의 별도 `--hm-sw-header-solid-stroke-width`, SVG/Border/선 Stroke는 보존했습니다.
- 속성창 `26:11822`는 `SWHeaderPropertyPanel` 하나가 외부 선택·항목·그룹·펼침 데이터로 렌더링합니다. 선택 없음/전체/단일 원/공통 항목 확장의 Story fixture는 314×256 / 314×361 / 314×658 / 314×754px입니다. CAD 값 계산과 객체 변경은 하지 않습니다.
- 너비 314px, 내용에 따른 높이, viewport 경계 보정, 내부 세로 스크롤, non-modal dialog(`aria-modal=false`)입니다. Dim은 없습니다. 기존 속성창 버튼 또는 More → 속성창으로 열고 재클릭·X·Escape로 닫습니다. 닫으면 보이는 원래 버튼/More로 Focus를 돌립니다.
- 항목은 id·이름·값·type·groupId·order·readOnly·disabled·validate·unit·Callback을 갖습니다. Text/Number/Select/Color/Slider/Computed를 지원합니다. `SWHeaderInput`을 재사용하고 Enter/Blur 확정·변경 없는 중복 보고 방지·Escape 취소·외부 값 반영을 적용합니다. 선택 id 교체/항목 삭제는 편집 임시값을 폐기합니다. 그룹 펼침은 값 변경과 분리됩니다.
- 공통 입력 외 Circle 레이아웃은 원본 208px SVG와 호스트 수치/graphicSlot을 결합하고, Position 레이아웃은 동적 열을 사용합니다. 원형 항목의 추가 일반 속성도 도형 아래에 배치됩니다. 모든 fixture 숫자·객체 종류/개수는 Storybook 데이터입니다.
- 내부 아이콘 29×27.13px은 원본 Default/Hover/Active SVG를 사용합니다(원본 SVG export root는 29×28로 반올림되며 root를 재조정하지 않습니다). 금색 Active 테두리와 다시 클릭하면 Default로 돌아가는 상태를 보존합니다. 현재 저장소에 CAD 액션 계약이 없어 `toolActive/onToolActiveChange/toolLabel`을 통해 호스트에만 알립니다.
- 닫기 버튼 44×30px은 속성창 전용 원본 SVG입니다. Hover는 빨강/흰 X이며 Header 창 종료와 별개입니다.
- K 버튼은 기존 40×38px 버튼 하나를 유지합니다. 하단 가이드는 원본 이미지/마스크·208×149px·금색 경계·짙은 녹색·문구를 유지합니다. 표시 Fade는 511ms입니다.
- K 원본 이미지 Pan/Zoom은 400ms 지연 → 1022ms 전환 → 100ms 지연 → 1022ms 전환 → 150ms 지연 → 511ms 전환입니다. 마지막 화면은 400ms 유지 후 511ms로 처음에 복귀합니다(반복을 위한 구현값). 재진입은 처음부터 재생하고 영역을 떠나면 타이머를 정리합니다. Reduced Motion은 첫 프레임 정지 이미지입니다.
- 버튼과 가이드는 한 Hover/Focus 영역입니다. 기존 640px Breakpoint 아래에서는 K 버튼을 추가 생성하지 않고 기존 More의 팝업 가이드 항목 Hover/Focus에 같은 가이드를 연결합니다. 기존 창 이동 1400ms CSS와 자산은 변경하지 않습니다.
- 이 패키지는 HM UI와 독립된 SW control library이므로 panel의 native label/select/range는 SW 전용 스타일·실제 control id로 연결합니다. HM UI의 Label이나 Select를 새 의존성으로 추가하지 않습니다. Story의 외부 데이터 조작 버튼은 테스트 호스트입니다.
검증·차이·NOT VERIFIED는 [추가 검증 기록](./sw-header-additional-verification-2026-10-16.md)을 확인합니다.
## 후속 5개 요구사항 기준 (2026-10-16)
- **속성Bar 26:10492:** 최대 1222×28px, #271F13, 좌우·하단 1px #08251C, 하단 radius 6px, inset 0 4px 4px black 60%. 객체명 90px·#FFC600, 필드간 16px·라벨/값 간 6px, 라벨 #F6F4F2·입력 #E60403. 기존 Noto Sans KR 400/700 정책과 SWHeaderInput을 재사용하므로 Dotum 원본과 획·폭은 다릅니다. 기존 Header 49px 아래의 별도 strip이며 객체 수정 영역에 정렬하고 좁으면 내부 가로 스크롤합니다. 실제 CAD 계산은 없습니다.
- **동적 데이터:** 속성창과 동일 SWHeaderProperty/Selection 레코드, onPropertyChange를 공유할 수 있습니다. id/order/name/value/validation/readOnly/disabled는 외부 데이터이며 Enter/Blur·Escape·외부 변경·삭제 draft 폐기를 적용합니다. Bar와 Panel 열림은 독립적입니다.
- **컨트롤 구성:** 선택적 controls allowlist로 파일·레이어·색상·선/문자/치수스타일·속성Bar·속성창·상세보기 caption·그리기모드·인쇄·K를 개별 제거/숨김/재추가/재정렬/허용 라벨 변경합니다. 미전달 시 기존 기본 디자인을 유지하며, 구성 모드에서만 개별 순서대로 한 영역에 배치합니다. 홈·창 제어는 공통 프레임으로 유지합니다. 기존 Breakpoint와 More 대표 정책은 남은 항목에 적용합니다.
- **Text Stroke:** Text Stroke Story의 지정 header 원본 스크린샷 블록과 전용 import만 제거했습니다. 원본 PNG·다른 비교 Story·타이포그래피·색상·검정 text-shadow는 보존합니다.
- **반복 선언 원인:** src/theme/sw.css 원본 1394행(작업 전)에 별도 solid-stroke-width 참조가 실제 존재했습니다. build.mjs는 src 진입점의 CSS를 dist/styles.css로 재빌드하고 Storybook은 공개 dist CSS를 불러옵니다. 별도 생성기가 해당 선언을 추가하는 증거는 없으며 build가 src를 역생성하지도 않습니다. 원본 선언과 유일한 변수 정의를 제거했습니다. 과거 `text-stroke-width` 제거와 다른 수정입니다. 과거 사용자가 편집한 파일 위치·캐시는 직접 재현할 수 없으므로 특정 사용자 행위를 원인으로 단정하지 않습니다.
- **btn_colorize 26:11301:** 색상 메뉴의 기존 색상 추출 버튼에 Default 26:11302 / Hover 26:11305 SVG를 로컬 자산으로 저장해 연결했습니다. 24×24 버튼, 18×18 아이콘, radius 3, gradient #C5B19E→#5F554C, 기본 border black40%, drop shadow 0 2px 2px black40%. Hover border #DA6813·black25% overlay·Hover 전용 SVG, Mouse Leave 복원. 키보드 focus-visible와 접근성 이름을 제공합니다. 클릭은 기존 onColorPick이며 색상 값/모드를 바꾸지 않습니다.
- **Overlay:** 26:11338은 현재 구현에 없고 호스트 API 연동 대상입니다. 임의 색상창을 생성하지 않았습니다.
- **재사용 근거:** 신규 업무 페이지가 아닌 독립 SW component package 수정입니다. SWHeaderInput/Property 타입/기존 메뉴·More를 재사용했습니다. HM UI 의존성·FilterBar·일반 Navigation은 이 구성요소의 역할에 맞지 않아 추가하지 않았습니다. native label은 실제 input id를 연결하며 기존 패키지 독립성을 유지합니다.
새 검증과 과거 기록은 [후속 검증 기록](./sw-header-followup-five-verification-2026-10-16.md)에서 구분합니다.
## 3차 기준 — 구성요소 표시·메뉴 순서·속성창 상태·형상 Slot (2026-10-16)
- **로고 필수:** 로고 슬롯(32×17, 홈 배경 위 12,16)은 모든 구성에서 표시합니다. `home` 제거 시 같은 배경·위치의 정적 로고(Hover 없음)입니다.
- **개별 제거:** `hiddenControls`로 홈·파일·그리기모드·5개 메뉴·상세보기 caption·속성Bar·속성창·인쇄·K·창 이동·최소화·종료를 각각 제거합니다. 남은 요소는 기본 크기를 유지하고 제거한 공간은 남기지 않습니다(구현값: 상세보기 블록 일부 제거 시 폭 auto·최소 64px, 인쇄/K pill과 창 버튼은 남은 버튼 폭, 창 영역 폭 auto). 아무것도 제거하지 않으면 기존 1920px 디자인과 동일합니다.
- **창 이동 Tooltip(구현값):** 최소화·종료 중 하나 이상을 제거하면 창 영역 오른쪽 밖은 잘리고(Header 밖 노출 방지), 1080px 미만 24px 이동 영역의 Tooltip은 이동 영역 왼쪽에 표시합니다. 기본 구성의 1400ms Motion·위치는 변경하지 않았습니다.
- **메뉴 순서:** `menuOrder`는 5개 유사 메뉴끼리만 자리를 바꿉니다. 기본은 레이어 → 색상 → 선스타일 → 문자스타일 → 치수스타일(Figma 26:8944)입니다.
- **More:** Breakpoint는 그대로입니다. 제거된 메뉴는 More에도 없고, More 목록은 현재 순서를 따릅니다. 대표 슬롯은 레이어(없으면 순서상 첫 메뉴)이며 More 선택 메뉴가 대신합니다.
- **속성창 4상태:** 26:11886 314×256, 26:11823 314×361, 26:11932 314×658, 26:12035 314×754 — 같은 SWHeaderPropertyPanel과 데이터로 구성하며 Storybook 실측이 일치합니다.
- **형상 Slot:** Figma 26:11932 `Group 2937`(219×260) 안 `Group 2920` 208×208 그림 영역을 `.hm-sw-property__shape`(그룹 콘텐츠 기준 39, 26)로 분리했습니다. 기본 내용은 기존 Figma 원형 SVG(`property-circle.svg`)입니다. 비움·외부 컴포넌트에도 라벨 위치와 패널 높이는 같습니다.
- **Figma와 남은 차이(기존 구현 유지):** Figma 라벨 x 위치는 원주 111.5/지름 102/반지름 152.5/면적 98.5이고 구현은 113/100/154/100(1~2px, 기존 값), 지름 y는 Figma 108·구현 110입니다. 이번 범위에서 값 변경은 하지 않았습니다.
@@ -0,0 +1,45 @@
# SW Header 상세 버튼 5개 공통화 검증 — 2026-10-16
1. **최종 판정: PASS(요청 범위 / Chromium).** 실제 Pointer Hover를 포함한 주 검증 256건, 보존 확인 15건 PASS. 범용 Guard의 미검증은 아래 15번에 별도로 남깁니다. 과거 보고서를 덮어쓰지 않았습니다.
2. **시작 Git:** `D:\디자인시스템\HMDesign-SW`, `feature/sw-package`, HEAD `9407c8567f57301659e591c03451cca1b828733c`. `M package.json`, `M package-lock.json`, `?? docs/sw/`, `?? packages/sw/`, `?? src/sw/`가 작업 전부터 있었습니다. 해당 미커밋·미추적 작업을 보존했습니다. 작업별 경로·브랜치 및 Commit/Push 보류 지시를 적용했습니다.
3. **기존 구조:** 색상·선·문자·치수는 `SWHeaderDropdown.tsx`의 내부 공통 `SWHeaderMenuDetail`을 사용했으나 `disabled={!onClick}`으로 Callback 부재 시 Hover가 차단됐습니다. 레이어는 별도 JSX, 치수는 글자 100%·Default 아이콘을 사용하는 별도 Hover 규칙이 있었습니다.
4. **공통화:** 기존 `SWHeaderMenuDetail`을 보완하고 레이어까지 연결했습니다. 새로운 중복 컴포넌트나 공개 export는 추가하지 않았습니다. `SWHeaderMenuDetailProps`는 native `ButtonHTMLAttributes`를 확장하며 `label`, `icon`, `onClick`, `disabled`, `className`, `aria-label` 등을 지원합니다. 장식 아이콘과 접근성 이름을 구분합니다. 레이어의 기존 class와 토큰 이름은 유지했습니다. 상태 CSS는 `.hm-sw-header__menu-detail` 한 곳에서 관리하고 버튼별 CSS에는 폭만 남겼습니다.
5. **문자스타일 기준:** Figma 파일 `rj6wpsfk24lOWvWiVRAANP`, Default `26:10789` / Hover `26:10792`를 Plugin API 읽기와 스크린샷으로 확인했습니다. 변경 전 실제 문자스타일 버튼을 브라우저로 측정한 `detail-button-qa/baseline.json`의 `textDefault` / `textHover`가 구현 기준입니다. Figma의 배경 .15/.25, radius 24/25, 14px 아이콘, gap 4, padding 5/10, 글자 35%, 아이콘 내부 SVG 20%/35%와 일치합니다. Disabled 전용 Figma Variant는 이 2개 노드에 없으며 현재 Default 외형을 보존하고 클릭만 차단했습니다.
6. **Default 5개:** 레이어 113×24, 색상 99×24, 선스타일 128×24, 문자스타일 142×24, 치수스타일 142×24px. 전부 배경 `rgba(197,177,158,0.15)`, 글자 `rgb(255,255,255)`와 opacity .35, radius 24px, border 0, box-shadow none, Text Stroke 0px. 아이콘 요소 opacity는 1이며 SVG path 내부 opacity .2입니다. 기존 레이블·아이콘을 유지했습니다.
7. **Hover 5개:** 실제 `locator.hover()` 후 전부 배경 `rgba(197,177,158,0.25)`, radius 25px, 글자 opacity .35, SVG path .35로 전환. 밝은 치수 예외는 제거했습니다. Mouse Leave 시 5개 모두 Default로 복귀했습니다. CSS 강제 pseudo 상태만으로 PASS 처리하지 않았습니다.
8. **Disabled:** 명시적 `disabled`만 적용하며 Callback 유무와 분리했습니다. 5개 모두 Default 외형 유지, 실제 포인터 클릭 시 Callback 없음. Hover 차단, 키보드 포커스 제외는 native disabled 동작입니다. 기존 fieldset disabled도 native `:disabled` 규칙을 따릅니다. 일반 버튼은 키보드 Focus-visible 윤곽선을 유지합니다.
9. **Callback:** 비교 Story에서 각 일반 버튼 클릭 1회·Enter 1회와 Disabled 클릭 차단을 확인했습니다. Callback 없는 5개도 실제 Hover·클릭을 수행했고 오류가 없습니다. 실제 Dropdown 5종에서 기존 `onLayerDetail` / `onColorDetail` / `onLineStyleDetail` / `onTextStyleDetail` / `onDimensionStyleDetail` 호출·닫힘·필드 Focus 복귀를 확인했습니다. 호스트 Callback이 없으면 실행·팝업·CAD 처리를 흉내 내지 않고 무동작입니다.
10. **Computed Style 차이:** 동일 상태의 5개 스타일은 변경 전 문자스타일 기준과 비교하여 차이 0(폭·텍스트의 의도된 차이 제외). 배경·color·opacity·border·radius·box-shadow·글자색/opacity/Stroke·아이콘 opacity/content를 비교했습니다. 행 Hover는 선·문자·치수 목록에서 기존 `rgb(98,83,61)` 유지. 상세 버튼 외 목록/Selected/상단 Header Hover 규칙은 수정하지 않았습니다. 전체 실측값은 [주 검증 JSON](./detail-button-qa/verify-result.json)에 있습니다.
11. **Story 및 회귀:** 기존 31개 유지 + verification Story 1개 = **32개 렌더 PASS**, page/console 오류 0. Default·Controls 2개 Story 각각 10폭(1920/1800/1440/1180/1080/900/768/640/520/390)에서 Header 포함 99개 요소의 위치·크기·display·visibility·color·background를 전후 비교해 차이 0. 11개 구성 프리셋×7폭 = **77/77 PASS**(메뉴 DOM 순서·제거·Header 높이·overflow 및 More 비어 있지 않음). 390/768/1280/1440에서 Header·문서 가로 overflow 0. 속성창 4상태는 314×256/361/658/754px, 창 이동은 3개 1400ms 애니메이션과 Leave 정리, K는 511ms 진입과 Leave 닫힘, Colorize 24px, 기존 편집 Input과 caret 색을 확인했습니다. [보존 확인 JSON](./detail-button-qa/preservation-result.json). 속성Bar·형상 Slot을 포함한 기존 Story가 렌더링됐으며 그 기능 소스는 수정하지 않았습니다. 기존 각 Story의 모든 상호작용을 전수 재검사했다는 뜻은 아닙니다.
12. **Typecheck / Build:** `npm.cmd run typecheck:sw`, `npm.cmd run build:sw`, `npm.cmd run typecheck` 모두 exit 0. Root typecheck의 pretypecheck가 package build를 수행한 후 동일 산출물을 사용해 `npm.cmd --ignore-scripts run build`, `npm.cmd --ignore-scripts run build-storybook`을 실행해 exit 0을 확인했습니다. 후속 Story 설명 수정은 Storybook만 다시 빌드했습니다. 기존 `bg-[var(...)]` CSS 최적화 경고와 chunk 크기 경고가 있으며 빌드 실패는 없습니다. 로그는 `detail-button-qa/root-typecheck.log`, `root-build.log`, `storybook-build.log`에 있습니다. 최종 브라우저 검증은 정적 Storybook 빌드를 로컬 HTTP로 제공해 실행했습니다. 패키지 재빌드 중 dev HMR 404가 발생한 시험은 PASS로 사용하지 않았습니다.
13. **변경 파일:** `packages/sw/src/components/sw-header/components/` 아래 `SWHeaderDropdown.tsx`, `SWHeaderLayerMenu.tsx`, `SWHeaderStyleListMenus.tsx`, `SWHeaderColorMenu.tsx`, `SWHeaderLineStyleMenu.tsx`; `packages/sw/src/theme/sw.css`; `src/sw/sw-header.stories.tsx`, `src/sw/sw-stories.css`; `docs/sw/sw-header-usage.md`, `docs/sw/sw-header-design-spec.md`; 신규 `packages/sw/tests/detail-buttons.browser.mjs`, 이 보고서, `docs/sw/detail-button-qa/` 증거. Color/Line 파일은 Callback 설명 주석만 갱신했습니다. Build가 gitignored `dist`·`storybook-static`도 갱신했습니다. `package.json`/lock·UI/OrgChart/Center Graphic/기존 공개 export·기존 `_local/` 파일은 편집하지 않았습니다.
14. **남은 디자인 차이:** Figma Dotum Regular와 현재 Noto Sans KR 700의 글리프 형태·굵기 차이는 기존대로입니다. Figma TEXT의 .4px Stroke와 달리 현재 구현의 상세 글자 Stroke는 0px이며, 사용자가 제거 유지 지시한 선언을 복구하지 않았습니다. 버튼 간 Default/Hover/Disabled 불일치는 발견하지 못했습니다. Figma와 현재 버튼 차이는 이번 요청의 우선순위에 따라 현재 문자스타일을 유지했습니다.
15. **NOT VERIFIED:** 실제 CAD/설치형 SW 호스트의 상세 팝업과 명령 적용, Firefox/Safari, 스크린리더 실제 낭독, 전체 Story의 모든 기존 상호작용 및 전 화면 픽셀 diff. 범용 Guard는 `status/staticStatus=NOT VERIFIED`, FAIL 0입니다. 원인: `@hm-design/sw/styles.css` resolver 및 native 버튼 재사용 판정은 범용 UI 검사로 자동 확정되지 않음. SW는 기존부터 UI와 독립된 컴포넌트 패키지이며 내부 버튼을 보완한 작업입니다. Guard 원본의 `styleEvidence`는 STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED이며 checks가 빈 배열이므로 SW CSS가 모두 검증됐다는 의미로 사용하지 않습니다. 원본 [guard.json](./detail-button-qa/guard.json)을 덮어 승격하지 않고 위 브라우저·Figma 근거를 별도로 제시합니다.
16. **종료 Git:** `feature/sw-package`, HEAD `9407c8567f57301659e591c03451cca1b828733c`; 상태 표시는 시작과 같은 `M package.json`, `M package-lock.json`, `?? docs/sw/`, `?? packages/sw/`, `?? src/sw/`이며 SW 디렉터리 안에 이번 변경이 추가됐습니다. 공식 원격 `main`도 같은 SHA를 읽기 확인했습니다. Stage/Commit/Push/PR/Merge/Publish/배포 없음. `git diff --check` 통과.
재현: `node packages/sw/tests/detail-buttons.browser.mjs verify docs/sw/detail-button-qa static`, 추가 보존 확인은 `preservation` 모드. Chrome/Playwright 경로는 `SW_CHROME_PATH` / `SW_PLAYWRIGHT_PATH`로 바꿀 수 있습니다. 변경 전 baseline은 이 작업 시작 상태이며 새로 덮어쓰면 기존 회귀 비교 기준이 바뀝니다.
비교 이미지: [Default](./detail-button-qa/comparison-default.png), [실제 Pointer Hover](./detail-button-qa/comparison-pointer-hover.png).
시간 기록(Asia/Seoul, KST):
- Started At: 2026-10-16 17:47:45
- Completed At: 2026-10-16 18:01:40
- Actual Duration: 13분 55초
- Estimated Duration: 최초 30~50분
- Variance: 예상보다 16분 05초~36분 05초 짧음
@@ -0,0 +1,114 @@
# SW Header 검증 기록 — Figma Dropdown 8종 · More 대표 필드 · Text Stroke · Input (2026-10-16)
> 이 문서는 이번 작업의 새 결과만 기록합니다. 이전 검증 기록([레이어 Dropdown](./sw-header-layer-dropdown-verification-2026-10-16.md), [속도·Composed Hover](./sw-header-speed-composed-hover-verification-2026-10-16.md), [창 이동 GIF](./sw-header-window-motion-gif-verification-2026-10-16.md), [창 이동 과거](./sw-header-window-motion-verification-2026-10-16.md))은 당시 결과로 보존합니다. 기준 명세: [디자인 명세 §5.3–§5.5, §6.1](./sw-header-design-spec.md).
## 1. 범위와 환경
| 항목 | 값 |
|---|---|
| Worktree / Branch / HEAD | `D:\디자인시스템\HMDesign-SW` / `feature/sw-package` / `9407c85`(시작·종료 동일, Commit 없음) |
| 요청 | ① Figma 8개 Dropdown ② 반응형 More 대표 필드 ③ 헤더 전체 Text Stroke ④ Dropdown 내부 Hover 정책 + 추가 요청 ⑤ 레이어 상세 버튼 · 수정 가능한 Input · 커서 · 문서 1개 기본 목록 ⑥ 동적 데이터 구조 |
| Figma | `rj6wpsfk24lOWvWiVRAANP` — 26:10986, 26:11036, 26:12666, 26:12659, 26:11104, 26:10702, 26:11181, 26:11215, 26:8944, 26:10767, 26:11656, 26:11664 (+ 행·input·상세 버튼 Variant). MCP `get_design_context` / `get_screenshot` + Plugin API 읽기 전용 스크립트(TEXT Stroke 전수 조사, Variant별 geometry) |
| Storybook | 이 Worktree의 dev 서버 `127.0.0.1:6024`(이미 실행 중이던 프로세스 사용) |
| 브라우저 검증 | Headless Chromium(Playwright, 다른 Worktree `HMDesign-OrgChart-Spike/node_modules`의 패키지를 읽기 전용으로 로드, 그 Worktree는 수정하지 않음). 스크립트·스크린샷은 세션 scratchpad(저장소 밖). 앱 내 Browser pane은 숨김 상태라 스크린샷이 갱신되지 않아 사용하지 않음 |
## 2. Figma 8개 디자인 (PASS-A)
브라우저 `getBoundingClientRect`(1920 프레임, 필드 기준 상대 좌표).
| 노드 | Figma | 구현 측정 | 판정 |
|---|---|---|---|
| 색상 26:10986 | 필드 192 × 38, 목록 192 × 158 @ y38, ByLayer 그룹 (12.5, 10) 167 × 30, 팔레트 (12, 50) 168 × 22, 도구 (15, 82), 하단 y118 h40, 링 194 × 198 | 192 × 38 / 192 × 158 @ 38 / (12.5, 48−38=10) 167 × 30 / (12, 50) / (15, 82) / 118 × 40 / 링 | MATCH |
| 사용자 색상 26:11036 | 본문 182, 사용자 행 24 | 182 | MATCH(사용자 칩 왼쪽 정렬 수정 후) |
| ByLayer / ByBlock | 82.5 × 28, Default / on 배경·inner shadow, 글자 `#D4CBBD` | 82.5 × 28, `aria-pressed` 상호 배타, on `#62533D` | MATCH(Hover 정책 §5) |
| 선스타일 26:11104 | 필드 284, drop_wrap 468 × 192 @ (−92, 37), drop 466 × 190, 열 182/142/142 | 473.2 × 192 @ (−94, 37), 열 187.2/142/142 | **차이**: 선 종류 열이 "Continuous"(Noto) 폭에 맞춰 +5.2px |
| 선 종류 행 26:10702 | 182 × 30, p12, gap 8, 70 × 2 미리보기 @ (12, 14), Hover `#62533D` / `#FF5C00` | 187.2 × 30, 미리보기 (12, 14) 70 × 2, CENTER 22·7·22·7, HIDDEN 33·33, Hover 색 일치 | MATCH(폭 차이 위와 같음) |
| 문자스타일 26:11181 | 164, 행 30 × 2, 하단 40(왼쪽 정렬), 본문 100 | 164 × 100, 행 30, 하단 (0, 98) 40 | MATCH(글꼴 칸 38 → 내용 폭 최대 56) |
| 치수스타일 26:11215 | 164, 행 30, 미리보기 60 × 15 @ (12, 7.5), 하단 오른쪽 | 164 × 100, 미리보기 (12, 7.5) 60 × 15, Hover 자산 전환 | MATCH |
| 상세 pill(색상·선·문자·치수) | 14 아이콘, gap 4, p 5/10, `.15`, radius 24, 글자 35% | 같음, 폭은 Noto 글자 폭(색상 상세 94.1 vs Figma 99) | MATCH(글꼴 폭 차이) |
자산: `color-pick.svg`(Figma `colorize`), `menu-dimension-row.svg` / `-hover.svg`(Figma 26:10729 행, mask id 외 동일 · Hover는 foreground fill 1곳만 다름). 상세 아이콘은 기존 `layer-detail.svg`가 Figma `ico_layer` Default와 동일해 재사용. `dist/index.js`·`dist/styles.css`에서 `figma.com/api/mcp/asset` 0건.
## 3. 상호작용 · Callback · Keyboard (PASS-E)
`Color Dropdown` Story의 호스트 예제(전 속성 연결)에서 실행, 26개 항목 모두 PASS:
- Hover로 열리지 않음 / 클릭으로 열림(`aria-expanded`, `aria-controls` 연결).
- 칩 → `onColorSelect(red)` 1회, 필드 `241,4,4`로 갱신, 목록 유지. ByLayer → ByBlock: `aria-pressed` `false,true`.
- RGB `256,0,0` → `aria-invalid`, Enter 시 보고 없이 복원. `#00ff00` → `onColorInput(0,255,0)` 1회. caret `rgb(230, 4, 3)`.
- Escape 닫기 + 필드 Focus 복귀. ↓ 열기 + 눌린 항목 Focus, ↓ 다음 항목.
- 선스타일을 열면 색상 닫힘. 선 종류 HIDDEN → 가중치·스케일 유지, 가중치 ByBlock → 선 종류·스케일 유지, User 0.35 / 스케일 2.5 확정, 스케일 0 거부, Tab으로 마지막 항목을 벗어나면 닫힘, 편집 중 Escape는 편집만 취소(목록 유지).
- 바깥 클릭 닫힘. 문자스타일 / 치수스타일 선택 → 닫힘 + 필드 갱신. 다른 속성(레이어·색상) 유지. 문자스타일 상세 → `onTextStyleDetail` 1회 후 닫힘.
- 선택·입력 Callback 중복 0, 페이지 오류 0.
## 4. 반응형 More 대표 필드 (PASS-B)
`Responsive More` Story, 16개 폭(1920 · 1800 · 1799 · 1440 · 1180 · 1179 · 1080 · 1079 · 900 · 899 · 768 · 640 · 639 · 520 · 519 · 390) × 더보기에 있는 모든 필드 항목. **0 FAIL**.
- 모든 폭: 헤더 49px, 헤더 가로 넘침 0, 객체 수정 영역 자식 겹침 0 · 영역 밖 0. ≥1800은 More 숨김(1920 배열 유지).
- 더보기 → 필드: 그 필드가 첫 칸, Dropdown 열림, Dropdown 좌·우가 헤더 안(390에서 선스타일은 세로 3단 191.2px), Focus = 그 필드, 겹침·넘침 0. → 레이어: 원래 배치, 레이어 Dropdown 열림.
- 1440 순서 테스트(선스타일 → 문자스타일 → 레이어): Callback = 각 `on*MenuOpenChange` + `on*Open` 1회씩, 데이터 Callback 0, 상태표(값) 변화 없음.
- 대표 슬롯 선택 근거: 객체 수정 영역 첫 칸(레이어 자리) — 1920 배열 첫 칸이며 모든 Breakpoint에서 남는 칸, 로고·파일명·창 제어와 분리. 최소 변경(CSS `order`·표시 규칙 + `SWHeader` UI 상태 1개).
## 5. Dropdown 내부 Hover 정책 (PASS-D)
- 독립 버튼(ByLayer · ByBlock · 칩 · 사용자 색상 · 색상 추출 · 상세 4종 · 레이어 상세): Storybook 전용 `data-sb-hover`로 Hover를 고정한 스크린샷이 Default와 동일. 레이어 상세는 computed style(배경 `rgba(197,177,158,.15)`, radius 24px, padding 5/10, 글자 .35) Default = Hover 고정.
- 목록 행 Hover 유지: 선 종류(1·4행), 가중치, 스케일, 문자·치수 행 = `#62533D` + `#FF5C00`(선 미리보기·치수 자산 포함). 현재 행 Hover는 이름 `#FFC600` 유지.
- Input Hover 유지(추가 요청): 1px `rgba(212,203,189,.25)`, 색상 입력은 `rgba(197,177,158,.2)`.
- 문서 Dropdown Hover(행 검정 40%, `+ New`/`Open` .3 / `#000`)는 변경 없음(computed 확인).
## 6. Text Stroke (PASS-C)
Figma 조사 결과와 구현 기준은 [명세 §6.1](./sw-header-design-spec.md#61-text-stroke-2026-10-16). Default Story computed style:
| 요소 | text-shadow | 자기 색 Stroke | 판정 |
|---|---|---|---|
| 파일명, 그리기모드, 필드 값, 글꼴 미리보기, 속성Bar/속성창, 문서 목록 이름 | 검정 1.3px 16방향 | 0.6px(바깥 0.3) | PASS |
| 12px 라벨, 상세보기 caption | 검정 1px 16방향 | 0 | PASS |
| 레이어 상세 / `… 상세` 글자 | 없음 | 0.8px(바깥 0.4) | PASS |
| `+ New` / `Open`, 창 이동 Tooltip, 더보기 항목 | 없음 | 0 | PASS(Figma 없음 / 요청으로 유지) |
| Input 값(`#E60403`) | 검정 1.3px | 0.6px | PASS |
- 8배 확대 비교: 검정 외곽선이 글자 바깥에 그려지고 Hover(`#FFC600`)·행 Hover(`#FF5C00`)에서도 유지. 말줄임 텍스트 외곽선 잘림 없음(`overflow-clip-margin` 2px).
- 남은 차이: Noto Sans KR 700 + Stroke는 Figma Dotum Regular + Stroke보다 약간 굵게 보입니다(Font Weight 정책 유지). 토큰으로 조정 가능.
## 7. 레이어 상세 · Input · 커서 · 문서 1개 (추가 요청)
| 항목 | 결과 |
|---|---|
| 레이어 상세 Default | 107.3 × 24(Figma 113 × 24는 Dotum 글자 폭), 배경·radius·padding·gap·글자 35% 일치. Hover 고정 = Default(무변화). Callback 없을 때 disabled + Figma 외형(opacity 1) |
| Input Default / Hover / 편집 | 58 × 20 / 58 × 24 / 58 × 24 — Figma와 동일. 글자 x 위치 Default = Hover(6px). 편집 `#0F0D0A` + 1px `.3`. ReadOnly는 20px 유지, Disabled 40% |
| 커서 | caret `#E60403`(computed). 깜박임·위치·선택은 네이티브. caret 높이 1 × 14 정밀 일치는 NOT VERIFIED |
| 편집 | 숫자 `12.75` Enter → `onCommit(12.75)`, `1.2.3` 복원, Escape → `onCancel` + 복원, 외부 값 `12.5` 반영, Tab(blur) 확정 |
| 한글 IME | CDP `Input.imeSetComposition`으로 조합 중 Enter 이벤트 → 확정 안 됨, 조합 완료 후 Enter → `한글` 확정. 실제 OS IME(Windows 한글 입력기) 수동 입력은 NOT VERIFIED |
| 문서 1개 기본 | Default Story에서 파일명 클릭 → 문서 1행(현재 문서 `#FFC600`, X 있음). `+ New` 55 × 24 / `Open` 50 × 24, 배경 .2, 글자 검정 70%, Hover .3 / `#000` — 기존 그대로 |
## 8. 동적 데이터 (추가 요청)
`Dynamic Data` Story: 선 종류 추가 · 순서 뒤집기(현재 항목 id로 유지) · 첫 항목 이름 변경(그 항목만), 현재 레이어 삭제(선택 표시 없음, 필드는 `layer` Props, 오류 없음), 레이어 추가, User 가중치 값 변경(그 행만), 문자스타일 14개(목록 240px 내부 스크롤, 헤더 49px), 문서 추가(2행, 현재 표시), 팔레트 삭제 · 사용자 색상 추가(6 + 3칩), 페이지 오류 0 — 13개 항목 PASS.
## 9. 보호 대상 (PASS-F)
- 기준 스냅샷(작업 시작 시 scratchpad 복사본)과 비교해 변경 없음: `SWHeaderLayerMenu.tsx`, `SWHeaderLayerIcons.tsx`, `SWHeaderWindowControls.tsx`, `SWHeaderDocumentTitle.tsx`, `SWHeaderHome.tsx`, `SWHeaderActions.tsx`, `SWHeaderProperties.tsx`.
- 창 이동 Motion CSS 블록과 `motion`이 들어간 모든 CSS 줄이 기준과 동일(1400ms · Keyframes · Drag 정지 · Reduced Motion).
- 레이어: 전구·눈꽃·자물쇠 클릭 → `onLayerVisibilityChange(layer-1, false)` · `onLayerFreezeChange(layer-1, true)` · `onLayerLockChange(layer-1, true)`, `onLayerSelect` 없음, 목록 유지, 아이콘 회색 — 확정 매핑 유지. `SWHeader`는 레이어 Dropdown 열림 상태를 제어형으로 전달(더보기 → 레이어 열기용)하며 레이어 컴포넌트 동작은 그대로.
- 1920 좌표: 레이어 467/234, 색상 705/192, 선 901/284, 문자 1189/164, 치수 1357/164, 상세보기 1521/157, 창 1680/240, 파일명 65/310, 높이 49 — 기준과 동일.
- 전체 17개 SW Header Story 렌더링 오류 0, 헤더 높이 모두 49px.
## 10. 빌드 (PASS-G)
| 명령 | 결과 |
|---|---|
| `npm run typecheck:sw` | PASS |
| `npm run build:sw` | PASS (`dist/styles.css` 70.71 kB, `dist/index.js` 239.32 kB) |
| `npm run typecheck`(루트, `build:packages` 포함) | PASS |
| `npm run build`(루트) | PASS. 기존 Tailwind `bg-[var(...)]` CSS 최적화 경고 1건·chunk 크기 경고는 이번 변경과 무관 |
| `npm run build-storybook` | PASS. SW Header Story 17개 index 확인(기존 10 + 신규 7) |
## 11. 남은 차이 · NOT VERIFIED
- 선 종류 열 182 → 187.2px(전체 468 → 473.2px), 문자스타일 글꼴 칸 38 → 내용 폭, 상세 pill 글자 폭: Dotum → Noto Sans KR 글자 폭 차이(글자 잘림 방지).
- Text Stroke 굵기 체감 차이(Noto 700 + Stroke vs Dotum Regular + Stroke).
- Figma 문자스타일 Variant3의 상세 버튼은 hover Variant(.25)로 배치되어 있으나 Hover 정책상 Default(.15)로 표시.
- 색상 선택 팝업("컬러칩 클릭", Figma "추후 디자인 진행예정")과 모든 `… 상세` 팝업 내부 화면: 미구현(Figma 전달 후).
- NOT VERIFIED: 실제 SW 호스트(설치형 SW / Electron)에서의 색·선·문자·치수 적용과 색상 추출, 실제 OS 한글 IME 수동 입력, caret 높이 14px 정밀 일치, Safari·Firefox의 `overflow-clip-margin`·`field-sizing`·`:has()` 렌더링(Chromium만 검증), 스크린리더 실제 낭독, 픽셀 단위 이미지 diff(좌표·computed style·확대 육안 비교만 수행).
@@ -0,0 +1,98 @@
# SW Header 후속 5개 요구사항 — 최종 통합 검증
이번 요청의 구현·실행 범위에 대한 기록입니다. 과거 PASS 문서를 덮어쓰지 않으며 전체 HMDesign·CAD 호스트·배포 승인을 뜻하지 않습니다.
1. **최종 판정: PASS — 요청한 구현 및 실행 검사 범위.** PASS-A~H를 확인했습니다. 별도 Figma 돋보기 Overlay 구현은 요청의 우선 구현 범위 밖인 호스트 연결 대상으로 남기며, Guard와 보조기기 등 미검증은 21번에서 분리합니다.
2. **시작 Git:** `D:\디자인시스템\HMDesign-SW`, `feature/sw-package`, `9407c8567f57301659e591c03451cca1b828733c`. 기존 `M package.json`, `M package-lock.json`, `?? packages/sw/`, `?? src/sw/`, `?? docs/sw/`를 보존했습니다. 이번 요청의 명시 경로·브랜치·Commit/Push 금지가 일반 Gitea main 정책보다 우선합니다. 시작 파일을 QA baseline에 복사하고 SHA-256을 기록했습니다.
3. **재사용:** 기존 Dropdown 8종·레이어 아이콘·Color/Number parser·SWHeaderInput·속성창 Property/Selection 타입·More·K 가이드·1400ms 창 이동·문서 목록·로고 슬롯·창 제어를 재사용했습니다. 다른 패키지 소스·기존 root manifest/lock은 수정하지 않았습니다. 원본 자산 45개와 과거 검증 문서 6개는 baseline과 바이트 단위로 같습니다.
4. **속성Bar 디자인/크기:** 새 공개 `SWHeaderPropertyBar`. Figma 26:10492를 직접 확인해 최대 1222×28, #271F13, 좌우/하단 #08251C, 하단 radius 6, inset shadow를 구현했습니다. 독립 기준 Story 실측 1222×28px입니다. 연결된 바는 객체 수정 영역의 실제 왼쪽부터 부모의 남은 폭을 사용합니다. 1920px 실제 Header에서 바 1222×28, Header 49px을 확인했으며 x=377을 하드코딩하지 않았습니다. 기존 Noto Sans KR 정책의 차이는 20번을 따릅니다.
5. **열기/닫기:** 기존 속성Bar 버튼 및 좁은 화면의 More를 연결했습니다. 미전달 시 내부 상태, propertyBarOpen/onPropertyBarOpenChange의 Controlled 상태, defaultPropertyBarOpen, 기존 onPropertyBarToggle을 지원합니다. 반복 클릭 닫기·외부 상태 변경을 확인했습니다. 바와 속성창은 동시 표시 및 독립 닫기가 가능하며, 바 토글이 다른 Dropdown 상태를 초기화하지 않습니다. 호스트 콘텐츠는 열린 바의 별도 28px 공간을 예약할 수 있습니다.
6. **동적 데이터/검증:** 속성창과 같은 SWHeaderProperty/Selection 레코드 및 onPropertyChange를 공유합니다. 객체 교체·필드 추가/삭제·라벨·순서·값·외부 갱신·전체/개별 ReadOnly/Disabled, min/max/validate를 확인했습니다. Enter/Blur 확정·Escape 취소·유효하지 않은 값 복원·편집 중 필드 삭제 시 draft 폐기를 확인했습니다. 값은 호스트 데이터이며 원/수치/CAD 계산을 패키지 기본값으로 넣지 않았습니다. Bar의 select는 option id 입력 검사, slider는 숫자 편집, computed는 읽기 전용으로 처리합니다.
7. **메뉴 단위 구성:** `controls?: readonly SWHeaderControlConfig[]`와 SWHeaderControlId를 공개했습니다. document/drawingMode/5개 메뉴/details/propertyBar/propertyPanel/print/popupGuide 각각을 allowlist로 구성합니다. 개별 제거·여러 개 제거·일부만 사용·모두 제거·재추가·visible:false/복원·배열 순서·허용 라벨 변경을 실제 UI로 확인했습니다. 파일명 데이터와 문서 접근성 접두사, K 아이콘과 접근성 이름을 구분했습니다. 컨트롤 내부 옵션 API는 기존과 같습니다. 설정 미전달 시 기존 1920px 디자인·계층을 유지하며, 설정 모드는 개별 컨트롤을 배열 순서로 같은 객체 영역에 배치합니다.
8. **More/값/Callback:** 기존 Breakpoint를 유지합니다. 제거 항목은 More에서도 제외하고, 남은 레이어 또는 첫 필드를 대표로 사용하며 대표 삭제 시 대체합니다. More에서 필드를 선택하면 표시·열림·Focus가 연결됩니다. 재정렬은 keyed 노드를 유지합니다. 편집 중 메뉴의 구조적 제거는 draft를 폐기하고 불필요한 데이터 Callback을 보내지 않습니다. 일반 Dropdown 닫힘의 기존 commit 동작은 유지합니다. 재추가 시 외부 선택값은 보존하고 내부 Dropdown은 닫힙니다. 열린 메뉴 재정렬 시 경계 위치도 다시 계산합니다. 개별 컨트롤 하나만 남긴 7개 폭을 포함한 경계 검사 84/84가 통과했습니다.
9. **Text Stroke Story:** 지정 제목·원본 스크린샷 영역·전용 import를 제거했습니다. 나머지 콘텐츠·실제 Header Typography/색상·다른 Figma 비교 Story는 유지했습니다. 원본 PNG 파일은 보존했습니다.
10. **반복 선언의 원인:** 작업 전 원본 `packages/sw/src/theme/sw.css` 1394행에 정확한 solid-stroke-width 참조가 실제로 있었고 변수는 131행에 정의되어 있었습니다. `packages/sw/src/index.ts` → 원본 CSS, `build.mjs` → dist/styles.css, Story → 공개 `@hm-design/sw/styles.css` → dist의 경로를 확인했습니다. 원본과 산출물이 달랐으므로 dist만 삭제하는 방식은 다음 build 때 원본에서 다시 포함되는 구조입니다. 소스·빌드/복사 스크립트·Storybook import·중복 정의를 조사했고 별도 생성기가 이 선언을 추가하거나 dist가 src를 역생성하는 증거는 없습니다. 과거 사용자가 실제 편집했던 파일 위치나 당시 캐시까지 관찰한 것은 아니므로 그 행위를 단정하지 않습니다.
11. **원본 수정:** `packages/sw/src/theme/sw.css`의 `:is(.hm-sw-header__layer-detail, .hm-sw-header__menu-detail) > span` Stroke 선언과 유일한 미사용 `--hm-sw-header-solid-stroke-width`를 제거했습니다. 덮어쓰기 `0`은 추가하지 않았습니다. 기존 text-shadow·다른 메뉴 텍스트·SVG·상세 버튼 크기 및 Hover는 보존했습니다. 이전 text-stroke-width 변수 제거와 구분한 새 수정입니다.
12. **Build/HMR 재생성 방지:** 원본/패키지 dist와 최종 Storybook CSS 14개에서 해당 선언·변수 일치 0입니다. 실제 브라우저에서 5개 상세 버튼의 Default/Hover 및 열린 선택 필드 상태를 확인했습니다. 일시적 CSS 검사 표식을 원본에 넣고 패키지 빌드 → 브라우저 computed 반영 → 원본 복원·재빌드 → 표식 제거 반영 → 새로고침까지 실행했습니다. Vite WebSocket의 `/packages/sw/dist/styles.css` update와 computed 변경을 확인했습니다. CSS import는 Vite의 js-update 모듈 형태로 전달됩니다. 검사 표식은 최종 소스/산출물에 없습니다. 재생성 방지 HMR 검사 8/8입니다. 현재 브라우저에서 오래된 Stroke를 적용하는 캐시 증거는 없었습니다.
13. **btn_colorize 비교:** Figma 26:11301 및 Default 26:11302 / Hover 26:11305 context·스크린샷을 직접 확인했습니다. 각각의 원본 SVG를 colorize-default.svg/colorize-hover.svg로 저장했습니다. 버튼 실측 24×24, 두 SVG의 원본·렌더 크기 18×18, radius 3, 지정 gradient·기본 border·drop shadow, Hover #DA6813 border·검정25% overlay·전용 SVG를 확인했습니다. 임시 Figma URL은 런타임에 없습니다. 기존 color-pick.svg는 기존 자산 보존 원칙으로 남겼습니다.
14. **아이콘/Callback:** 기존 색상 메뉴의 색상 추출 위치를 교체했습니다. Default/Hover/Mouse Leave/키보드 focus-visible/Enter 클릭을 확인했습니다. Hover 시 Callback 없음, 클릭 시 기존 onColorPick 1회·메뉴 닫힘, 기존 색상값·모드 유지가 통과했습니다. 색상 입력 검증·사용자 색상·ByLayer/ByBlock 필수 선택과 선택 강조도 기존 회귀 검사를 통과했습니다.
15. **26:11338 Overlay:** 패키지 내 실제 색상 돋보기 Overlay 구현은 없습니다. 기존 onColorPick으로 호스트가 연결할 수 있으며 별도의 임의 색상 선택창은 만들지 않았습니다. 현재 구현 상태는 사용 가이드·Story에 명시했습니다.
16. **반응형:** 실제 Header 실측 폭 1920/1440/1080/900/768/640/390px에서 49px·가로 넘침 없음·바 28px·viewport 경계·스크롤로 마지막 필드 접근·More 대표 전환·삭제 후 대체·Dropdown 경계를 확인했습니다. 각 컨트롤 단독 구성도 같은 7개 폭에서 검사했습니다. 기존 속성창/K 가이드 경계와 창 이동 Motion은 기존 회귀와 보존 검사로 확인했습니다. 서로 다른 팝업 상태와 속성Bar는 독립적이며 overlay z-index는 기존 Dropdown보다 낮습니다.
17. **Story 회귀:** 기존 21개 export를 보존하고 Property Bar / Dynamic Controls / Colorize 3개를 추가해 총 24개입니다. 모든 Story 렌더·적용 Header 높이와 기존 주요 상호작용을 검사했습니다. 기존 통합 195/195, 기존 보완 43/43, 신규 122/122, 추가 경계 84/84, HMR 8/8이 통과했습니다. 보완 원본의 45개 중 이번 라벨 API로 수정한 DocumentTitle/Fields의 바이트 동일성 2개는 제외하고 나머지 보존/상호작용을 검사했습니다. 모든 가능한 호스트 데이터 조합의 전수 검증이라는 뜻은 아닙니다.
18. **명령/경고:** package.json의 실제 명령을 확인해 최종 SW Typecheck → SW Build → Root Typecheck → Root Build → Storybook Build 순차 실행, 모두 exit 0입니다. pre 훅에 포함된 build:packages 반복은 기존 명령 정의 그대로입니다. SW 출력 styles.css 약85.83kB/index.js 약686.35kB. Root/Storybook의 기존 유형 Tailwind bg-[var(...)] CSS 최적화 경고·500kB chunk 경고·Storybook 플러그인 시간 안내는 남습니다. SW 신규 타입/빌드 오류나 신규 형태의 소스 경고는 없습니다. 로컬 초기 개발 서버의 전역 설정/registry 쓰기 제한은 브라우저 검증용 서버에서 해결했고 제품 소스를 수정하지 않았습니다. lint 명령은 정의되어 있지 않습니다.
19. **변경 파일:** baseline 대비 소스/문서 23개(기존 19개 수정, 신규 자산/컴포넌트/본 보고 4개 추가), 생성 dist 제외입니다. 기존 root package.json/lock은 이번 작업 전후 바이트가 같습니다. 상세 목록은 아래에 있습니다.
20. **Figma와 남은 차이:** 기존 패키지의 Noto Sans KR 400/700 정책을 유지해 Figma Dotum Regular와 글자 폭/획/베이스라인이 다릅니다. 바의 수치는 호스트 예시 데이터이고 산술 관계를 계산/보정하지 않습니다. 구성 모드는 사용자가 지정한 순서대로 펼친 구성이므로 승인된 기본 그룹 배치와 다를 수 있으며 미설정 기본 디자인은 보존합니다. 과거 보고에 있는 선스타일 content 폭 차이와 운영 호스트별 font/CSS 커스터마이징은 이번 범위 밖입니다.
21. **NOT VERIFIED:** Guard 원본 status/staticStatus는 NOT VERIFIED, FAIL 0 / 미검증65건(ENTRY-002 1, REUSE-004 43, SCOPE-001 15, TOKEN-001 3, TOKEN-002 3)입니다. styleEvidence는 STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED이며 checks 배열은 비어 있습니다. 이를 전체 디자인 또는 동적 입력의 PASS로 확대하지 않습니다. 독립 SW CSS 공개 entry, Story native 호스트 조작, 동적 class/style 및 guard scope 해석 한계는 브라우저 증거와 별개로 남깁니다. 픽셀 단위 자동 Figma 이미지 diff, 실제 Figma 프로토타입 직접 재생, Windows IME/스크린리더 실기기, 모든 대비 조합, CAD 연산·파일/OS 창 동작·실제 26:11338·외부 소비앱 캐시·배포는 미검증입니다.
22. **종료 Git:** 시작과 같은 feature/sw-package / 9407c8567f57301659e591c03451cca1b828733c, 기존 root 2개 M와 SW 3개 폴더 Untracked 범주를 유지합니다. git diff --check 통과. Stage/Commit/Push/PR/Merge/Publish/배포, Reset/Clean/강제 Checkout을 수행하지 않았습니다. 사용자 검토/승인을 기다립니다.
## 변경 파일 목록
```text
MOD packages/sw/README.md
MOD packages/sw/src/components/sw-header/index.ts
MOD packages/sw/src/components/sw-header/SWHeader.tsx
MOD packages/sw/src/components/sw-header/types.ts
MOD packages/sw/src/components/sw-header/components/SWHeaderColorMenu.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderDocumentTitle.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderDropdown.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderFields.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderInput.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderKGuide.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderLayerMenu.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderLineStyleMenu.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderMore.tsx
MOD packages/sw/src/components/sw-header/components/SWHeaderStyleListMenus.tsx
MOD packages/sw/src/theme/sw.css
MOD src/sw/sw-header.stories.tsx
MOD src/sw/sw-stories.css
MOD docs/sw/sw-header-design-spec.md
MOD docs/sw/sw-header-usage.md
ADD packages/sw/src/assets/colorize-default.svg
ADD packages/sw/src/assets/colorize-hover.svg
ADD packages/sw/src/components/sw-header/components/SWHeaderPropertyBar.tsx
ADD docs/sw/sw-header-followup-five-verification-2026-10-16.md
```
## 검증 증거
QA root: `C:\Users\user\.codex\visualizations\2026\10\16\01a1439a-bd89-7030-8978-48f687e44b0d\sw-header`
- baseline/, baseline-sha256.json, git-start.txt: 기존 미커밋 파일 보존본.
- legacy-verify.mjs / browser-results.json: 기존 통합195개.
- legacy-followup.mjs / followup-results.json: 기존 보완43개·원본 보존.
- new-verify.mjs / new-results.json: 신규122개, 실제7개 Header 폭.
- edges.mjs / edges-results.json: 단독 구성·열린 메뉴 재정렬84개.
- hmr.mjs / hmr-results.json / hmr-build.log: 재빌드·HMR·복원·새로고침8개.
- build-results.json / final-*.log: 최종 순차5개 명령.
- guard-final.json: 원본 미검증65개, 임의로 PASS로 변경하지 않음.
- changed-files.json: baseline 대비 파일 목록.
- header-bar-1920.png, header-bar-390.png, colorize-hover.png: 실제 React 렌더.
## 시간 기록 (Asia/Seoul)
- Started At: 2026-10-16 16:26:26 KST
- Completed At: 2026-10-16 16:50:41 KST
- Actual Duration: 24분 15초
- Estimated Duration: 약 90분
- Variance: 예상보다 65분 45초 짧음
@@ -0,0 +1,147 @@
# SW Header 레이어 Dropdown 검증 기록 (2026-10-16)
> 범위: `@hm-design/sw` 레이어 필드 Dropdown과 전구·눈꽃·자물쇠 On/Off. 기준: Figma `rj6wpsfk24lOWvWiVRAANP` 5개 Node. 규격은 [디자인 명세 §5.2](./sw-header-design-spec.md#52-레이어-dropdown-figma-headermenu레이어-2610940-2026-10-16), 사용법은 [사용 가이드](./sw-header-usage.md#레이어-dropdown-예)를 따릅니다.
## 1. 작업 환경
| 항목 | 값 |
|---|---|
| Worktree / Branch | `D:\디자인시스템\HMDesign-SW` / `feature/sw-package` |
| HEAD | `9407c85` (시작·종료 동일, Commit 없음) |
| 시작 상태 | `M package.json`, `M package-lock.json`, `?? docs/sw/`, `?? packages/sw/`, `?? src/sw/` — 기존 미커밋·Untracked 파일 보존(시작 시 스크래치 백업, Reset/Clean/Checkout 없음) |
| 검증 Storybook | `npx storybook dev -p 6047`(6024·6031·6032는 다른 세션 사용 중) |
## 2. Figma 5개 Node
| # | Node | 내용 | 확인 방법 |
|---|---|---|---|
| ① | `26:10940` `header/menu/레이어` (default 26:10941, hover 26:10952, Variant3 26:10963) | 필드·펼침 목록·레이어 상세 | `get_design_context` + screenshot |
| ② | `26:10810` `icon_전구` | 4 Variant: 컬러 / 컬러 Hover(검정 25%) / 회색(ellipse `#E6E6E6→#555`) / 회색 Hover | `get_design_context` + screenshot |
| ③ | `26:10855` `icon_눈꽃` | 4 Variant SVG(`#00CCFF` / `#949494`, Hover = 검정 25% 타일 추가) | 동일 |
| ④ | `26:10912` `icon_자물쇠` | 4 Variant SVG(`#E4BF4D·#D8A300` / `#BEBEBE·#A1A1A1`, Hover = 타일 추가) | 동일 |
| ⑤ | `26:10687` `header/dropdown/brown/row` (Default 26:10688, Hover 26:10695) | 행 234 × 30, Hover `#62533D` / `#FF5C00` | 동일 |
Prototype의 Smart Animate(Off → Off Hover, ease-out 300ms)는 사용자 요청서 기재값을 따랐습니다. Figma Prototype player 재생으로는 확인하지 않았습니다(NOT VERIFIED).
## 3. 자산
- 신규 8개(`layer-ellipse-off`, `layer-freeze-hover/off/off-hover`, `layer-lock-hover/off/off-hover`, `layer-detail`)를 Figma MCP에서 내려받아 `packages/sw/src/assets/`에 저장. 수정·재작성 없음.
- 기존 `layer-union/ellipse/state-a/state-b`는 Figma 원본과 mask id 외 동일 → 재사용.
- Hover SVG = Default SVG + `<rect 20×20 rx2 fill=black 25%>` 1줄 외 동일(정규화 diff).
- `packages/sw/src`, `src/sw`, `packages/sw/dist`, `storybook-static`에서 `figma.com/api` 검색 0건.
## 4. 디자인 측정 (Storybook `SW / Header / Layer Dropdown`, 1920 프레임, `getBoundingClientRect`·computed style)
| 항목 | 기대(Figma) | 측정 | 판정 |
|---|---|---|---|
| 필드 | 234 × 38, x 467 | 234 × 38, x 466.7(헤더 기준) | MATCH |
| 펼친 필드 | 테두리 `#081813`, radius 3 3 0 0, 라벨·값 `#FFC600` | `rgb(8,24,19)`, `3px 3px 0 0`, `rgb(255,198,0)` | MATCH |
| 펼침 외곽선 | 236 × 320 at −1/−1, radius 4 | `::before` inset −39 / −1 / −1 / −1, radius 4, 노란 gradient | MATCH |
| 목록 | 234 × 280, 필드 바로 아래 | 234 × 280, y = 필드 하단 | MATCH |
| 배경·그림자·radius | `#27241D`, `0 4px 8px rgba(16,48,38,.25)`, 0 0 3 3 | 동일 | MATCH |
| 행 | 234 × 30 × 8 | 234 × 30, 8개 | MATCH |
| 아이콘 | 20 × 20, x+12 / 32 / 52 | 동일 | MATCH |
| 견본 | 16 × 16 at x+80, 안쪽 14 × 14 | 동일 | MATCH |
| 이름 | x+104(+1), `#F6F4F2`, 700 | 동일 | MATCH |
| 하단 | 40, 버튼 오른쪽 12px | 40, 버튼 오른쪽 끝 = 목록 −12 | MATCH |
| 레이어 상세 | radius 24, 배경 `rgba(197,177,158,.15)`, 아이콘 14, 글자 opacity .35 | 동일, 버튼 107.3 × 24(글자 69.3) | MATCH(글자 폭은 글꼴 차이) |
| 행 Hover | `#62533D` / `#FF5C00` | `rgb(98,83,61)` / `rgb(255,92,0)`(고정·실제 Hover 모두) | MATCH |
| 아이콘 Hover | 전구 검정 25% 배경, 눈꽃·자물쇠 Hover SVG | 전구 `rgba(0,0,0,.25)`, Hover img opacity 1 | MATCH |
| Off Hover 전환 | 300ms ease-out | Off: `0.3s ease-out`, On: 0s | MATCH |
| 헤더 높이 | 49 | 49 | MATCH |
3배 확대 아이콘 표(12개 Variant)를 Figma screenshot과 육안 비교: 형태·색 일치. 픽셀 diff는 수행하지 않았습니다.
## 5. 동작 검증
JS 이벤트(`click`, `PointerEvent`, `KeyboardEvent`)와 실제 마우스·키보드 입력(브라우저 pane `left_click`, `hover`, `key`)으로 확인했습니다.
| 시나리오 | 결과 |
|---|---|
| 필드 클릭 → 열림, 다시 클릭 → 닫힘 | PASS (`aria-expanded` 동기화, `onLayerMenuOpenChange` true/false) |
| 전구 / 눈꽃 / 자물쇠 클릭(JS 4회, 실제 마우스 1회, 키보드 Space·Enter 2회) | PASS — 각 Callback 1회, `onLayerSelect` 0회, 목록 유지, 해당 레이어만 변경(상태표 확인) |
| 행 선택(JS, 실제 Enter) | PASS — `onLayerSelect` 1회, 목록 닫힘, Focus 필드 복귀, 필드에 레이어명·상태 아이콘 반영(회색 전구 확인) |
| 바깥 pointerdown, 다른 필드 pointerdown | PASS — 닫힘 |
| Escape(JS, 실제 키) | PASS — 닫힘, Focus 필드 |
| ↓(닫힘) | PASS — 열림, 현재 레이어 이름 버튼 Focus(2px `#F0F7F5` outline) |
| ↓↑ / ←→ | PASS — 같은 열 유지 행 이동, 행 안 이동 |
| Tab 진입 / Shift+Tab 두 번으로 이탈 | PASS — 목록 안에서는 유지, 밖으로 나가면 닫힘 |
| 레이어 상세(Callback 있음) | PASS — `onLayerDetail` 1회 후 닫힘 |
| 레이어 상세(Callback 없음) | PASS — `disabled`, opacity 1(Figma 외형), cursor default |
| Callback 없는 아이콘 | PASS — `role="img"` span 24개, 버튼 0개 |
| disabled 레이어 | PASS — 아이콘 3개·선택 버튼 모두 disabled |
| 20개 레이어 | PASS — 목록 240px 내부 스크롤(scrollHeight 600), 전체 280, 헤더 가로 스크롤 0 |
| 레이어 없음 | PASS — 하단만(40px), 필드는 `layer.name` |
| 긴 레이어명 | PASS — 행·필드 ellipsis, 오른쪽 padding 12 유지 |
| 콘솔 오류 | 0건 |
## 6. 반응형
`Layer Dropdown`의 Callback 예제 프레임 폭을 바꿔 측정하고, 같은 폭의 기존 `Responsive` Story(`layers` 없음) 필드 위치와 비교했습니다.
| 폭 | 필드 x / 폭 (기존과 동일) | 열린 뒤 필드 이동 | 헤더 가로 스크롤 | 목록 헤더 안 | 목록 이동 | 아이콘·텍스트 겹침 | 견본 |
|---|---|---|---|---|---|---|---|
| 1920 | 466.7 / 234 ✔ | 없음 | 0 | ✔ | 0 | 없음 | 16 × 16 |
| 1800 | 346.7 / 234 ✔ | 없음 | 0 | ✔ | 0 | 없음 | 16 × 16 |
| 1799 | 466.7 / 234 ✔ | 없음 | 0 | ✔ | 0 | 없음 | 16 × 16 |
| 1440 | 466.7 / 234 ✔ | 없음 | 0 | ✔ | 0 | 없음 | 16 × 16 |
| 1180 | 306.7 / 234 ✔ | 없음 | 0 | ✔ | 0 | 없음 | 16 × 16 |
| 1080 | 207.7 / 234 ✔ | 없음 | 0 | ✔ | 0 | 없음 | 16 × 16 |
| 900 | 246.7 / 234 ✔ | 없음 | 0 | ✔ | 0 | 없음 | 16 × 16 |
| 768 | 310.7 / 234 ✔ | 없음 | 0 | ✔ | 0 | 없음 | 16 × 16 |
| 640 | 182.7 / 234 ✔ | 없음 | 0 | ✔ | 0 | 없음 | 16 × 16 |
| 390 | 167.7 / 113.7 ✔ | 없음 | 0 | ✔ | 왼쪽 이동, 오른쪽 여백 1.3 | 없음 | 16 × 16 |
- 1차 측정에서 390px 열림 상태에 헤더 `scrollWidth`가 1px 늘어나는 문제(외곽선이 목록 밖 1px)를 발견해, 이동 계산에 1px 여백을 두도록 수정 후 320 / 360 / 390 / 500 / 519 / 640에서 0을 재확인했습니다.
- 390에서 실제 조작(열기 + 자물쇠 Toggle → Callback 1회, 목록 유지) 확인. 브라우저 Viewport 390 에뮬레이션은 Storybook 페이지 내부 폭이 766px로 잡혀 헤더 프레임 390 기준으로만 확인했습니다(실기기 NOT VERIFIED).
- 더보기 메뉴 정책·기존 Breakpoint 변경 없음(필드 위치가 기존 Story와 동일).
## 7. 회귀 확인
| 항목 | 결과 |
|---|---|
| `layers` 미전달 필드 | 기존 DOM(아이콘 4개 img 14×18 / 14×14 / 20×20 / 20×20, `aria-expanded=false`)과 동일 |
| Composed Controls 고정 Hover | 5개 필드 값 `#FFC600`, Gradient ring opacity 1, 선 미리보기 `#FFC600` 유지 |
| 문서 Dropdown·창 이동·인쇄·최소화·종료·속성 토글 CSS | 변경 없음(diff는 레이어 관련 규칙 추가뿐) |
## 8. Build
| 명령 | 결과 |
|---|---|
| `npm run typecheck -w @hm-design/sw` | PASS |
| `npm run build:sw` | PASS (`dist/styles.css` 45.35 kB, `dist/index.js` 202.28 kB) |
| `npm run typecheck`(root, packages build 포함) | PASS |
| `npm run build`(root) | PASS (기존 500 kB chunk 경고만) |
| `npm run build-storybook` | PASS, index에 `sw-header--layer-dropdown` |
## 9. 아이콘 색상 ↔ 상태 매핑 확정 검증 (사용자 확정, 2026-10-16)
확정 기준: 전구 컬러 `visible=true` / 회색 `false`, 눈꽃 컬러 `frozen=false`(동결 해제) / 회색 `true`(동결), 자물쇠 컬러 `locked=false`(잠금 해제) / 회색 `true`(잠금). 클릭 시 해당 boolean 반전값을 Callback으로 전달합니다. 기존 구현(`layerIconOn()`, `onChange(layer, !pressed)`)이 이 기준과 같아 동작 코드는 바꾸지 않았고, 타입 주석·Story 설명·문서의 잠정 표기만 확정으로 바꿨습니다. SVG·Default/Hover CSS 변경 없음.
검증: Storybook `Layer Dropdown`의 Callback 예제에서 각 아이콘을 두 번씩 클릭했습니다. 색은 렌더링된 SVG의 실제 fill(전구 `#FAB846`/`#9C9C9C`, 눈꽃 `#00CCFF`/`#949494`, 자물쇠 `#E4BF4D`/`#BEBEBE`)로 판정했습니다.
| 레이어 · 아이콘 | 클릭 전 | Callback | 클릭 후 |
|---|---|---|---|
| layer-0 전구 | `visible=true` 컬러 | `onLayerVisibilityChange(layer-0, false)` | `false` 회색 |
| layer-0 전구 | `false` 회색 | `(layer-0, true)` | `true` 컬러 |
| layer-0 눈꽃 | `frozen=false` 컬러 | `onLayerFreezeChange(layer-0, true)` | `true` 회색 |
| layer-0 눈꽃 | `true` 회색 | `(layer-0, false)` | `false` 컬러 |
| layer-0 자물쇠 | `locked=false` 컬러 | `onLayerLockChange(layer-0, true)` | `true` 회색 |
| layer-0 자물쇠 | `true` 회색 | `(layer-0, false)` | `false` 컬러 |
| layer-5 전구 | `visible=false` 회색 | `onLayerVisibilityChange(layer-5, true)` | `true` 컬러 |
| layer-5 전구 | `true` 컬러 | `(layer-5, false)` | `false` 회색 |
| layer-5 눈꽃 | `frozen=true` 회색 | `onLayerFreezeChange(layer-5, false)` | `false` 컬러 |
| layer-5 눈꽃 | `false` 컬러 | `(layer-5, true)` | `true` 회색 |
| layer-5 자물쇠 | `locked=true` 회색 | `onLayerLockChange(layer-5, false)` | `false` 컬러 |
| layer-5 자물쇠 | `false` 컬러 | `(layer-5, true)` | `true` 회색 |
- 모든 클릭에서 Callback 1회, `onLayerSelect` 0회, 목록 유지. `aria-pressed`는 표시됨 / 동결됨 / 잠김과 일치.
- 클릭하지 않은 다른 레이어 7개의 상태는 변하지 않음.
## 10. 남은 차이 · NOT VERIFIED
- 아이콘 색상 ↔ 상태 매핑은 위 §9로 확정·검증했습니다. 실제 SW 호스트에서 이 값으로 도면이 표시·동결·잠금되는지는 외부 연동이 필요합니다(NOT VERIFIED).
- 실제 SW 호스트에서의 레이어 표시·동결·잠금 반영과 레이어 상세 화면: 패키지 단독 검증 불가(NOT VERIFIED).
- 현재 레이어 이름 `#FFC600`, 목록 최대 높이 240px, 좁은 폭 왼쪽 이동은 Figma에 없는 구현값입니다.
- 레이어 상세 버튼 Hover는 Figma Variant가 없어 추가하지 않았습니다.
- Figma Smart Animate 곡선의 Prototype player 실재생 비교, 픽셀 diff, 실기기 390px, 스크린리더 실사용 검증은 수행하지 않았습니다.
@@ -0,0 +1,94 @@
# SW Header — 1400ms 및 Composed Controls Hover 통합 검증
## 1. 최종 판정: PARTIAL
요청 범위의 Chrome 브라우저 통합 검사 170개와 이웃 필드 영향 검사 5개, 픽셀 비교, Root Typecheck / SW Build / Root Build / Storybook Build는 PASS입니다. 실제 네이티브 호스트 창 제어·다른 브라우저·보조기술·독립 SW Guard coverage는 NOT VERIFIED이므로 전체 PASS로 확대하지 않습니다.
## 2. 작업 시작 시 Git 상태
- 경로 `D:\디자인시스템\HMDesign-SW`, branch `feature/sw-package`.
- HEAD `9407c8567f57301659e591c03451cca1b828733c`.
- 기존 변경 ` M package-lock.json`, ` M package.json`, `?? docs/sw/`, `?? packages/sw/`, `?? src/sw/`.
- 기존 파일 SHA-256과 수정 전 CSS·필드 TSX·Story 사본을 `_local/sw-speed-hover-qa/`에 보존했습니다. 미추적 파일을 삭제·초기화하지 않았습니다.
## 3. 창 이동 Motion 변경 전후
전체 1640 → **1400ms**, Tooltip 520 → **약 443.90ms**, 이동 완료 1040 → **약 887.80ms**, 완료 후 유지 600 → **약 512.20ms**입니다. 모든 구간은 1400/1640(35/41)로 동일하게 단축했습니다. 주기는 약 14.63% 짧아지고 재생 속도는 약 17.14% 빨라집니다. 원본 GIF와 다른 사용자 요청 사항입니다.
세 CSS animation duration만 변경했습니다. 실제 Hover에서 1.4초와 2.8초 iteration을 확인했고, 이탈 시 원래 SVG로 복귀합니다. Drag 중 paused/Tooltip 숨김, 호스트 Pointer Capture 이벤트 전달, Reduced Motion의 원래 아이콘·정적 Tooltip도 통과했습니다.
## 4. 이동 경로 보존
세 `@keyframes`의 좌표·백분율·보간 선언은 수정 전과 바이트 단위로 동일합니다. 이전 41개 브라우저 프레임을 시간 × 35/41에 맞춰 다시 측정했으며 창·커서·Tooltip의 상대 위치 차이는 **최대 0px**였습니다. 따라서 오른쪽 28px / 위쪽 4.5px, 커서 상승·등장 순서·가속/감속 비율은 보존합니다. GIF와의 공간 오차 기록을 현재 시간까지 같다는 뜻으로 해석하지 않습니다.
## 5. Composed Controls Hover 대상
공용 레이어·색상·선스타일·문자스타일·치수스타일 필드의 버튼 전체 Hover에 적용했습니다. 동일 컴포넌트를 사용하는 Full Header에도 같은 규칙을 적용합니다. Composed Controls의 기존 레이어·색상·상세보기 예제는 보존하고 기존 공개 선/문자/치수 컴포넌트와 5개 필드의 고정 Hover 비교를 추가했습니다. 새 Story나 새 공개 API는 없습니다.
상세보기는 이번 필드 강조에서 제외합니다. `속성Bar`/`속성창`의 텍스트 전용 Hover, caption·검정 divider·기존 선택 토글·배경을 유지하고 Hover Border를 추가하지 않았습니다.
## 6. 텍스트 · 선 · 아이콘 색상
- PASS: 각 필드 버튼의 빈 모서리 부분에 Hover해도 모든 라벨·값이 `rgb(255,198,0)`입니다. 선스타일의 가중치·스케일, 폰트 이름 텍스트도 포함합니다.
- PASS: 선 미리보기 5개 조각의 채움만 노란색, 1px 검정 border와 형태·위치 유지.
- PASS: 문자 미리보기는 기존 텍스트이며 색만 변경. 원래 별도 검정 Stroke/그림자가 없으므로 새로운 외곽선을 추가하지 않았습니다.
- PASS: 치수 Hover SVG는 foreground path의 흰 채움 1곳만 `#FFC600`으로 변경. mask용 흰 rect와 검정 외곽선 path·참조·좌표는 원본과 동일합니다. 60 × 15px wrapper에서 원본/Variant visibility만 교대하며 CSS filter는 없습니다.
- PASS: 색상 견본의 실제 값 `#ffe500`, chip border, 레이어 상태 SVG·막대 색 유지. 표시 문자열·실제 값은 변경되지 않습니다.
- PASS: Default / Selected / Disabled / Disabled+Selected × 5개 필드에서 Hover·이탈·콜백을 확인. Disabled는 새 Hover 효과가 없고 Selected의 기존 색은 유지됩니다. 선택 단독 상태의 미리보기 색을 확장하지 않았습니다.
## 7. 검정 외곽선 · 기존 Hover Border
기존 노란 Gradient `::after`의 선언은 그대로이며 enabled Hover에서는 opacity 1, Disabled에서는 0입니다. 검정 선 border·치수 mask는 유지합니다. 4배 브라우저 캡처에서 5개 필드 모두 Default와 Hover의 순수 검정 픽셀 위치 집합이 동일했습니다. 선 미리보기 필드는 1920개, 치수 필드는 2903개의 검정 픽셀이 유지됐습니다. 문자 필드는 원래 별도 검정 픽셀이 0개였습니다.
5개 Default 화면은 수정 전과 후의 PNG 픽셀이 모두 동일했습니다. Hover 전후 버튼·텍스트·미리보기의 크기/위치·배경·그림자가 같았고, 5개 이웃 영향 검사에서도 Hover한 필드 외 텍스트 색은 변하지 않았습니다.
## 8. Storybook 시각 검증
실제 빌드된 `SW / Header / Window Move Motion`과 `Composed Controls`를 Chrome `155.0.8059.79`에서 열었습니다. 속도 보정된 9개 고정 프레임이 실제 1400ms CSS animation을 pause하고, 반복 경계에서 문구가 숨겨지는 것을 확인했습니다. Composed의 실제 세 스타일 필드와 5개 고정 Hover, 기존 속성창 클릭·선택 상태를 확인했습니다. 비교 PNG와 Story 전체 캡처도 시각 검토했습니다.
이는 실제 공용 React 컴포넌트 결과이며 별도 HTML/CSS 모방 화면이 아닙니다. 개발 서버 프로세스나 포트를 변경하지 않고 임시 localhost 서버에서 검사 후 종료했습니다.
## 9. 반응형 · Callback 회귀
요청 폭 1920 / 1800 / 1440 / 1180 / 1080 / 900 / 768 / 640 / 390px와 경계 1079 / 639px를 추가 확인했습니다. 모두 Header 높이 49px, Header/문서 가로 overflow 0px이며 기존 auxiliary 필드·속성·색상·인쇄/가이드의 More 이동 정책이 유지됐습니다. 이동 영역도 88 / 24 / 숨김 그대로입니다.
일반/선택 필드 클릭과 Enter 콜백, Disabled 클릭 차단, Full Header 5개 필드 콜백, More로 이동한 선스타일 콜백·메뉴 닫힘, 상세보기 선택 토글, 최소화·종료 콜백을 확인했습니다. Drag의 원래 div 이벤트와 호스트 모의 Pointer Capture·move/up도 통과했습니다. 실제 OS 창 제어는 NOT VERIFIED입니다.
## 10. Typecheck · Build · Storybook · Guard
- PASS: `npm.cmd run typecheck`. 기존 pretypecheck의 theme 확인·workspace package build와 SW Build도 포함합니다.
- PASS: SW package build(`build:sw`, 위 prerequisite 포함), `npm.cmd --ignore-scripts run build`, `npm.cmd --ignore-scripts run build-storybook`. 이미 통과한 prerequisite은 반복하지 않았습니다.
- PASS: 브라우저 통합 조건 170개, 이웃 영향 5개, 5개 Default 픽셀 보존과 검정 픽셀 비교. pageerror 0건.
- 기존 빌드 경고: 문서 예시 `bg-[var(...)]`, Storybook `:host-context` minifier, 500kB 초과 bundle와 plugin timing 안내. exit 0이며 범위 밖 코드를 수정하지 않았습니다.
- Guard 1회: `node scripts/ai-generation-guard.mjs --target . --profile STORYBOOK --scope PARTIAL --files src/sw/sw-header.stories.tsx,packages/sw/src/theme/sw.css,packages/sw/src/components/sw-header/components/SWHeaderFields.tsx --json`. 원본 status/staticStatus는 NOT VERIFIED, FAIL 없음. SW styles 연결 `ENTRY-002`, 패키지 구현 graph 밖 `SCOPE-001`, 연결에 의존한 Token/동적 값 미검증이 남았습니다. Guard를 수정하거나 결과를 PASS로 덮어쓰지 않았습니다.
- Guard styleEvidence: STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED, checks=[]. 독립 SW 실검사 완료라는 의미가 아닙니다. 별도 Runtime/사용자 디자인 의도는 위 검사 범위 PASS, 전체 검증은 PARTIAL입니다.
## 11. 변경 파일 목록
1. `packages/sw/src/theme/sw.css` — 1400ms 및 필드 Hover.
2. `packages/sw/src/components/sw-header/components/SWHeaderFields.tsx` — 치수 원본/Variant의 동일 크기 wrapper.
3. `packages/sw/src/assets/menu-dimension-ico-hover.svg` — 신규 foreground 색 Variant.
4. `src/sw/sw-header.stories.tsx` — 기존 모션·Composed Story 보완.
5. `docs/sw/sw-header-design-spec.md` — 현재 §11.5 및 기존 정책 정합.
6. `docs/sw/sw-header-usage.md` — 현재 시간·필드 Hover·Story 사용법.
7. `docs/sw/sw-header-window-motion-gif-verification-2026-10-16.md` — 과거 내용 뒤 후속 기록만 추가.
8. `packages/sw/README.md` — 현재 동작과 링크.
9. 이 검증 기록 — 신규 문서.
Git 무시 경로 `_local/sw-speed-hover-qa/`에 시작 해시·사본, 전/후 Default/Hover PNG, `pixel-comparison.json`, `composed-hover-comparison.png`, Story·폭별 PNG, `verification.json`, `neighbors.json`, 원본 `guard.json`, 빌드 로그와 `final-summary.json`을 보존합니다.
## 12. 기존 디자인 보존 근거
수정한 필드 블록·duration·주석을 원래 값으로 복원한 임시 문자열이 시작 CSS 전체와 동일합니다. 따라서 상세보기·홈·문서·인쇄/가이드·최소화/종료·반응형 CSS는 바이트 단위로 보존됐습니다. 필드 TSX도 치수 wrapper/import 외 문자열이 동일하며 기존 Callback·Props를 변경하지 않았습니다. 다른 Story, 기존 SVG, `SWHeaderWindowControls.tsx`, root package.json/Lockfile은 시작 상태를 유지합니다.
과거 GIF 검증 문서는 시작 사본이 현재 문서의 정확한 prefix임을 확인했습니다(append-only). 다른 패키지 소스·Worktree·DWG/DXF Viewer는 수정하지 않았습니다. Root의 기존 package build에 따른 Git 무시 dist 재생성만 있습니다. 의존성 설치·공개 API 변경은 없습니다.
## 13. 남은 차이 · NOT VERIFIED
원본 GIF와 시간만 다릅니다(사용자 요청). 기존 Tooltip의 최소화/종료 일부 시각적 겹침은 유지하며 포인터를 차단하지 않습니다. 실제 네이티브 창 제어, 다른 브라우저·터치/펜·보조기술, 외부 CSS 커스터마이징, 독립 SW Guard coverage는 미검증입니다. 현재 Chrome 기본 테마 범위의 PASS를 전체 플랫폼 승인으로 확대하지 않습니다.
## 14. 작업 종료 시 Git 상태
`feature/sw-package`, HEAD `9407c8567f57301659e591c03451cca1b828733c` 유지. status 그룹도 ` M package-lock.json`, ` M package.json`, `?? docs/sw/`, `?? packages/sw/`, `?? src/sw/`를 유지하며 실제 이번 변경은 해당 미추적 그룹 내부에 있습니다. Index 변경·Commit·Push·PR·Merge·Publish·배포는 수행하지 않았습니다.
작업 시간(KST): Started At 2026-10-16 13:54:54 / Completed At 2026-10-16 14:08:24 / Actual Duration 13분 30초 / Estimated Duration 15~25분 / Variance 15분 대비 −1분 30초, 25분 대비 −11분 30초.
+626
View File
@@ -0,0 +1,626 @@
# SW Header 사용 가이드
> **상세 버튼 최신 기준(2026-10-16):** 레이어·색상·선·문자·치수 상세는 문자스타일 디자인의 `SWHeaderMenuDetail`을 공통 사용합니다. `on*Detail` 미전달 시 Hover 유지·클릭 무동작이며, Callback 전달 시 기존 1회 호출·닫힘·Focus 복귀를 유지합니다. 내부 공통 컴포넌트는 `label`, `icon`, `onClick`, `disabled`, `className`, `aria-label` 등 native 버튼 Props를 지원합니다. 새 공개 export는 추가하지 않았습니다. 비교 Story는 `SW/Header → Detail Buttons (Shared States)`(verification 태그, 직접 ID `sw-header--detail-buttons`)입니다. 과거의 Callback 미전달=Disabled 및 치수만 밝은 Hover 설명보다 이 기준이 우선합니다. [이번 검증 기록](./sw-header-detail-buttons-verification-2026-10-16.md).
> **3차 기준(2026-10-16):** 로고 외 개별 제거 `hiddenControls`, 유사 메뉴 순서 `menuOrder`, 속성창 4상태 Story·비교, 형상 Slot `shapeContent`를 추가했습니다. [3차 절](#구성요소-표시메뉴-순서속성창-4상태형상-slot-2026-10-16-3차)과 [3차 검증 기록](./sw-header-composition-shape-verification-2026-10-16.md)이 같은 주제의 이전 설명보다 우선합니다.
> **후속 5개 요구사항 기준(2026-10-16):** `SWHeaderPropertyBar`, 메뉴 단위 `controls`, `btn_colorize` Default/Hover를 추가했습니다. 상세 pill의 `solid-stroke-width`도 제거했습니다. 아래 후속 연결 절과 [새 검증 기록](./sw-header-followup-five-verification-2026-10-16.md)이 현재 기준입니다. 과거 실행 기록은 보존합니다.
> **최신 API/동작:** 아래 [속성창·K 가이드 추가 연결](#속성창k-가이드-추가-연결-2026-10-16)을 먼저 확인하세요. 색상 기준은 정확히 하나 표시되며, 상세 버튼 Hover와 목록 Selected 정책이 변경됐습니다. 과거 예시의 `colorMode=null`은 직접 색상 의미를 유지하지만 모드 표시까지 해제하지 않습니다.
`@hm-design/sw`의 SW Header를 화면에 연결하는 방법입니다. 디자인 값과 판정은 [디자인 명세](./sw-header-design-spec.md)를 따릅니다.
> 패키지는 아직 npm Registry에 게시하지 않았습니다. 아래 설치 방법은 HMDesign Workspace 또는 로컬 tarball 기준입니다.
## 1. 패키지 구조
```text
packages/sw/
├── src/
│ ├── assets/ Figma 원본 SVG와 Hover Variant (build 시 data URI로 포함, 로고 제외)
│ ├── components/
│ │ ├── sw-header/
│ │ │ ├── SWHeader.tsx 전체 헤더
│ │ │ ├── components/ 하위 컨트롤 (공개 + 내부)
│ │ │ ├── types.ts Props / 값 타입
│ │ │ └── index.ts
│ │ └── sw-theme/SWTheme.tsx 하위 컨트롤 단독 사용 시 토큰 범위
│ ├── theme/sw.css SW 전용 토큰·스타일
│ └── index.ts
├── build.mjs 독립 library build (Vite lib + tsc 선언)
├── package.json exports: "." , "./styles.css"
└── README.md
```
`@hm-design/ui`·`@hm-design/center-graphic`·OrgChart 패키지를 import하지 않습니다. Peer는 `react`·`react-dom` `^19.0.0`뿐입니다.
## 2. 설치
### HMDesign Workspace 안
루트 `package.json`의 Workspace(`packages/*`)에 포함되어 있습니다.
```bash
npm install
npm run build:sw
```
`build:packages`(typecheck·build·Storybook 전처리)도 마지막에 `build:sw`를 실행합니다.
### 다른 React 프로젝트(로컬 tarball)
```bash
npm pack -w @hm-design/sw --pack-destination ../packs
```
```bash
npm install ../packs/hm-design-sw-0.1.0.tgz
```
소비 프로젝트는 `src` 내부 파일을 참조하지 않고 `dist` 공개 API만 사용합니다.
## 3. Import
```tsx
import { SWHeader } from "@hm-design/sw"
import "@hm-design/sw/styles.css"
```
- CSS는 JS에서 자동 import되지 않으므로 앱 진입점 등에서 한 번 import합니다.
- TypeScript 7에서 CSS side-effect import 타입 오류가 나면 Vite 프로젝트의 `/// <reference types="vite/client" />`(또는 `declare module "*.css"`)를 둡니다.
- 글꼴: 패키지는 폰트 파일을 포함하지 않습니다. 앱에서 Noto Sans KR(400/700)을 로드합니다.
## 4. 공개 API
| Export | 용도 |
|---|---|
| `SWHeader` | 전체 헤더. `ref`는 `<header>` |
| `SWHeaderHome` | 홈 버튼(로고 영역). `logoSrc` / `logo` / `logoAlt` / `onClick` |
| `SWHeaderDocumentTitle` | 파일명 버튼. `documents`를 주면 문서 Dropdown(`title`, `onClick`, `documents`, `menuOpen`, `onMenuOpenChange`, `onDocumentSelect`, `onNew`, `onOpen`, `documentIcon`, `newLabel`, `openLabel`) |
| `SWHeaderLayerControl`, `SWHeaderColorControl`, `SWHeaderLineStyle`, `SWHeaderTextStyle`, `SWHeaderDimensionStyle` | 속성 필드 5종. `SWHeaderLayerControl`은 선택 `state`(현재 레이어의 표시·동결·잠금)로 필드 아이콘을 회색으로 표시 |
| `SWHeaderLayerMenu` | 레이어 필드 + 레이어 Dropdown(`value`, `layers`, `activeLayerId`, `open`, `onOpenChange`, `onClick`, `onLayerSelect`, `onVisibilityChange`, `onFreezeChange`, `onLockChange`, `onDetail`). `SWHeader`는 `layers`가 있을 때 이것을 사용 |
| `SWHeaderColorMenu` | 색상 필드 + 색상 Dropdown(2026-10-16, `value`, `colors`, `userColors`, `mode`, `open`, `onOpenChange`, `onClick`, `onColorSelect`, `onModeChange`, `onColorInput`, `onPick`, `onDetail`). `SWHeader`는 `colors`가 있을 때 사용 |
| `SWHeaderLineStyleMenu` | 선스타일 필드 + 3열 Dropdown(`value`, `lineTypes`, `lineWeights`, `activeLineTypeId`, `activeLineWeightId`, `onLineTypeSelect`, `onLineWeightSelect`, `onLineWeightValueChange`, `onScaleChange`, `onDetail`, 열림 Props). `SWHeader`는 `lineTypes` 또는 `lineWeights`가 있을 때 사용 |
| `SWHeaderTextStyleMenu`, `SWHeaderDimensionStyleMenu` | 문자·치수스타일 필드 + 목록 Dropdown(`value`, `textStyles` / `dimensionStyles`, `active*Id`, `on*Select`, `onDetail`, 열림 Props) |
| `SWHeaderLinePreview`, `swHeaderLinePatterns` | 70 × 2 선 미리보기와 Figma CENTER(22·7·22·7) / HIDDEN(33·33) 패턴 |
| `SWHeaderInput` | 수정 가능한 SW Input(Figma input 26:11656 · cursor 26:11664). 레이어 상세 등 상세 영역의 값 편집용 |
| `parseSWHeaderColor`, `parseSWHeaderNumber` | Input 유효성 검사 도우미(`r,g,b` / `#rrggbb`, 소수) |
| `SWHeaderProperties` | 상세보기(속성Bar / 속성창) |
| `SWHeaderActions` | 인쇄 · 팝업 가이드 |
| `SWHeaderWindowControls` | 창 이동 · 최소화 · 종료 |
| `SWTheme` | 하위 컨트롤을 헤더 밖에서 쓸 때 토큰·글꼴·버튼 초기화 범위 |
| 타입 | `SWHeaderProps`, `SWHeaderMenuId`, `SWHeaderLayerValue`, `SWHeaderLayer`, `SWHeaderColorValue`, `SWHeaderLineStyleValue`, `SWHeaderTextStyleValue`, `SWHeaderDimensionStyleValue`, `SWHeaderDocument`, `SWHeaderColorMode`, `SWHeaderColorOption`, `SWHeaderColorInputValue`, `SWHeaderLineType`, `SWHeaderLineWeight`, `SWHeaderTextStyleOption`, `SWHeaderDimensionStyleOption`, `SWHeaderInputValueType`, 각 하위 컴포넌트 `*Props`, `SWThemeProps` |
내부 구현(`SWHeaderField`, `SWHeaderMore`, `useSWHeaderDropdown`, `SWHeaderMenuDetail`)은 공개하지 않습니다.
## 5. SWHeader Props
`HTMLAttributes<HTMLElement>`(단 `title`, `children`, `color` 제외)를 함께 받습니다. `className`·`style`·`aria-*`는 `<header>`에 전달됩니다.
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| `documentTitle` | `string` | 필수 | 파일명. 좁으면 말줄임, 전체 이름은 `title` 툴팁 |
| `drawingModeLabel` | `string` | `"그리기모드"` | 그리기 모드 라벨 |
| `layer` | `{ name }` | `{ name: "0" }` | 현재 레이어(`layers`에 `activeLayerId`와 같은 항목이 없을 때 필드에 표시) |
| `layers` | `SWHeaderLayer[]` | — | 레이어 필드 아래 Dropdown 목록(배열 순서 그대로). 없으면 Dropdown 없이 기존 필드 버튼, `[]`이면 하단(레이어 상세)만 |
| `activeLayerId` | `string` | — | 현재 레이어. 필드에 이름·상태 아이콘 표시, 목록에서 이름만 `#FFC600` |
| `layerMenuOpen` | `boolean` | — | 레이어 Dropdown 열림 상태(제어형). 없으면 내부에서 관리 |
| `color` | `{ value, swatch? }` | `{ value: "ByLayer", swatch: "#ffffff" }` | 색상 값 표시와 칩 색 |
| `lineStyle` | `{ name, weight, scale }` | `ByLayer / ByLayer / 1.00` | 선스타일 |
| `textStyle` | `{ font, name }` | `Arial / Standard` | 문자스타일 |
| `dimensionStyle` | `{ name }` | `Standard` | 치수스타일 |
| `activeMenu` | `"layer" \| "color" \| "lineStyle" \| "textStyle" \| "dimensionStyle" \| null` | `null` | 선택(열림) 표시 |
| `propertyBarOpen` | `boolean` | `false` | 속성Bar 눌림 |
| `propertyPanelOpen` | `boolean` | `false` | 속성창 눌림 |
| `disabled` | `boolean` | `false` | 메뉴·속성·인쇄/가이드 비활성(창 제어 제외) |
| `tools` | `ReactNode` | — | 객체 수정 영역의 필드 뒤 추가 도구 |
| `logoSrc` | `string` | — | 로고 이미지 URL. 32 × 17 로고 영역에 `contain`으로 맞춤 |
| `logo` | `ReactNode` | — | 로고 요소(인라인 SVG 등). `logoSrc`보다 우선 |
| `logoAlt` | `string` | — | 제품명. 홈 버튼 이름이 "<logoAlt> 홈"(없으면 "홈") |
| `documents` | `SWHeaderDocument[]` | — | 파일명 아래 문서 Dropdown 목록. 없으면 Dropdown 없이 기존 버튼, `[]`이면 New / Open만 |
| `documentMenuOpen` | `boolean` | — | 문서 Dropdown 열림 상태(제어형). 없으면 내부에서 관리 |
| `activeDocumentId` | `string` | — | 현재 문서. 목록 순서는 그대로 두고 이 문서의 이름만 `#FFC600` |
| `documentIcon` | `ReactNode` | Figma 문서 아이콘 | `icon`이 없는 문서의 아이콘(29 × 12 영역) |
| `documentNewLabel` / `documentOpenLabel` | `string` | `+ New` / `Open` | 하단 버튼 글자 |
| `colors` | `SWHeaderColorOption[]` | — | 색상 Dropdown 기본 팔레트(2026-10-16). 없으면 Dropdown 없이 기존 필드 버튼 |
| `userColors` | `SWHeaderColorOption[]` | — | 사용자 색상(개수 가변, 2px 간격 줄바꿈, 4행 초과 시 팔레트 영역 스크롤) |
| `colorMode` | `"byLayer" \| "byBlock" \| null` | — | 눌린 ByLayer / ByBlock. `null`·미지정 = 명시 색 |
| `colorMenuOpen` | `boolean` | — | 색상 Dropdown 열림(제어형). 없으면 내부 관리 |
| `lineTypes` / `lineWeights` | `SWHeaderLineType[]` / `SWHeaderLineWeight[]` | — | 선스타일 Dropdown의 선 종류 / 가중치 열. 둘 다 없으면 기존 필드 버튼. 스케일 열은 `lineStyle.scale` |
| `activeLineTypeId` / `activeLineWeightId` | `string` | `lineStyle.name` / `.weight`와 같은 이름의 행 | 현재 행(이름 `#FFC600`) |
| `lineStyleMenuOpen` | `boolean` | — | 선스타일 Dropdown 열림(제어형) |
| `textStyles` / `dimensionStyles` | `SWHeaderTextStyleOption[]` / `SWHeaderDimensionStyleOption[]` | — | 문자 / 치수스타일 Dropdown 행. 없으면 기존 필드 버튼 |
| `activeTextStyleId` / `activeDimensionStyleId` | `string` | `textStyle.name` / `dimensionStyle.name`과 같은 이름의 행 | 현재 행 |
| `textStyleMenuOpen` / `dimensionStyleMenuOpen` | `boolean` | — | 각 Dropdown 열림(제어형) |
Dropdown 데이터 타입(모두 `id`로 식별, 배열 순서 = 표시 순서, 패키지에 고정 데이터 없음):
| 타입 | 필드 |
|---|---|
| `SWHeaderColorOption` | `{ id; color; value?; label?; disabled? }` — `color` CSS 색, `value` 보고·표시 값(기본 `color`의 `r,g,b`), `label` 접근성 이름 |
| `SWHeaderColorInputValue` | `{ value: "r,g,b"; rgb: [r, g, b]; hex: "#rrggbb" }` |
| `SWHeaderLineType` | `{ id; name; pattern?: number[]; disabled? }` — `pattern` 70px 미리보기의 대시 길이(4px 간격, 비율 맞춤), 없으면 실선 |
| `SWHeaderLineWeight` | `{ id; name; value?; editable?; disabled? }` — `value` 오른쪽 값(ByDefault 0.25), `editable` 이면 값 Input(User) |
| `SWHeaderTextStyleOption` | `{ id; name; font; disabled? }` |
| `SWHeaderDimensionStyleOption` | `{ id; name; disabled? }` |
`SWHeaderDocument`: `{ id: string; name: string; title?: string; icon?: ReactNode; disabled?: boolean }` — `title`은 툴팁(기본 `name`).
`SWHeaderLayer`: `{ id: string; name: string; color?: string; visible?: boolean; frozen?: boolean; locked?: boolean; disabled?: boolean }` — `color`는 16px 견본 색(기본 `#ffffff`), 기본값은 `visible: true`, `frozen: false`, `locked: false`. 확정 매핑([명세 §5.2.1](./sw-header-design-spec.md#521-아이콘-onoff와-데이터-의미)):
| 아이콘 | 컬러 | 회색 | 클릭 시 Callback 값 |
|---|---|---|---|
| 전구 | `visible=true` | `visible=false` | `onLayerVisibilityChange(layer, !visible)` |
| 눈꽃 | `frozen=false`(동결 해제) | `frozen=true`(동결) | `onLayerFreezeChange(layer, !frozen)` |
| 자물쇠 | `locked=false`(잠금 해제) | `locked=true`(잠금) | `onLayerLockChange(layer, !locked)` |
헤더는 값을 직접 바꾸지 않으므로 호스트가 받은 값으로 `layers`를 갱신해야 아이콘 색이 바뀝니다.
> **로고는 확정 디자인이 아닙니다.** 로고 영역의 위치·크기는 공통 디자인이고, 로고 이미지는 사용하는 SW 프로그램이 정합니다. 패키지에는 로고가 들어 있지 않으며 `logo`·`logoSrc`를 전달하지 않으면 로고 없이 표시됩니다(배치는 그대로). Storybook의 기본 로고는 임시 자산(`sw-header-logo-placeholder.svg`)입니다.
기본값은 Figma 표시값이 아니라 중립값입니다. Figma 샘플 값은 Storybook `figmaArgs`를 참고합니다.
## 6. Callback
| Callback | 호출 시점 |
|---|---|
| `onHomeClick` | 홈(로고) 클릭 |
| `onDocumentClick` | 파일명 클릭(Dropdown 열고 닫을 때도 호출) |
| `onDocumentMenuOpenChange(open)` | 문서 Dropdown 열림·닫힘 |
| `onDocumentSelect(document)` | 문서 항목 선택. 문서를 여는 동작은 호스트가 구현 |
| `onDocumentClose(document)` | 행의 X(문서 닫기). 주면 모든 행에 X가 생기고 Hover·키보드 Focus한 행에만 보입니다. 선택 Callback과 중복되지 않습니다. 실제 닫기·목록 갱신·다음 선택은 호스트가 처리 |
| `onDocumentNew`, `onDocumentOpen` | Dropdown 하단 `+ New` / `Open` |
| `onLayerOpen`, `onColorOpen`, `onLineStyleOpen`, `onTextStyleOpen`, `onDimensionStyleOpen` | 각 필드 클릭(Dropdown이 있으면 열고 닫을 때도 호출) 또는 더보기 항목 선택 1회(대표 필드 전환, §7 "반응형 More") |
| `onLayerMenuOpenChange(open)` | 레이어 Dropdown 열림·닫힘 |
| `onLayerSelect(layer)` | 레이어 행(견본·이름) 선택. 목록이 닫힙니다. 현재 레이어 변경은 호스트가 `activeLayerId`로 반영 |
| `onLayerVisibilityChange(layer, nextVisible)` | 전구 클릭. 목록 유지, 선택 Callback 없음 |
| `onLayerFreezeChange(layer, nextFrozen)` | 눈꽃 클릭. 목록 유지, 선택 Callback 없음 |
| `onLayerLockChange(layer, nextLocked)` | 자물쇠 클릭. 목록 유지, 선택 Callback 없음 |
| `onLayerDetail()` | 하단 `레이어 상세`. 호출 후 목록이 닫힙니다. 없으면 버튼은 Figma 외형 그대로 비활성 |
| `onColorMenuOpenChange(open)` 등 `on*MenuOpenChange` | 각 Dropdown 열림·닫힘(색상·선/문자/치수스타일) |
| `onColorSelect(option)` | 팔레트·사용자 색상 칩. 목록 유지 |
| `onColorModeChange(mode)` | ByLayer / ByBlock. 목록 유지. 상호 배타 표시는 호스트가 `colorMode`로 반영 |
| `onColorInput(value)` | RGB 입력 확정(Enter·Focus 이동·목록 닫힘). 유효한 `r,g,b`(0–255) 또는 `#rrggbb`만, 값이 바뀐 경우만 |
| `onColorPick()` | 색상 추출 버튼. 목록이 닫히고 실제 추출(도면 픽셀 등)은 호스트가 구현 |
| `onLineTypeSelect(type)` / `onLineWeightSelect(weight)` | 선 종류 / 가중치 행. 다른 열 값은 건드리지 않고 목록 유지 |
| `onLineWeightValueChange(weight, value)` | `editable` 가중치 Input 확정(0 이상 소수) |
| `onLineScaleChange(value)` | 스케일 Input 확정(0 초과 소수) |
| `onTextStyleSelect(style)` / `onDimensionStyleSelect(style)` | 행 선택 후 목록 닫힘 |
| `onColorDetail`, `onLineStyleDetail`, `onTextStyleDetail`, `onDimensionStyleDetail` | 하단 `… 상세`. 전달 시 호출 후 목록 닫힘. 없으면 공통 Default/Hover를 유지하고 클릭은 무동작. 상세 팝업은 패키지 범위 밖(추후 Figma 전달 후) |
| `onPropertyBarToggle`, `onPropertyPanelToggle` | 속성 토글 클릭 또는 더보기 항목 선택 |
| `onPrint`, `onPopupGuide` | 인쇄·팝업 가이드 |
| `onWindowMoveStart(event)` | 창 이동 영역 pointer down |
| `onMinimize`, `onClose` | 최소화·종료 |
헤더는 선택·열림 상태를 스스로 저장하지 않습니다. 호스트가 Callback에서 상태를 바꾸고 Props로 다시 내려줍니다. 실제 드롭다운 목록, 파일 열기, 창 이동·최소화·종료(브라우저 / Electron / 설치형 SW마다 다름)는 호스트가 구현합니다.
## 7. 전체 헤더 예제
```tsx
import { useState } from "react"
import { SWHeader, type SWHeaderMenuId } from "@hm-design/sw"
import "@hm-design/sw/styles.css"
export function DrawingHeader({ fileName }: { fileName: string }) {
const [activeMenu, setActiveMenu] = useState<SWHeaderMenuId | null>(null)
const [panelOpen, setPanelOpen] = useState(false)
const toggle = (id: SWHeaderMenuId) => () => setActiveMenu(current => (current === id ? null : id))
return (
<SWHeader
documentTitle={fileName}
layer={{ name: "0_Layer_Cludmark" }}
color={{ value: "255,229,0", swatch: "#ffe500" }}
lineStyle={{ name: "Continuous", weight: "0.25", scale: "1.00" }}
textStyle={{ font: "Arial", name: "Standard" }}
dimensionStyle={{ name: "Standard" }}
activeMenu={activeMenu}
propertyPanelOpen={panelOpen}
onLayerOpen={toggle("layer")}
onColorOpen={toggle("color")}
onLineStyleOpen={toggle("lineStyle")}
onTextStyleOpen={toggle("textStyle")}
onDimensionStyleOpen={toggle("dimensionStyle")}
onPropertyPanelToggle={() => setPanelOpen(open => !open)}
onClose={() => window.close()}
/>
)
}
```
### 로고 지정 예
```tsx
import productLogo from "./assets/product-logo.svg" // 각 프로그램의 로고 파일
<SWHeader documentTitle={fileName} logoSrc={productLogo} logoAlt="제품명" />
// React 요소로 전달(인라인 SVG 등). logoSrc보다 우선합니다.
<SWHeader documentTitle={fileName} logo={<ProductMark />} logoAlt="제품명" />
```
- 로고 영역은 32 × 17px입니다. 가로·세로 비율이 달라도 영역 안에 맞춰지고 잘리지 않습니다. 선명하게 보이도록 SVG 또는 64 × 34px 이상 이미지를 권장합니다.
- 로고 영역 크기는 디자인 기준이므로 바꾸지 않는 것을 권장합니다(토큰 `--hm-sw-header-logo-width` / `-logo-height`).
### 문서 Dropdown 예
```tsx
const [openDocuments, setOpenDocuments] = useState<SWHeaderDocument[]>([])
<SWHeader
documentTitle={current.name}
documents={openDocuments} // 각 프로그램의 문서 목록(표시 순서 그대로)
activeDocumentId={current.id} // 이름만 #FFC600, 순서는 그대로
onDocumentSelect={document => activate(document.id)}
onDocumentClose={document => closeDocument(document.id)} // 목록·현재 문서는 호스트가 갱신
onDocumentNew={createDocument}
onDocumentOpen={showOpenDialog} // 파일 열기는 호스트가 구현
/>
```
- 클릭으로 열고 닫습니다(Hover로 열리지 않음). 바깥 클릭·Escape·항목 선택·New/Open 후 닫힙니다.
- Dropdown은 헤더 아래로 겹쳐 열립니다. SWHeader의 조상 요소에 `overflow: hidden`이 있으면 잘리므로, 헤더는 페이지 최상단처럼 잘리지 않는 위치에 둡니다.
### 레이어 Dropdown 예
```tsx
const [layers, setLayers] = useState<SWHeaderLayer[]>(() => drawing.layers.map(toHeaderLayer))
const patch = (id: string, change: Partial<SWHeaderLayer>) =>
setLayers(list => list.map(layer => (layer.id === id ? { ...layer, ...change } : layer)))
<SWHeader
documentTitle={fileName}
layers={layers} // 각 프로그램의 레이어 목록(표시 순서 그대로)
activeLayerId={currentLayerId} // 필드 표시 + 목록에서 이름만 #FFC600
onLayerSelect={layer => setCurrentLayer(layer.id)}
onLayerVisibilityChange={(layer, visible) => { viewer.setLayerVisible(layer.id, visible); patch(layer.id, { visible }) }}
onLayerFreezeChange={(layer, frozen) => { viewer.setLayerFrozen(layer.id, frozen); patch(layer.id, { frozen }) }}
onLayerLockChange={(layer, locked) => { viewer.setLayerLocked(layer.id, locked); patch(layer.id, { locked }) }}
onLayerDetail={openLayerManager} // 없으면 버튼 비활성(임의 팝업 없음)
/>
```
- 레이어 필드를 클릭해야 열립니다(Hover는 테두리·글자색만). 행 선택·Escape·바깥 클릭·Tab 이탈·레이어 상세 후 닫힙니다. 전구·눈꽃·자물쇠 클릭은 목록을 유지합니다.
- 헤더는 레이어 상태를 저장하지 않습니다. Callback에서 도면(`viewer.*`은 예시)과 `layers`를 함께 갱신해야 아이콘이 바뀝니다. 실제 표시·동결·잠금 처리와 편집 가능 여부 판단은 호스트 책임입니다.
- 키보드: 필드에서 ↓로 열면 현재 레이어에 Focus, ↓↑ 행 이동(같은 열), ←→ 행 안 이동, Enter/Space 실행. 상세 규격은 [디자인 명세 §5.2](./sw-header-design-spec.md#52-레이어-dropdown-figma-headermenu레이어-2610940-2026-10-16).
### 색상 · 선/문자/치수스타일 Dropdown 예 (2026-10-16)
```tsx
const [color, setColor] = useState<SWHeaderColorValue>({ value: "ByLayer", swatch: "#ffffff" })
const [colorMode, setColorMode] = useState<SWHeaderColorMode | null>("byLayer")
const [lineStyle, setLineStyle] = useState({ name: "Continuous", weight: "0.25", scale: "1.00" })
<SWHeader
documentTitle={fileName}
color={color}
colors={program.palette} // [{ id: "red", color: "#ff0000", label: "빨강" }, ...]
userColors={program.userColors} // 개수 가변
colorMode={colorMode}
onColorSelect={option => { viewer.setColor(option.color); setColor({ value: option.value ?? option.color, swatch: option.color }); setColorMode(null) }}
onColorModeChange={mode => { viewer.setColorMode(mode); setColorMode(mode) }}
onColorInput={({ value, hex }) => { viewer.setColor(hex); setColor({ value, swatch: hex }); setColorMode(null) }}
onColorPick={startPickFromDrawing} // 실제 픽셀 추출은 호스트
onColorDetail={openColorDetail} // 없으면 Hover 유지·클릭 무동작(상세 팝업 미제공)
lineStyle={lineStyle}
lineTypes={program.lineTypes} // [{ id: "center", name: "CENTER", pattern: swHeaderLinePatterns.center }, ...]
lineWeights={program.lineWeights} // [{ id: "user", name: "User", value: "0.25", editable: true }, ...]
onLineTypeSelect={type => setLineStyle(s => ({ ...s, name: type.name }))} // 가중치·스케일 유지
onLineWeightSelect={weight => setLineStyle(s => ({ ...s, weight: weight.value ?? weight.name }))}
onLineWeightValueChange={(weight, value) => updateWeight(weight.id, value)}
onLineScaleChange={scale => setLineStyle(s => ({ ...s, scale }))}
textStyle={textStyle}
textStyles={program.textStyles} // [{ id, font: "Arial", name: "Standard" }, ...]
onTextStyleSelect={style => setTextStyle({ font: style.font, name: style.name })}
dimensionStyle={dimensionStyle}
dimensionStyles={program.dimensionStyles} // [{ id, name: "ISO - 25" }, ...]
onDimensionStyleSelect={style => setDimensionStyle({ name: style.name })}
/>
```
- 클릭으로 열고 닫습니다(Hover는 노란 테두리·글자만). 다른 Dropdown을 열면 앞의 것이 닫히고, 바깥 클릭·Escape·Tab 이탈로 닫힙니다. 헤더 높이(49px)와 다른 필드 위치는 변하지 않습니다.
- 색상·선스타일은 여러 속성을 이어서 바꿀 수 있도록 선택 후에도 열려 있고, 문자·치수스타일은 행 선택 후 닫힙니다. 색상 추출과 `… 상세`는 호출 후 닫힙니다.
- 헤더는 값을 저장하지 않습니다. Callback 값으로 `color` / `colorMode` / `lineStyle` 등을 갱신해야 필드가 바뀝니다. 명시 색을 고를 때 ByLayer/ByBlock을 해제할지는 호스트 정책입니다(Storybook 예제는 해제).
- 키보드: 필드에서 ↓로 열면 현재(눌린) 항목 또는 첫 항목에 Focus, ↓↑로 Dropdown 안 버튼·Input 순서대로 이동, Enter/Space 실행, Escape 닫기(필드로 Focus 복귀).
- Hover: 상세 버튼 5개는 문자스타일 상세 기준 공통 Hover(배경 .25·radius 25·아이콘 35%·글자 35%)를 적용합니다. ByLayer/ByBlock은 기존 후속 Hover 기준을 따릅니다. 색상 칩·색상 추출·목록 행(선 종류·가중치·스케일·문자·치수)·Input의 현재 동작은 유지합니다.
- 좁은 헤더: 목록은 헤더 안으로 옮겨지며, 468px 선스타일 목록은 헤더 폭 480 미만에서 세로로 쌓입니다.
### 동적 데이터 구조
모든 Dropdown(문서·레이어·색상·선 종류·가중치·문자·치수스타일)은 Props 배열로만 그립니다.
- 항목 추가·삭제·이름/값 변경·순서 변경은 배열을 바꾸면 되고 컴포넌트 소스 수정이 필요 없습니다. 각 항목은 `id`로 식별되어 바뀐 항목만 다시 그리고 다른 행의 상태(입력 중 값 등)를 보존합니다.
- 현재 항목(`active*Id`)이 배열에서 사라지면 선택 표시가 없어지고 필드는 값 Props(`layer`, `lineStyle` 등)를 그대로 표시합니다. 다음 현재 항목은 호스트가 정합니다.
- `disabled`(선택 불가), `editable`(가중치 값 Input), Input의 `readOnly` / `disabled`를 항목별로 줄 수 있습니다.
- 목록이 길면 Dropdown 안에서만 스크롤합니다(행 목록 최대 240px = 8행, 사용자 색상은 팔레트 118px). 헤더 레이아웃은 유지됩니다.
- Storybook `SW / Header / Dynamic Data`에서 추가·삭제·이름·값·순서 변경을 직접 확인할 수 있습니다.
### 수정 가능한 Input — `SWHeaderInput`
레이어 상세 등 상세 영역에서 속성값을 직접 고칠 때 씁니다(Figma input 26:11656, cursor 26:11664). 상세 팝업 레이아웃은 아직 없으므로 Input과 계약만 제공합니다.
```tsx
import { SWHeaderInput, parseSWHeaderNumber, SWTheme } from "@hm-design/sw"
<SWTheme>
<SWHeaderInput
aria-label="X 좌표"
valueType="number" // "text"(기본) | "number"
value={entity.x} // 확정값(호스트). 바뀌면 최신 값 표시
onValueChange={draft => preview(draft)} // 편집 중 임시값(선택)
onCommit={next => updateEntity({ x: next })} // Enter · Focus 이동 · 감싼 Dropdown 닫힘, 유효 + 변경 시만
onCancel={() => clearPreview()} // Escape: 확정값으로 복원
parse={draft => parseSWHeaderNumber(draft, { min: 0 })} // 선택: 형식·범위 직접 지정
/>
</SWTheme>
```
- 상태: Default(배경·테두리 없음, 58 × 20 기준) / Hover(1px `rgba(212,203,189,.25)`, 24px) / 편집 중(`#0F0D0A`, 1px `.3`, 24px). 글자 `#E60403` 14px + 검정 Text Stroke, 글자 위치는 상태와 무관하게 고정. 폭은 값 길이에 맞춥니다.
- 커서: 네이티브 caret을 `#E60403`(`caret-color`)으로 표시하고 깜박임도 네이티브입니다(가짜 커서 없음). 선택·IME(한글 조합 중 Enter는 확정하지 않음)는 브라우저 기본 동작입니다.
- `onCommit`이 없거나 `readOnly`이면 읽기 전용(Hover·편집 표시 없음), `disabled`는 40%.
- 선택형(목록) 입력은 Figma 디자인 전달 후 같은 데이터 정의(`id`·이름·값·타입)로 확장합니다. 현재는 텍스트·숫자와 사용자 `parse`(임의 형식)를 지원합니다.
### 반응형 More — 대표 필드 전환
헤더가 좁아 필드가 더보기(…)로 옮겨졌을 때, 더보기에서 필드 항목을 고르면 그 필드가 객체 수정 영역 첫 칸(레이어 필드 자리)에 표시되고 Dropdown이 바로 아래에 열립니다. Focus는 그 필드로 옮겨갑니다.
- 레이어는 그동안 더보기로 이동하며, 더보기 → 레이어로 원래 배치로 돌아갑니다. 이미 화면에 있는 필드는 더보기에 나오지 않습니다.
- 전환은 UI 상태입니다. 데이터 Callback은 호출되지 않고, 필드 클릭과 같은 `on*Open` 1회와 `on*MenuOpenChange(true)`만 호출됩니다. 값은 그대로 유지됩니다.
- 폭이 넓어져 그 필드가 다시 보이는 단계에서는 1920 기준 배치로 돌아갑니다(Breakpoint 1800 / 1180 / 1080 / 900 / 640 / 520은 그대로).
- Dropdown 데이터(`colors` 등)가 없는 필드도 같은 방식으로 대표 칸에 표시됩니다(목록 없이 `on*Open`만).
## 8. 하위 컴포넌트 재사용
헤더 밖에서는 `SWTheme`로 감쌉니다. 소비자가 `hm-sw-*` class를 붙이지 않습니다.
```tsx
import { SWTheme, SWHeaderLayerControl, SWHeaderColorControl, SWHeaderProperties } from "@hm-design/sw"
<SWTheme>
<SWHeaderLayerControl value={{ name: "0" }} onClick={openLayers} />
<SWHeaderColorControl value={{ value: "255,229,0", swatch: "#ffe500" }} selected />
<SWHeaderProperties panelOpen={panelOpen} onPanelToggle={togglePanel} />
</SWTheme>
```
`SWTheme`는 배치(flex 등)를 정하지 않습니다. 배치는 `className`·`style`로 소비 화면에서 정합니다.
## 9. 테마 커스터마이징
`--hm-sw-header-*` 토큰만 바꿉니다. 사용자 확정 3개 면(파일명·객체 수정·속성 입력)은 기본값을 유지하는 것을 권장합니다.
```tsx
import type { CSSProperties } from "react"
<SWHeader documentTitle="a.dwg" style={{ "--hm-sw-header-selected-color": "#ffd84d" } as CSSProperties} />
```
토큰 목록과 사용처는 [디자인 명세 §14](./sw-header-design-spec.md#14-sw-전용-토큰-목록)를 따릅니다. `@hm-design/ui` Theme Runtime(JSON)과는 연결되어 있지 않습니다.
## 10. DWG/DXF 뷰어 연동 시 고려사항
- 기존 뷰어 `Toolbar.tsx`의 파일명(`fileName`)은 `documentTitle`로, 패널 토글(`propsOpen` 등)은 `propertyPanelOpen` + `onPropertyPanelToggle`로 연결할 수 있습니다.
- 뷰어의 파일 열기·샘플·Model/Layout 탭·2.5D·어두운 배경·로드 상태는 Figma SW Header에 없는 뷰어 전용 도구입니다. 필요하면 `tools` 슬롯에 호스트가 렌더링합니다(스타일은 호스트 책임).
- 헤더 높이는 49px입니다. 고정 배치(`position: fixed`)는 `className`·`style`로 호스트에서 지정합니다.
- 반응형은 모두 헤더 자신의 폭(container query)을 기준으로 하므로 사이드 패널 때문에 헤더가 좁아져도 동작합니다. 단계(1800 / 1180 / 1080 / 900 / 640 / 520)는 [디자인 명세 §12](./sw-header-design-spec.md#12-반응형)를 따릅니다.
- 뷰어는 현재 `@hm-design/ui`의 SWHeader 패턴을 사용합니다. 이 패키지로 바꾸는 작업은 이번 범위가 아닙니다.
## 10.1 상호작용 상태
- 그룹 필드 Hover(현재 추가 기준): 레이어·색상·선/문자/치수스타일은 버튼 전체 Hover 시 라벨·값과 지정된 선/폰트/치수 미리보기가 `#FFC600`이 됩니다. 기존 Gradient Hover Border와 검정 외곽선은 유지하며 색상 견본·레이어 상태 아이콘은 바꾸지 않습니다. Disabled는 새 Hover 효과를 적용하지 않고 Selected 색은 기존대로입니다. 상세보기의 텍스트 전용 Hover는 변경하지 않습니다. [디자인 명세 §11.5](./sw-header-design-spec.md#115-현재-추가-기준--1400ms-및-그룹-필드-hover-2026-10-16).
- Hover Border: 테두리가 있는 버튼(홈, 5개 속성 필드, 더보기, 인쇄, 팝업 가이드)은 마우스를 올리면 테두리가 Figma 홈 버튼의 노란 gradient로 바뀝니다. 5개 필드 내부 텍스트·미리보기는 위 추가 기준을 함께 적용합니다. 파일명과 속성Bar·속성창은 예외로, 테두리 없이 글자만 `#FFC600`으로 바뀝니다(파일명 Dropdown은 클릭해야 열림). 문서 Dropdown 안은 Figma hover를 따릅니다: 목록 항목은 검정 40% 배경, `+ New` / `Open`은 배경이 진해지고(20% → 30%) 글자가 검정 100%가 됩니다(노란 테두리 없음). 배경·크기·위치는 변하지 않습니다. 비활성 버튼은 바뀌지 않습니다.
- Hover(창 제어 · 바로가기 아이콘): 인쇄·팝업 가이드는 노란 테두리와 함께 아이콘의 `#B6D0C9` 부분만 `#FFC600`으로 바뀝니다(외곽선·그림자·타일 색은 그대로). 최소화는 검정 20% 배경 + 흰 막대, 종료는 `#E40000` 배경 + 흰 X로 바뀝니다(Figma 26:12156 / 26:12161, 32 × 48 유지, 노란 테두리 없음). 소비자는 class나 Prop을 추가하지 않습니다. 상세는 [디자인 명세 §11.2](./sw-header-design-spec.md#112-창-제어--바로가기-아이콘-hover-2026-10-16).
- Selected(`activeMenu`, `propertyBarOpen`, `propertyPanelOpen`): 글자만 `#FFC600`. Hover와 함께 표시됩니다.
- Focus: 키보드 Focus 윤곽선은 Hover 테두리 위에 표시됩니다.
- Hover 테두리 색은 `--hm-sw-header-hover-border-gradient` 토큰, 최소화 Hover 배경·막대 색은 `--hm-sw-header-window-button-hover-background` / `--hm-sw-header-window-icon-hover-color` 토큰으로 바꿀 수 있습니다. 종료·인쇄·팝업 가이드 Hover 아이콘은 패키지 SVG Asset입니다. 대상·제외 목록은 [디자인 명세 §11.1](./sw-header-design-spec.md#111-hover-테두리-공통-상호작용-규칙)을 따릅니다.
- Text Stroke(2026-10-16): Figma에 Stroke가 있는 헤더·Dropdown 텍스트는 검정 외곽선(14·16px 1.3px, 12px 라벨 1px)과 자기 색 0.3px Stroke를 표시합니다. Hover·Selected는 채움색만 바뀌고 외곽선은 유지됩니다. 더보기 메뉴·창 이동 Tooltip은 Figma에 Stroke가 없어 적용하지 않고, `+ New` / `Open`은 요청에 따라 기존 그대로입니다. 외곽선 색·굵기는 `--hm-sw-header-text-outline-color`, `--hm-sw-header-text-stroke-width` 등으로 바꿀 수 있습니다([명세 §6.1](./sw-header-design-spec.md#61-text-stroke-2026-10-16)).
## 10.2 창 이동 Hover Motion
별도 Prop이나 의존성 없이 Hover하면 GIF의 경로·구간 비율을 유지하면서 후속 사용자 요청에 따라 **1.4초마다 반복**합니다(원본 GIF 1.64초와 다름). 커서가 아래에서 위로 먼저 올라가고 약 444ms에 문구가 나타난 뒤 창·커서·문구가 오른쪽 위로 이동합니다. 약 888ms에 이동을 마치고 약 512ms 유지한 뒤 반복합니다. 28px / −4.5px 이동과 가속·감속 비율은 그대로이며 이탈 시 즉시 기본 SVG로 돌아갑니다. 크기·줄무늬·SVG·Tooltip 규격은 SW/Figma를 유지합니다.
`onWindowMoveStart`는 기존 `PointerEventHandler<HTMLDivElement>`입니다. 예를 들어 호스트는 `event.currentTarget.setPointerCapture(event.pointerId)`로 이후 포인터를 추적할 수 있습니다. 패키지는 포인터를 캡처하거나 실제 창을 이동시키지 않습니다. 아이콘과 `창 이동` Tooltip은 `pointer-events: none`이며 Callback 이벤트와 최소화·종료 클릭을 가로채지 않습니다. 누르는 동안 CSS `:active`로 모션을 멈추고 Tooltip을 숨깁니다. Tooltip은 창 이동 영역 밖의 최소화 및 종료 왼쪽 일부에 겹칠 수 있습니다.
- 헤더 폭 ≥1080px: 88px 영역, GIF 기준 반복 모션과 Tooltip.
- 640–1079px: 24px 드래그 영역, 그림은 숨기고 정적 Tooltip만 표시.
- <640px: 기존대로 창 이동 영역 숨김.
- Reduced Motion: 기본 아이콘과 정적 Hover Tooltip을 유지하고 애니메이션을 끕니다. 24px 단계에서는 기존대로 그림이 숨겨집니다. OS 설정을 재생 중 바꾸어도 적용됩니다.
- Focus: 창 이동 div는 기존대로 Tab 대상이 아니며 키보드 창 이동·Focus Tooltip을 제공하지 않습니다. 실제 호스트의 키보드 창 이동 대안과 보조기술 검증은 별도로 필요합니다. 최소화·종료는 이름이 있는 기본 버튼이며 Focus 표시가 유지됩니다.
Tooltip은 45 × 17px, 검정 80% 배경과 흰색 12px/400 글자입니다. Tooltip만 Figma의 Inter를 우선하고 한글·미설치 환경은 기존 SW 글꼴로 fallback합니다. 별도 폰트 설치는 하지 않습니다. 경로·자산은 [명세 §11.4](./sw-header-design-spec.md#114-현재-창-이동-motion--ani_hover_movegif-우선-2026-10-16), 현재 시간과 필드 Hover는 [명세 §11.5](./sw-header-design-spec.md#115-현재-추가-기준--1400ms-및-그룹-필드-hover-2026-10-16), 결과는 [현재 통합 검증](./sw-header-speed-composed-hover-verification-2026-10-16.md)을 참고합니다. 원본 GIF 속도 검증과 GIF 전달 전 결과는 각각 [GIF 검증 기록](./sw-header-window-motion-gif-verification-2026-10-16.md), [과거 검증 기록](./sw-header-window-motion-verification-2026-10-16.md)에 보존합니다.
## 11. 지원 · 미지원
| 지원 | 미지원(호스트 또는 후속 작업) |
|---|---|
| Figma 헤더 1개(26:8944) 표시·선택·Focus·Disabled | 색상·선스타일·문자스타일·치수·레이어 **상세 팝업**(Figma 전달 후), 색 선택 팝업(Figma "추후 디자인 진행예정") |
| 색상(26:10986 / 26:11036)·선스타일(26:11104)·문자스타일(26:11181)·치수스타일(26:11215) Dropdown, ByLayer/ByBlock, RGB·가중치·스케일 Input(2026-10-16) | 실제 색 적용·픽셀 색상 추출·선/문자/치수 스타일 적용(호스트 구현) |
| 반응형 More 대표 필드 전환, `SWHeaderInput`(Default / Hover / 편집 / 커서), Text Stroke(2026-10-16) | 선택형 상세 Input(Figma 미전달) |
| 문서 Dropdown(26:10662) 목록·선택·New/Open Callback·키보드 | 실제 파일 열기·새 문서 만들기(호스트 구현) |
| 레이어 Dropdown(26:10940) 목록·선택·전구/눈꽃/자물쇠 Callback·레이어 상세 Callback·키보드 | 실제 레이어 표시·동결·잠금과 레이어 상세 화면(호스트 구현) |
| 1800 / 1180 / 1080 / 900 / 640 / 520 반응형 + 더보기(옮긴 기능도 같은 Props·Callback) | SW 팝업(DraggablePanel) |
| SSR 문자열 렌더링(window 접근 없음) | 창 이동·최소화·종료의 실제 동작 |
| React 19, ESM, 타입 선언 | CommonJS 빌드, Light/Dark 테마, Runtime Theme JSON 연결 |
## 12. Storybook
```bash
npm run storybook
```
`SW / Header` 아래 Story: Overview, Default, Controls, Interactive States(인쇄·팝업 가이드·최소화·종료 Default/Hover 비교와 Callback 기록 포함), Responsive, Composed Controls, Logo Slot(기본 임시 로고 / 사용자 지정 로고 / 로고 미전달), Document Dropdown(Default / Hover / Open / 선택 / 여러 문서 / 문서 없음 / 긴 파일명 / 좁은 화면), Layer Dropdown(Layer Default / Hover / Open(Figma 8행) / Row Hover / 전구·눈꽃·자물쇠 4상태 3배 확대 / 선택·Toggle Callback 기록과 상태표 / 여러 상태·현재 레이어 / 긴 레이어명 / 20개 스크롤 / 레이어 없음 / 레이어 상세 / 상태 표시 전용 / 좁은 화면 1440–390). Responsive Story는 헤더 폭 18개(1920–390, 각 단계 경계 전후 포함) 프레임을 나열합니다. 모든 단계가 헤더 폭 기준이므로 Viewport를 바꾸지 않아도 확인할 수 있습니다.
`Window Move Motion`은 GIF 주요 3상태와 속도 변경 후 약 0 / 205 / 410 / 444 / 683 / 888 / 1366 / 1400ms 프레임, 실제 Hover·Drag·최소화·종료 Callback 및 반복(`motion cycle`) 기록, 축소 폭 예제를 제공합니다. 내부 고정 시간은 원본 GIF 시간 × 1400/1640의 정확한 값입니다. 고정 프레임은 Storybook 전용 Hover attribute와 Web Animations API로 시간을 멈춥니다. Reduced Motion에서는 기본 아이콘·정적 Tooltip 정책을 따르므로 정상 타임라인 비교는 해당 설정을 끄고 Story를 다시 로드합니다.
2026-10-16 추가 Story: `Color Dropdown`, `Line Style Dropdown`, `Text · Dimension Style Dropdown`(Default / Hover / Expanded / 행 Hover / 독립 버튼 Hover 무변화 / Callback 기록 / 좁은 화면), `Responsive More`(대표 필드 전환 Callback 기록 1440·768·390 + 16개 폭), `Text Stroke`(Figma 원본 이미지 비교 · 3배 확대 · 말줄임), `Layer Detail · Input`(Figma 참조 이미지 · 레이어 상세 Default/Hover 무변화/Disabled/Callback · Input Default/Hover/ReadOnly/Disabled/편집/확정·취소·외부 값·IME), `Dynamic Data`(항목 추가·삭제·이름·값·순서 변경). `Document Dropdown`의 첫 예제는 현재 열린 문서 1개이며, 기본 `figmaArgs`도 그 문서 1개를 전달합니다(`documents=[]`는 예외 상태 예제로만 남김). 검증 결과는 [Dropdown·More·Text Stroke·Input 검증 기록](./sw-header-dropdowns-stroke-input-verification-2026-10-16.md)에 있습니다.
`Composed Controls`에는 기존 레이어·색상·상세보기와 공개 선/문자/치수 컨트롤, 5개 필드의 고정 Hover 비교가 있습니다. 실제 공용 컴포넌트를 렌더링하며 60 × 15px 치수 Hover SVG는 흰 foreground만 노란색으로 바꾸고 검정 mask 외곽선을 유지합니다. 기존 Story만 보완했으며 새 Story는 추가하지 않았습니다. [현재 통합 검증](./sw-header-speed-composed-hover-verification-2026-10-16.md)을 참고합니다.
## 속성창·K 가이드 추가 연결 (2026-10-16)
기존 공개 Props를 유지한 추가 API입니다. `propertyPanel`을 전달하면 기존 속성창 버튼에 패널이 연결됩니다. 미전달 시 기존 토글/Callback 계약만 유지하며 임의의 CAD 데이터/편집 창을 만들지 않습니다.
```tsx
import { useState } from "react"
import { SWHeader, type SWHeaderProperty } from "@hm-design/sw"
import "@hm-design/sw/styles.css"
function DrawingHeader() {
const [properties, setProperties] = useState<SWHeaderProperty[]>([
{ id: "layer", groupId: "common", name: "레이어", type: "text", value: "층_01" },
{ id: "weight", groupId: "common", name: "선가중치", type: "number", value: "0.25", min: 0, unit: "mm" },
])
return <SWHeader
documentTitle="도면"
propertyPanel={{
selection: { id: "selection-42", kind: "single", title: "선택된 객체 - 호스트 객체" },
groups: [{ id: "common", collapsedCount: 6 }],
properties,
onPropertyChange: (property, value) => {
// 실제 CAD 반영 및 계산은 호스트에서 수행한 뒤 결과를 Props로 돌려줍니다.
setProperties(items => items.map(item => item.id === property.id ? { ...item, value } : item))
},
onToolActiveChange: active => console.info("호스트 선택 도구", active),
}}
onPropertyPanelOpenChange={open => console.info("속성창 열림", open)}
/>
}
```
- Uncontrolled: `propertyPanelOpen`을 생략합니다. Controlled: `propertyPanelOpen={open}`, `onPropertyPanelOpenChange={setOpen}`을 함께 전달합니다. 기존 `onPropertyPanelToggle`은 버튼/More 클릭 시 그대로 호출됩니다. 이전 Controlled 호스트가 새 Open Callback 없이 기존 Toggle Callback만 사용한다면 X/Escape 닫기에서도 기존 Toggle로 알립니다.
- 선택 대상이 바뀔 때 `selection.id`도 반드시 변경합니다. 같은 property id라도 이전 객체의 임시 편집값은 재사용하지 않습니다.
- `selection.kind`: none/all/single. `selection.title`·`counts`·`overviewTitle`은 호스트 데이터입니다. counts의 선택적 row/column은 Figma 집계 배치를 표현할 수 있습니다. `onOverviewClick`도 선택 변경을 요청할 뿐 내부 CAD 동작은 없습니다.
- 항목 타입: text/number/select/color/slider/computed. `id/name/value/type/groupId`가 필수이고, `order`로 표시 순서를 지정합니다. 중복 id를 사용하지 마세요.
- `options`는 select의 id/name/disabled 목록입니다. 선택은 Enter 또는 Focus 이동으로 확정하고 Escape로 취소합니다. 삭제되거나 disabled가 된 option은 새 값으로 확정하지 않습니다. 옵션 데이터가 바뀌면 임시 선택을 현재 호스트 값으로 돌립니다.
- `color`는 기존 RGB/HEX parser를 재사용합니다. `swatch`로 별도 견본을 제공할 수 있습니다. `linePreview/linePattern`은 기존 선 미리보기를 재사용합니다. `annotation`은 (ByL) 같은 보조 정보, `unit`은 mm/% 같은 단위입니다.
- number/slider는 `min/max`, slider는 `step`을 지원합니다. slider의 기본 범위는 0~100이며 Story의 0~1 등은 호스트가 지정한 범위입니다. 수치를 계산하거나 Figma의 시각적 thumb 위치에 맞춰 임의 변환하지 않습니다.
- `validate(draft)`가 false면 거부합니다. 항목의 `onCommit(value, property)`와 패널의 `onPropertyChange(property, value)`는 서로 다른 통지 지점입니다. 둘 다 연결하면 각각 통지됩니다.
- 그룹은 `layout=rows/circle/position`, `order/name/collapsedCount/positionLabel`을 받습니다. circle은 원본 SVG 위 `graphicSlot=circumference/diameter/radius/area`의 항목을 배치합니다. 다른 항목은 그래픽 아래에 배치합니다. 계산값은 computed/readOnly로 전달합니다.
- `expandedGroupIds/onExpandedGroupsChange`로 펼침을 제어하거나, Uncontrolled일 때 `defaultExpandedGroupIds`로 시작합니다. 펼침은 property Callback을 실행하지 않습니다.
- `toolActive/onToolActiveChange/toolLabel`로 내부 아이콘을 연결합니다. 현재 의미는 호스트가 정하는 선택 도구 상태 통지이며 CAD 기능을 내장하지 않습니다.
- 단독 사용: `<SWTheme><SWHeaderPropertyPanel ... onClose={close} /></SWTheme>`. 단독 배치는 호스트의 `style`로 지정할 수 있습니다.
- 동적 패널 입력은 `SWHeaderInput commitOnUnmount={false}`를 사용합니다. 기존 Input의 기본 `true` 동작은 유지합니다.
- K 가이드는 별도 데이터를 요구하지 않습니다. 기존 K 버튼 Hover/Focus로 원본 이미지가 Pan/Zoom 반복되고, 작은 화면의 More에서도 같은 미리보기가 보입니다. Reduced Motion·Escape·재진입 초기화가 적용됩니다. 클릭은 기존 `onPopupGuide`만 호출합니다.
- 색상 모드의 null은 직접 색상 의미를 보존하고, 필수 모드 표시는 마지막 유효 모드(처음 ByLayer)를 유지합니다. 선택된 모드를 재클릭해도 onColorModeChange가 중복 호출되지 않습니다.
- 신규 Story: Property Panel / Property Panel Variants / K Guide / Additional Hover. 기존 17개 Story를 유지합니다.
실행 결과와 제한은 [추가 검증 기록](./sw-header-additional-verification-2026-10-16.md)을 따릅니다.
## 후속 5개 요구사항 연결 (2026-10-16)
### 속성Bar와 공유 데이터
```tsx
import { SWHeader, SWHeaderPropertyBar, type SWHeaderProperty } from "@hm-design/sw"
import "@hm-design/sw/styles.css"
const fields: SWHeaderProperty[] = [
{ id: "length", groupId: "shape", name: "길이", type: "number", value: length, min: 0 },
]
const data = {
selection: { id: objectId, title: objectName, kind: "single" as const },
properties: fields,
onPropertyChange: (property: SWHeaderProperty, next: string) => updateObject(property.id, next),
}
<SWHeader documentTitle={fileName}
propertyBar={data} propertyBarOpen={barOpen} onPropertyBarOpenChange={setBarOpen}
propertyPanel={{ ...data, groups: [{ id: "shape", name: "형상" }] }}
propertyPanelOpen={panelOpen} onPropertyPanelOpenChange={setPanelOpen} />
```
- `propertyBar`는 `SWHeaderPropertyBarProps`: selection/properties/onPropertyChange, 전체 readOnly/disabled 및 div HTML 속성입니다. 개별 property의 readOnly/disabled/validate/min/max도 적용합니다. 컴포넌트 밖에서 단독 사용하면 `SWTheme`로 감쌉니다.
- `propertyBar` 생략 시 propertyPanel의 selection/properties/onPropertyChange만 공유합니다. 두 데이터를 모두 생략하면 토글 상태만 제공하며 임의 속성이나 CAD 수치를 만들지 않습니다.
- `propertyBarOpen` 생략 시 내부 상태, `defaultPropertyBarOpen`으로 초기값을 정합니다. Controlled는 `propertyBarOpen/onPropertyBarOpenChange`를 함께 사용합니다. 기존 `onPropertyBarToggle`도 클릭마다 보존됩니다. 속성창 열림과 독립적입니다.
- properties는 id로 식별하고 `order` 오름차순(같은 값은 배열 순서)으로 표시합니다. 객체 교체 시 selection.id를 바꿉니다. name/value/항목 추가·제거·외부 변경을 반영합니다.
- 값 편집은 `SWHeaderInput`을 사용합니다. Enter/Blur 확정, Escape 취소, 유효하지 않은 입력 복원, 외부 value 반영, 편집 중 삭제 draft 폐기를 지원합니다. 바의 select 타입은 option id를 입력하고 해당 options로 검사합니다. slider 타입은 숫자로 편집합니다. computed는 읽기 전용입니다. 속성창의 select/range UI를 바에 복제하지 않습니다.
- 헤더는 49px, 바는 별도 28px입니다. 폭은 최대 1222px이며 객체 수정 영역의 실제 위치부터 부모의 남은 폭을 사용합니다. x=377 고정값은 없습니다. 좁으면 focus 가능한 region 안에서 가로 스크롤하며 input focus로 해당 값에 접근할 수 있습니다. 호스트 콘텐츠는 열림 상태에 따라 필요한 28px 공간을 예약하세요. 기존 Dropdown은 바 위에 표시됩니다.
### 메뉴 컴포넌트 자체 구성
```tsx
<SWHeader documentTitle={fileName}
controls={[{ id: "lineStyle", label: "선 종류" }, { id: "layer" }]}
layers={layers} activeLayerId={layerId} onLayerSelect={selectLayer}
lineTypes={lineTypes} lineStyle={lineStyle} onLineTypeSelect={selectLineType} />
```
- `controls?: readonly SWHeaderControlConfig[]`는 전체 allowlist입니다. 미전달하면 승인된 기존 구성·계층·1920px 디자인을 유지합니다. `[]`는 선택적 컨트롤 전부 제거입니다. 홈/로고·창 제어는 유지합니다.
- id: document / drawingMode / layer / color / lineStyle / textStyle / dimensionStyle / details / propertyBar / propertyPanel / print / popupGuide. `details`는 상세보기 안내 caption이며 필수 액션이 아닙니다.
- 배열에서 빼거나 `visible:false`로 숨기며, 배열 순서가 실제 DOM·키보드·배치 순서입니다. 중복 id는 첫 번째 표시 항목만 사용합니다. 숨김 해제/재추가할 수 있습니다. `label`은 필드 라벨·버튼 이름·More 이름을 바꿉니다. 문서는 실제 documentTitle을 유지하고 접근성 이름 접두사를 바꿉니다. K는 아이콘을 유지하고 접근성 이름을 바꿉니다.
- 구성 모드는 각 컨트롤을 하나의 객체 수정 영역 안에서 재배치합니다. 기본 모드의 그룹 외곽 배치를 바꾸지 않습니다. 파일·색상 등을 제거한 공간은 고정 슬롯으로 남지 않습니다.
- 기존 1800/1180/1080/900/640/520px 기준은 유지합니다. 남은 필드 중 레이어(없으면 배열의 첫 필드)를 대표 슬롯으로 사용하고 More 선택으로 대체합니다. 대표 요소 삭제 시 남은 필드로 대체합니다. 표시 중인 대표는 More에서 제외되고, 구성에서 빠진 요소는 More에서도 사라집니다.
- 값은 기존 Props/Callback으로 계속 호스트가 소유합니다. 재정렬은 keyed 컴포넌트를 유지하고, 제거된 Dropdown과 draft는 정리하며 데이터 Callback을 생성하지 않습니다. 내부 열림 상태는 제거 후 재추가 시 닫힘입니다. Controlled 열림은 호스트가 전달한 값이 우선합니다. 일반적인 Dropdown 닫힘 시 기존 입력 확정 계약은 유지합니다.
- 구성요소 배열과 Dropdown 내부 옵션 배열은 별개입니다. layers/colors/userColors/lineTypes/lineWeights/textStyles/dimensionStyles의 동적 기능은 그대로 사용합니다.
### Colorize와 CSS 경로
- 색상 메뉴의 기존 `색상 추출` 위치에 Figma `26:11302`/`26:11305` 원본 SVG를 연결했습니다. 버튼 24×24, 아이콘 18×18, Hover는 주황 경계와 검정 25% overlay입니다. Hover는 Callback을 실행하지 않으며 클릭/Enter는 기존 `onColorPick`을 호출하고 메뉴를 닫습니다.
- `26:11338 color_돋보기` Overlay는 패키지에 구현되어 있지 않습니다. 호스트가 onColorPick에서 연결할 대상이며 임의 대체 색상창은 만들지 않았습니다.
- CSS 편집 원본은 `packages/sw/src/theme/sw.css`입니다. SW build가 dist/styles.css를 만들고 Story가 공개 `@hm-design/sw/styles.css`를 읽습니다. dist만 편집하면 다음 빌드에서 원본이 다시 포함됩니다. 원본 상세 span의 Stroke 선언과 미사용 solid-stroke-width 변수를 제거했으므로 산출물 override는 필요하지 않습니다. 외부 소비 앱에는 SW를 다시 빌드한 뒤 산출물을 갱신하세요.
- Text Stroke Story의 지정 원본 스크린샷·제목·전용 import를 제거했습니다. 이미지 파일과 나머지 비교 Story·text-shadow는 보존했습니다.
- 기존 21개 Story에 Property Bar / Dynamic Controls / Colorize를 추가했습니다. 현재 실행 결과는 [후속 검증 기록](./sw-header-followup-five-verification-2026-10-16.md)을 따릅니다.
## 구성요소 표시·메뉴 순서·속성창 4상태·형상 Slot (2026-10-16 3차)
이 절이 같은 주제의 이전 설명보다 우선합니다. 검증 결과는 [3차 검증 기록](./sw-header-composition-shape-verification-2026-10-16.md)에 별도로 기록했습니다.
### 로고 필수, 나머지 개별 제거 — `hiddenControls`
```tsx
<SWHeader documentTitle={fileName} logoSrc={logoUrl} logoAlt="EasyBeam"
hiddenControls={["document", "color", "textStyle", "print", "minimize"]}
/* 값·목록·Callback Props는 기존 그대로 */ />
```
- `hiddenControls?: readonly SWHeaderHideableControlId[]`. 대상 id: `home`, `document`, `drawingMode`, `layer`, `color`, `lineStyle`, `textStyle`, `dimensionStyle`, `details`, `propertyBar`, `propertyPanel`, `print`, `popupGuide`, `windowMove`, `minimize`, `close`. 로고는 대상이 아니며 항상 표시합니다(`logo` / `logoSrc` / `logoAlt` Props 유지).
- `home`을 제거하면 홈 동작만 사라지고 로고는 같은 32×17 슬롯에 정적 이미지로 남습니다(Hover·`onHomeClick` 없음).
- 전체 `controls` 목록 없이 쓸 수 있으며, 남은 요소는 기본 디자인의 크기·계층·Breakpoint를 유지합니다. `controls`와 함께 쓰면 두 조건을 모두 적용합니다(`controls`의 `visible:false`와 같은 의미).
- 제거한 요소는 렌더링하지 않습니다: 공간 없음, Dropdown·속성창·속성Bar·K 가이드 표시 안 함, Callback 없음. 열린 상태에서 제거하면 내부 열림 상태만 조용히 닫힙니다(데이터 Callback 없음).
- 배열에서 다시 빼면 재표시됩니다. 선택값은 호스트 Props이므로 그대로이고, 내부 Dropdown은 닫힌 상태로 돌아옵니다.
- 미전달 또는 `[]`이면 승인된 기본 Header와 동일합니다(1920~390px DOM 위치·크기·색 비교 차이 0).
- 객체 영역 요소를 제거하면 More 이동은 CSS 대신 스크립트가 같은 Breakpoint(1800/1180/1080/900/640)로 결정합니다. 표시할 항목이 없는 폭에서는 More 버튼을 만들지 않습니다. 창 영역은 남은 요소 폭만 차지합니다.
- 로고만 남겨도 Header 높이 49px와 녹색 객체 영역 배경은 유지합니다.
### 유사 메뉴 순서 — `menuOrder`
```tsx
<SWHeader documentTitle={fileName}
menuOrder={["color", "layer", "dimensionStyle", "lineStyle", "textStyle"]} />
```
- 대상: `layer`, `color`, `lineStyle`, `textStyle`, `dimensionStyle`(`SWHeaderMenuId`). 메뉴는 메뉴가 이미 차지한 자리 안에서만 서로 바뀌며 홈·파일·로고·그리기모드·상세보기·창 제어는 움직이지 않습니다.
- 미전달: 승인된 기본 순서 `레이어 → 색상 → 선스타일 → 문자스타일 → 치수스타일`(`controls`를 쓰면 그 배열 순서).
- 누락 규칙: 지정한 id가 먼저 지정 순서로 오고, 빠진 id는 그 뒤에 기본 순서로 붙습니다. 중복 id는 첫 번째만, 알 수 없는 id는 무시합니다. 숨긴 메뉴가 들어 있어도 오류 없이 건너뜁니다.
- 순서는 DOM·Tab 순서·More 목록 순서에 함께 반영됩니다. React key는 메뉴 id라 재정렬해도 값·Callback·Dropdown·Focus가 유지되며, 순서 변경만으로는 어떤 Callback도 호출하지 않습니다.
- 대표 슬롯: 레이어가 표시 중이면 레이어, 아니면 순서상 첫 메뉴가 기본 대표입니다. More에서 고른 메뉴가 대표 자리를 대신하며, 그 메뉴가 제거되면 기본 대표로 돌아갑니다.
### 속성창 4가지 상태
새 팝업 없이 기존 `SWHeaderPropertyPanel` 하나를 호스트 데이터로 바꿉니다.
| 상태 | Figma | selection.kind | 데이터 |
|---|---|---|---|
| 선택된 객체 없음 | 26:11886 · 314×256 | `none` | common 그룹만 |
| 전체 객체 선택 | 26:11823 · 314×361 | `all` + `counts` | common 그룹, 혼합값 `mixed` |
| 원 1개 선택 | 26:11932 · 314×658 | `single` + `overviewTitle` | common(collapsedCount) + shape(circle) + position |
| 모든 항목 펼침 | 26:12035 · 314×754 | `single` | 위와 같고 `expandedGroupIds={["common"]}` |
Storybook: `Property Panel · 선택 없음 / 전체 선택 / 원 1개 / 모두 펼침` 개별 Story와 `Property Panel · 4개 비교`(디자인 검증용, 실측 크기 표시, 좁으면 줄바꿈). 실제 Header는 현재 선택에 맞는 패널 하나만 표시합니다.
### 형상 영역 Slot — `shapeContent`
```tsx
<SWHeader documentTitle={fileName} propertyPanel={{
selection, properties, groups,
shapeContent: ({ selection, group, properties }) => <MyShapeViewer objectId={selection.id} />,
}} />
```
- `layout: "circle"` 그룹의 208×208 영역(그룹 260px 안 39, 26)에 렌더링합니다. `SWHeaderPropertyPanel`과 `SWHeader.propertyPanel` 모두 같은 Prop입니다.
- `undefined`(미지정): Figma 원형 그림(기존 기본). `null` / `false`: 빈 영역 — 크기·치수 라벨 위치·패널 높이 유지. ReactNode: 외부 컴포넌트. 함수: `{ selection, group, properties }`(해당 그룹 항목)를 받아 렌더링 — 객체 유형별 교체에 사용합니다.
- 내용은 영역 안에 맞춰지고(overflow hidden) 원주·지름·반지름·면적 입력은 그 위에 그대로 표시됩니다. 값은 `properties`로만 전달되며 형상 Slot과 독립입니다. 입력은 기존 SWHeaderInput 검증·Enter/Blur 확정·Escape 취소를 사용합니다.
- 패키지는 CAD 계산·형상 렌더링 엔진을 포함하지 않습니다. Storybook의 사각형/점선 원 SVG는 Story 전용 샘플입니다.
@@ -0,0 +1,101 @@
# SW Header 창 이동 Motion — GIF 기준 최종 검증 (2026-10-16)
## 1. 최종 판정: PARTIAL
GIF 기준 모션 수정과 Chrome 브라우저 검증 **PASS**, Root Typecheck / SW Build / Root Build / Storybook Build **PASS**입니다. 실제 호스트의 OS 창 이동·최소화·종료, 다른 브라우저·보조기술과 독립 SW 패키지의 Guard coverage는 **NOT VERIFIED**이므로 전체 PASS로 확대하지 않습니다. GIF 이전 검증은 [과거 기록](./sw-header-window-motion-verification-2026-10-16.md)으로 보존합니다.
## 2. 시작 Git 상태
- Worktree: `D:\디자인시스템\HMDesign-SW`, branch `feature/sw-package`.
- HEAD: `9407c8567f57301659e591c03451cca1b828733c`.
- 기존 변경: ` M package-lock.json`, ` M package.json`, `?? docs/sw/`, `?? packages/sw/`, `?? src/sw/`.
- `_local/sw-motion-gif-qa/baseline.json`에 기존 SW·Story·문서와 root manifest/Lockfile의 SHA-256을 보존하고, 변경 전 CSS·창 제어 TSX·Story 사본도 저장했습니다. 기존 미추적 파일을 삭제·초기화하지 않았습니다.
## 3. 기존 구현과 GIF의 차이
기존 구현은 2.7265658초 1회 재생이며, 커서가 이미 위쪽에 있는 별도 SVG로 fade-in하고 창을 축소한 뒤 오른쪽으로 이동했습니다. 문구가 이른 시점부터 나타났고 마지막에 dissolve로 Default로 복귀했습니다. 이 방식은 이번 GIF의 ‘아래쪽 커서 상승 → 문구 → 전체 이동 → 유지 → 즉시 반복’과 달랐습니다. 수정 전에 같은 40ms 간격의 41개 실제 브라우저 프레임을 캡처했습니다.
## 4. 추가 수정 범위
기존 모션 CSS만 GIF 표본으로 바꾸고 기존 `Window Move Motion` Story의 설명·고정 시간·반복 로그를 갱신했습니다. 기존 Default SVG, Hover용 SVG 두 개, 줄무늬, Tooltip box·색상·크기, 공개 API와 TSX 이벤트 연결은 유지했습니다. Tooltip 글꼴 선언만 Figma의 Inter를 우선하고 기존 SW 글꼴을 fallback하도록 명시했습니다. 폰트 파일이나 의존성은 추가하지 않았습니다.
누르는 동안 CSS `:active`로 3개 애니메이션을 일시정지하고 Tooltip을 숨깁니다. 전역 이벤트·포인터 캡처·preventDefault·React 상태는 추가하지 않았습니다.
## 5. 프레임별 비교 결과
기준 파일: `D:\디자인시스템\ani_hover_move.gif`, SHA-256 `c7d22819cfb8f8cd8593684ce1e5c676a545fa09fa9fc7573d59f0f237d28026`. 640 × 192px, 41프레임, 각 40ms, 1640ms, 무한 반복입니다.
GIF 높이 192px를 48px Header bar의 4배로 환산했습니다. 창의 녹색 폭도 96 / 4 = 24px로 기존 SVG와 일치합니다. GIF의 여분 가로 캔버스는 UI에 넣지 않았습니다. 비교 이미지에서만 가로 여백 32px를 잘라 기존 아이콘의 시작 위치를 정렬했습니다. 비교 결과는 Header 크기의 CSS px로 표시합니다.
- 0–520ms: 창은 제자리, 커서는 오른쪽 아래에서 위로 약 12.5px 이동하고 왼쪽으로 0.75px 이동합니다.
- 520ms: 문구가 처음 나타납니다. 41개 프레임 전체에서 문구 표시 여부가 일치했습니다.
- 약 600–1040ms: 창은 오른쪽 28px / 위 4.5px 이동합니다. 커서는 최초 위치 대비 오른쪽 27.25px / 위 17px입니다. 40ms 표본 사이를 선형 보간하여 원본의 가속·감속을 따라가며 GIF에 없는 spring overshoot를 제거했습니다.
- 1040–1640ms: 600ms 동안 창·커서·문구의 끝 자세를 유지합니다. 1640ms에 첫 프레임으로 즉시 반복합니다.
- 최종 41개 캡처의 녹색 도형 좌상단 비교: 창 위치 최대 오차 0px, 커서 최대 오차 0.5px. 문구 glyph 좌상단 최대 오차 0.5px입니다.
- 커서와 문구의 보이는 간격: GIF 9px, 구현 9.25–9.5px이며 프레임별 차이는 0.5px 이내입니다.
위 수치는 색이 있는 도형/glyph의 경계 좌표 비교이며 전체 픽셀 동일성 판정은 아닙니다. 기존 Figma SVG의 검정 외곽선·녹색 줄무늬·Tooltip 배경을 유지했으므로 GIF의 검정 캔버스와 외관 전체를 복제하지 않았습니다. 현재 동작은 Figma Prototype의 1.022/0.744/0.958초 spring·1회 정책보다 GIF의 시간과 반복을 우선합니다.
## 6. Drag 검증
실제 공개 `@hm-design/sw`를 React로 렌더링한 QA fixture에서 확인했습니다. `onWindowMoveStart`가 원래 div의 PointerEvent를 한 번 받았고 `target/currentTarget`, `pointerId`, `pointerType=mouse`, `buttons=1`, `defaultPrevented=false`를 유지했습니다. 호스트 모의 코드가 설정한 Pointer Capture를 통해 Tooltip·창 제어 버튼 위로 이동해도 pointermove와 pointerup이 전달됐습니다.
포인터를 누르는 동안 애니메이션은 paused이며 200ms가 지나도 currentTime이 변하지 않았고 Tooltip은 숨겨졌습니다. Drag 중 최소화·종료 콜백은 발생하지 않았고, 이후 각 버튼 클릭은 자신의 콜백만 호출했습니다. Reduced Motion에서도 드래그 콜백·해제가 유지됐습니다. 실제 OS 창 이동과 네이티브 호스트 통합은 NOT VERIFIED입니다.
## 7. Tooltip · 반복 · Reduced Motion · Focus
- PASS: 45 × 17px, 검정 80% 배경, 흰색 12px/400, Inter 우선 + SW fallback 선언. 한글 또는 Inter가 없는 환경은 기존 Noto Sans KR 등으로 표시되며 Inter 파일 로드를 보증하지 않습니다.
- PASS: 장식 자식 전체의 `pointer-events: none`, Tooltip 겹침 위치에서 최소화 버튼 hit test, 최소화·종료 Callback 전달.
- PASS: 실제 Hover를 유지해 1.64초 간격의 iteration 2회, 중복 animationstart 없음. 12회 빠른 진입·이탈에서 start/cancel 각 12회, 잔여 애니메이션 0개.
- PASS: Hover 해제 즉시 Default SVG·위치·문구 숨김, 재진입 시 처음부터 시작.
- PASS: 재생 중 reduce 전환 시 애니메이션 0개, 원래 Default SVG와 정적 Tooltip 유지. 누르는 동안 Tooltip 숨김, 이탈하면 숨김. 24px 단계의 기존 그림 숨김 정책도 유지.
- PASS: 최소화 → 종료 Tab 이동, 2px Focus 윤곽선, Enter로 종료 콜백.
- 기존 제한: 이동 div는 role=presentation/Tab 비대상이며 키보드 창 이동·Focus Tooltip을 제공하지 않습니다. 호스트의 키보드 대안과 보조기술은 미검증입니다.
## 8. 반응형
1920 / 1440 / 1280(추가) / 1080 / 1079 / 768 / 640 / 639 / 390px에서 실제 viewport와 Header 폭을 검사했습니다. 모두 높이 49px, Header 및 문서의 가로 overflow 0px, Tooltip은 Header 경계 안이었습니다. 1080px 이상 이동 영역 88px, 640–1079px는 24px와 정적 Tooltip만, 639px 이하는 영역 숨김을 유지합니다. 최소화·종료의 배치와 클릭은 유지했습니다.
Tooltip은 기존처럼 이동 영역 밖으로 나와 최소화·종료 일부에 시각적으로 겹칩니다. 시각적 겹침이 없다고 판정하지 않았으며, 포인터 차단이나 가로 스크롤이 없음을 확인했습니다. 기존 문서명·레이어명의 말줄임 구조는 변경하지 않았습니다.
## 9. Typecheck · Build · Storybook · Guard
- PASS: `npm.cmd run typecheck` — root·SW를 포함한 workspace 타입 검사와 기존 pretypecheck의 theme 확인·package build. 최종 CSS와 Story 변경 뒤 실행했습니다.
- PASS: `npm.cmd run build:sw` 및 최종 pretypecheck에 포함된 SW Build.
- PASS: `npm.cmd --ignore-scripts run build`, `npm.cmd --ignore-scripts run build-storybook`. 이미 통과한 공통 prerequisite을 중복 실행하지 않았습니다.
- PASS: Chrome `155.0.8059.79`, 실제 CSS 애니메이션·9개 Story 단계·520ms 등장/1640ms 반복 경계·motion cycle/Drag/최소화/종료 로그·Reduced Motion. 브라우저 통합 검사 41개 PASS, pageerror 0건, HTTP 오류 0건.
- 기존 경고: 문서 예시 `bg-[var(...)]` CSS 최적화, Storybook `:host-context` minifier, 500kB 초과 chunk와 plugin timing 안내. exit 0이며 범위 밖의 기존 코드를 수정하지 않았습니다.
- 개발 서버의 기존 포트·프로세스를 건드리지 않고 빌드된 Storybook과 QA fixture를 임시 localhost 서버에서 검사하고 종료했습니다.
- Guard 1회: `node scripts/ai-generation-guard.mjs --target . --profile STORYBOOK --scope PARTIAL --files src/sw/sw-header.stories.tsx,packages/sw/src/theme/sw.css --json`. 원본 status/staticStatus **NOT VERIFIED**, FAIL 없음. SW styles export 미해석 `ENTRY-002`, 패키지 graph 밖 `SCOPE-001`, 연결에 의존한 Token/동적 값 미검증이 남았습니다. Guard를 수정하거나 PASS로 덮어쓰지 않았습니다.
- Guard 원본 styleEvidence는 STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED이며 checks=[]입니다. SW 실검사 완료라는 뜻은 아닙니다. 별도 Runtime은 위 Chrome 범위 PASS, 디자인 의도는 GIF 프레임 좌표·순서 및 Figma 자산·Tooltip 규격 범위에서 확인했고, 전체 검증은 호스트 등의 미검증 때문에 PARTIAL입니다.
## 10. 변경 파일 · 근거
1. `packages/sw/src/theme/sw.css` — 창 이동 모션 블록만 수정.
2. `src/sw/sw-header.stories.tsx` — 기존 모션 Story 블록만 갱신.
3. `docs/sw/sw-header-design-spec.md` — 과거 §11.3을 보존하고 현재 GIF 기준 §11.4 추가.
4. `docs/sw/sw-header-usage.md` — 현재 모션·Drag·반복·Story 사용법 반영.
5. `packages/sw/README.md` — 현재 GIF 기준과 검증 링크 반영.
6. 이 검증 기록 — 신규 문서.
Git 무시 경로 `_local/sw-motion-gif-qa/`에 원본 분석 JSON, 전/후 41개 PNG 및 좌표 JSON, `frame-comparison.json`, 비교 이미지 `gif-before-after.png`, 실제 React 캡처 미리보기 `window-move-preview.gif`, 폭별 PNG, `storybook-final.png`, `verification.json`, build 로그, 원본 `guard.json`, 시작 해시와 사본을 보존합니다. GIF는 QA 미리보기일 뿐 제품 Header에 사용하지 않습니다.
## 11. 기존 디자인 · 작업 보호
모션 블록 밖 CSS와 기존 TSX/이벤트 연결이 시작 사본과 동일함을 확인했습니다. SVG·나머지 Story·기존 반응형·다른 Header 요소·root package.json/Lockfile·과거 검증 문서도 시작 해시와 일치합니다. 다른 패키지 소스·다른 Worktree·DWG/DXF 뷰어를 변경하지 않았습니다. Root의 기존 package build는 Git 무시 dist만 재생성했습니다. 의존성 설치는 없습니다.
## 12. 남은 항목
실제 Electron/설치형 호스트의 OS 창 이동·최소화·종료, 터치/펜, 다른 브라우저, 키보드 창 이동 대안과 스크린리더, 전용 SW Guard coverage는 NOT VERIFIED입니다. Tooltip의 기존 시각적 버튼 겹침은 명시한 채 유지했습니다.
## 13. 종료 Git 상태
브랜치 `feature/sw-package`, HEAD `9407c8567f57301659e591c03451cca1b828733c`를 유지합니다. 기존 status 그룹 ` M package-lock.json`, ` M package.json`, `?? docs/sw/`, `?? packages/sw/`, `?? src/sw/`도 유지하며 이번 변경은 그 미추적 그룹 내부에 포함됩니다. Index 변경·Commit·Push·PR·Merge·Publish·배포는 수행하지 않았습니다. `git diff --check`와 문서 링크/공백 검사를 확인했습니다.
작업 시간(KST, GIF 수신 후 실제 작업): Started At 2026-10-16 13:38:28 / Completed At 2026-10-16 13:52:16 / Actual Duration 13분 48초 / Estimated Duration 20~30분 / Variance 20분 대비 −6분 12초, 30분 대비 −16분 12초.
## 14. 후속 변경 — 1400ms 및 Composed Controls Hover
위 1.64초·41프레임 검증은 당시 결과로 보존합니다. 이후 사용자 요청으로 경로·Keyframes 비율을 유지한 채 주기만 1400ms로 변경했습니다. 현재 문구 등장 약 444ms, 이동 완료 약 888ms, 유지 약 512ms입니다. 원본 GIF의 시간과 다른 의도적인 변경입니다.
같은 후속 작업에서 그룹 필드의 Hover 텍스트 및 지정 선/폰트/치수 미리보기를 `#FFC600`으로 추가 강조했습니다. 검정 외곽선과 기존 Gradient Hover Border·색상 견본·레이어 상태를 유지하고 상세보기는 제외했습니다. 이 변경의 새로운 검증 결과와 미검증 범위는 [속도·Composed Hover 통합 검증](./sw-header-speed-composed-hover-verification-2026-10-16.md)에 별도로 기록합니다. 위 과거 수치와 판정을 소급 변경하지 않습니다.
@@ -0,0 +1,85 @@
# SW Header 창 이동 Motion 최종 검증 — 2026-10-16
## 1. 최종 판정: PARTIAL
요청 범위의 Chrome 브라우저 검증과 빌드는 통과했습니다. Reduced Motion의 실제 CSS 오류 한 건을 수정하고 영향 범위를 재검증했습니다. 실제 호스트 창 제어, Figma player의 반복 정책·수치 곡선 동등성, 전용 SW Guard 검증은 미완료이므로 전체 PASS로 처리하지 않습니다.
## 2. 작업 시작 시 Git 상태
- 경로: `D:\디자인시스템\HMDesign-SW`, 브랜치 `feature/sw-package`.
- HEAD: `9407c8567f57301659e591c03451cca1b828733c`.
- origin: `https://gitea.hmac.kr/b25027/HMDesign.git`.
- 기존 변경: ` M package-lock.json`, ` M package.json`, `?? docs/sw/`, `?? packages/sw/`, `?? src/sw/`. SW는 미추적 상태이므로 일반 `git diff`만으로 기존 구현과 이번 작업을 구분할 수 없습니다.
- 이 작업은 사용자 지정 SW Worktree에서만 수행했고 Git 이력·Index·원격을 변경하지 않았습니다. 시작 파일의 SHA-256은 `_local/sw-motion-qa/baseline.json`에 보존했습니다.
## 3. 기존 Claude 구현에서 확인한 사항
`SWHeaderWindowControls.tsx`의 기존 div / `onPointerDown`, 3개 SVG, 4개 CSS Keyframes, 직접 선언된 `linear()` easing, 1회 재생·즉시 이탈 복귀, Tooltip, 88px / 24px / 숨김 단계, `Window Move Motion` Story와 7개 고정 프레임이 실제로 존재했습니다. 300ms의 Hover 창·커서·Tooltip opacity는 `0.930667`로 측정했습니다. 모션과 Drag 코드를 다시 작성하지 않았습니다.
## 4. 이번 추가 수정
기존 Reduced Motion 규칙 `.hm-sw-header__motion:hover > *`보다 레이어별 애니메이션 선택자의 specificity가 높아 모션 해제가 적용되지 않았습니다. 이 규칙만 동일 specificity의 `:is(.레이어...)` 선택자로 바꿨습니다. 일반 Keyframes·시간·SVG·좌표·공개 Props·Callback·Breakpoint는 그대로입니다. 나머지 수정은 문서 추가입니다.
초기 검증 JSON의 두 FAIL은 보존했습니다. 수정 후 해당 Reduced Motion 검사와 영향 범위만 별도로 재검사했으며, 성공했던 일반 모션·반응형 검사를 형식적으로 반복하지 않았습니다.
## 5. Figma 3단계 비교
Figma `rj6wpsfk24lOWvWiVRAANP` / `26:10220`의 디자인 컨텍스트·스크린샷·Prototype reactions를 읽었습니다. Default `26:10221`, Hover `26:10258`, Hover2 `26:10295`의 크기와 좌표는 [명세 §11.3](./sw-header-design-spec.md#113-창-이동-hover-motion--figma-2610220-2026-10-16)에 기록했습니다.
- PASS: 1022ms의 창 (32, 15), 24 × 18.857px; 1767ms의 창 (59, 12), 커서 (72, 10), Tooltip (89, 11). CSS subpixel 오차 0.05px 이내로 비교했습니다.
- PASS: GENTLE / QUICK / BOUNCY preset 이름, SMART_ANIMATE / DISSOLVE, 1.0220938 / 0.7442555 / 0.9582165초와 두 1ms 대기는 원본 reactions와 일치합니다. QUICK overshoot, DISSOLVE 혼합, 실제 1회 재생 완료·Default 복귀도 확인했습니다.
- PASS: Hover SVG 두 개는 원본과 일치하며 Default SVG는 마스크 ID 차이만 있습니다. 원본 파일을 교체하지 않았습니다.
- PARTIAL: Figma motion API의 animated-node 결과는 비어 있었고 preset의 수치 곡선은 반환되지 않았습니다. 기존 spring 표본 근사와 opacity용 단조 BOUNCY를 유지했으며, 중간 프레임의 완전 동일성은 검증하지 않았습니다. Tooltip 글꼴은 기존 SW 정책인 Noto Sans KR이며 Figma의 Inter와 다릅니다.
- NOT VERIFIED: Figma Prototype player에서 Default 복귀 뒤 포인터 유지 중 자동 재시작하는지. 구현은 1회 재생 정책을 유지했습니다.
## 6. Drag 검증
공개 `@hm-design/sw` 패키지를 실제 React로 렌더링한 로컬 QA fixture에서 PointerEvent를 받았습니다. `onWindowMoveStart` 1회, `currentTarget`과 `target`이 기존 div임, `pointerId`·`pointerType=mouse`·`buttons=1` 전달, `defaultPrevented=false`를 확인했습니다. 호스트 모의 코드가 Pointer Capture를 설정한 뒤 Tooltip·최소화 영역 위로 이동해도 pointermove와 pointerup이 전달됐습니다. Drag 중 최소화·종료 콜백이 발생하지 않았고, 이후 각 버튼 클릭은 자신의 콜백만 호출했습니다. Reduced Motion에서도 같은 전달을 확인했습니다.
실제 Electron/설치형 호스트의 OS 창 이동·최소화·종료, 터치·펜 입력과 네이티브 Drag 중 애니메이션은 **NOT VERIFIED**입니다. 브라우저 검증은 호스트 통합 완료 판정이 아닙니다.
## 7. Tooltip · Reduced Motion · Focus
- PASS: Tooltip과 내부 그래픽의 `pointer-events: none`; Tooltip이 겹친 위치의 hit test는 최소화 버튼에 도달합니다. 실제 버튼 콜백도 전달됩니다.
- PASS: Hover 종료 즉시 기본 상태; 빠른 진입·이탈 12회에서 start/cancel 각각 12회, 잔여 애니메이션 0개; 포인터를 4초 이상 유지해도 비정상 반복 없음.
- PASS(수정 후): reduce에서 애니메이션 0개, 기본 아이콘과 정적 Tooltip 유지, 이탈 시 숨김, 재생 중 reduce 전환 즉시 정지. 24px 영역은 기존대로 그림을 숨기고 Tooltip만 표시합니다.
- PASS: 최소화 → 종료 Tab 이동, 접근성 이름과 2px Focus 윤곽선, Enter로 종료 콜백.
- 기존 제한: 창 이동 div는 `role=presentation`, `tabIndex=-1`이며 키보드 이동·Focus Tooltip을 제공하지 않습니다. 키보드 이동 대안과 보조기술 검증은 호스트 과제입니다. 접근성 전체 PASS를 선언하지 않습니다.
## 8. 반응형
실제 viewport와 독립 Header 폭으로 1920 / 1440 / 1280(추가) / 1080 / 1079 / 768 / 640 / 639 / 390px를 검사했습니다. 모든 폭에서 높이 49px, 문서와 Header의 가로 overflow 0px, Tooltip은 Header의 세로·가로 경계 안이었습니다. 1080px 이상 move 88px, 640–1079px move 24px와 정적 Tooltip, 639px 이하 숨김을 확인했습니다.
Tooltip은 원본 좌표 관계상 최소화와 종료 왼쪽 일부에 **시각적으로 겹칩니다**. 클릭을 차단하거나 가로 스크롤을 만들지는 않으나, 이를 ‘시각적 겹침 없음’으로 판정하지 않았고 승인된 배치를 임의로 바꾸지 않았습니다. 390px의 기존 문서명·레이어명 말줄임도 유지됩니다.
## 9. Typecheck · Build · Storybook · Guard
- PASS: `npm.cmd run typecheck` — root와 SW를 포함한 workspace typecheck. 기존 pretypecheck의 theme 확인·package build도 통과했습니다.
- PASS: `npm.cmd run build:sw` — CSS 수정 후 최종 산출물 생성.
- PASS: `npm.cmd --ignore-scripts run build`, `npm.cmd --ignore-scripts run build-storybook` — 이미 통과한 공통 prerequisite의 중복 실행을 생략했습니다. CSS 변경 후 관련 build 결과를 다시 생성했습니다. 의존성 설치·Lockfile 수정은 없습니다.
- PASS: 빌드된 Storybook의 실제 CSS animation, 단계별 7개 프레임 고정 시간, 기존 pointer/animation/callback log, 정상·reduce 모드. Chrome `155.0.8059.79`, headless mouse/keyboard에서 확인했습니다. 6024 개발 서버의 포트 불가 안내 뒤 기존 프로세스를 건드리지 않고 임시 localhost 서버를 사용했으며 종료했습니다.
- 기존 경고: 문서 예시 `bg-[var(...)]`의 CSS 최적화, Storybook의 `:host-context` minifier, 500kB 초과 chunk 및 plugin timing 안내. 관련 과거 문서에도 기록된 경고이며 이번 오류로 분류하지 않았습니다. runtime pageerror는 0건입니다. 초기 fixture에서 404 console 경고 1건이 있었고 최종 동일 fixture의 HTTP 오류 재확인에서는 0건이었습니다(초기 경고 URL은 기록하지 않아 원인 단정 안 함).
- Guard 1회: `node scripts/ai-generation-guard.mjs --target . --profile STORYBOOK --scope PARTIAL --files src/sw/sw-header.stories.tsx,packages/sw/src/theme/sw.css --json`. 원본 `status` / `staticStatus`는 **NOT VERIFIED**입니다. `@hm-design/sw/styles.css` 연결 미해석(`ENTRY-002`), 패키지 구현이 Storybook graph 밖(`SCOPE-001`), 해당 연결에 의존한 Token/동적 값 미확인이 남았습니다. Guard를 변경하거나 원본 결과를 PASS로 덮어쓰지 않았습니다.
- Guard `styleEvidence`: STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED, `checks=[]`. SW CSS 실검사 완료를 뜻하지 않습니다. 별도 브라우저 Runtime은 위 범위 PASS, 디자인 의도는 정적 좌표 확인과 spring/반복 미검증을 합쳐 PARTIAL, 최종은 PARTIAL입니다.
## 10. 변경 파일
1. `packages/sw/src/theme/sw.css` — Reduced Motion 해제 선택자 수정.
2. `docs/sw/sw-header-design-spec.md` — §11.3 추가.
3. `docs/sw/sw-header-usage.md` — §10.2와 기존 Story 설명 보완.
4. `packages/sw/README.md` — 모션 사용·검증 링크 추가.
5. 이 검증 기록 — 신규 문서.
로컬 QA fixture·JSON·캡처·로그는 Git 무시 경로 `_local/sw-motion-qa/`에 있습니다. 원본 FAIL을 포함한 `verification.json`, 수정 후 `reduced-final.json`, 종합 `final-summary.json`, 원본 `guard.json`, `baseline.json`, build 로그와 폭별 PNG를 보존합니다. 이 파일들은 패키지 배포·공개 API에 포함하지 않습니다.
## 11. 기존 디자인 · 작업 보호
시작 해시와 비교해 기존 파일 중 변경된 것은 위 CSS·문서 4개뿐입니다. 새 선택자를 원래 한 줄로 되돌린 임시 사본의 SHA-256이 시작 CSS와 일치하여, CSS의 다른 바이트는 보존됨을 확인했습니다. 기존 TSX·SVG·Story·package.json·Lockfile은 시작 상태와 일치합니다. 다른 패키지의 소스·DWG/DXF 뷰어·다른 Worktree는 수정하지 않았습니다. Root의 기존 prerequisite build에 의해 Git 무시 dist는 재생성됐습니다.
## 12. 남은 문제 · NOT VERIFIED
실제 네이티브 창 제어, Figma player 반복 및 수치 easing의 완전 동등성, `linear()` 미지원 브라우저, 다른 브라우저·터치·펜·스크린리더, 키보드 창 이동 대안, 전용 SW Guard coverage는 미검증/후속 범위입니다. Tooltip의 시각적 버튼 겹침은 위와 같이 명시하며 변경하지 않았습니다.
## 13. 작업 종료 시 Git 상태
브랜치와 HEAD는 시작 상태를 유지합니다. 변경 그룹도 ` M package-lock.json`, ` M package.json`, `?? docs/sw/`, `?? packages/sw/`, `?? src/sw/`이며, 기존 미추적 그룹 내부에 이번 CSS·문서 변경이 포함됩니다. Stage·Commit·Push·PR·Merge·Publish·배포는 수행하지 않았습니다. Git 상태만으로 이번 변경이 0개라는 뜻은 아니며 파일별 시작 해시 비교를 함께 사용합니다.