38 KiB
SW Step Header 디자인 명세 (SWProcessHeader)
상태:
@hm-design/sw1차 구현 기준(2026-11-03). 코드:packages/sw/src/components/sw-process-header/, CSS:packages/sw/src/theme/sw-process-header.css, Story:SW / Step Header(2026-11-03 표시 명칭 변경, 컴포넌트·공개 API 이름SWProcessHeader·SWProcessBar등은 유지). 원본: Strana Main Header —D:\Strana\Git_StranaDevDesignHEADedda3cc(Header 7173e82 + 분할 버튼 b7f412c)의HeaderStyles.xaml,HeaderBrushes.xaml,MainProcessBar.xaml,MainWorkspaceWindowView.xaml, HanmacColors.xaml. Strana 저장소는 읽기 전용 참고이며 수정하지 않습니다. 승인: 2026-11-03 사용자 승인(방안 B · 설계 계획서 7절 9개 항목 권장안).
1. 개요
SW 일반 모드의 독립 Header입니다. SWHeader(편집 메뉴 Header), SWViewerHeader(뷰어 모드)와 같은 층위이며 한 화면에는 Header를 하나만 둡니다. SWHeader·SWViewerHeader·sw.css는 바꾸지 않았습니다.
| 공개 컴포넌트 | 역할 |
|---|---|
SWProcessHeader |
49px Header 프레임: 로고 → 파일명 → 가운데(프로세스 · Layer 면) → 창 영역(도구 pill · 창 이동 · 최소화 · 최대화/복원 · 종료) |
SWProcessBar |
프로세스 단계 영역(그룹 번호·이름, 단계 탭, 그룹 사이 화살표, 활성 그룹 발광). Header 밖에서는 SWTheme 안에서 씁니다 |
SWProcessSplitButton |
tools pill 안 화면 분할 도구(버튼 + 6칸 선택 패널, §11.3) |
2. 구조와 재사용
SWProcessHeader <header class="hm-sw-theme hm-sw-process-header"> 49px (면 48 + 아래 1px 배경)
├─ SWHeaderHome 재사용 — 9-slice 홈 도형(Strana HomeFace/HomeBorder/HomeShadow와 같은 경로·gradient·그림자)
├─ SWHeaderDocumentTitle 재사용 — 파일명 면(DocFace / DocBorder = SW 문서 토큰과 같은 값)
├─ __center
│ ├─ SWProcessBar 신규
│ └─ __layer 면 SW 객체 면 토큰(ObjFace / LayerFaceBorder와 같은 값)
│ └─ Layer 필드 신규 내부 — 전구는 SWHeaderLayerToggle(내부) 재사용, Strana 전구와 같은 그림
└─ __window DocFace 면 + DocBorder 1px
├─ SWToolGroup pill 재사용(`tools` 슬롯을 감쌈) — 테두리·면만 Strana 값으로 범위 안에서 조정
├─ 창 이동 재사용 — SWHeaderWindowControls(창 이동만): 줄무늬 · 아이콘 · 1.4s Hover Motion · 툴팁
└─ 캡션 버튼 3개 신규 내부 — 최소화 · 최대화/복원 · 종료
SWHeaderLayerControl·SWHomeMenu는 쓰지 않습니다(빈 20px 칸, 2단 타일 메뉴가 Strana와 달라서). SWHeaderWindowControls는 창 이동만 쓰고(showMinimize·showClose false), 최소화·최대화/복원·종료는 이 Header의 캡션 버튼입니다(2026-11-03 변경, §10).
3. 레이아웃·반응형
Strana Grid의 별 비율을 CSS flex 비율로 옮겼습니다. 숨김 단계·More는 없습니다(Strana와 같음). 보장 폭은 1100px 이상이며 그보다 좁으면 Header 안에서 잘립니다.
| 영역 | Strana | 구현 |
|---|---|---|
| 홈 | 106 × 48, 오른쪽 17px이 파일명 위로 겹침 | SWHeader 우선(2026-11-03): SWHeaderHome 그대로 = 12 + 로고 칸(높이 17, 로고 비율 폭) + 36, 32 × 17 로고 → 80. 오른쪽 15px이 파일명과 겹친 뒤 2px 간격(SWHeader bar gap, margin-right: -15px). logoText는 18px 700, 58px 칸(→ 106) |
| 파일명 | 286*, 최소 100, 최대 286, 글자 시작 22 |
폭만 Strana: flex: 286 1 0, min-width: 100px, max-width: 286px. 글자는 SWHeader 그대로(x 65 시작, padding 5 / 12 → 글자 x 70, 14px 700·자간 0.84px·SW 외곽선·세로 가운데), SW 화살표 숨김 |
| 가운데 | 1260* |
flex: 1260 1 0 |
| 프로세스 | 1003* |
flex: 1003 1 0 |
| Layer 면 | 257*, 최소 152, 최대 257, 양옆 1 |
flex: 257 1 0, 칸 152 ~ 257, 면은 칸 안 양옆 1px(150 ~ 255, 가상 요소), 높이 48, radius 6 |
| Layer 필드 | 면 안 양옆 8, 최대 234 × 38, padding 12 / 10 | 같음(가운데 정렬) |
| 창 영역 | Auto: pill(위·왼쪽 3, 높이 42) + 창 이동 88 + 버튼 32 × 3 | 같음(약 313px, 도구 3개 기준) |
| 프로세스 그룹 | 자연 폭까지, 좁아지면 자연 폭 비율로 함께 줄어듦, 양옆 남는 폭은 같게 | 그룹 flex: 0 1 auto(flex-shrink가 기본 폭에 비례), 양옆 flex: 1 1 0 |
| 단계 탭 | UniformGrid, 넓을 때 127 + 간격 1, 좁으면 0까지 | flex 한 줄 flex: 1 1 0(같은 폭), 크기 기준 요소 127px, 선택 탭도 함께 축소(§11.1) |
4. 프로세스 영역 규칙
| 요소 | 규칙 |
|---|---|
| 바탕 | ProcessBase 세로 gradient, DocBorder 1px, 위 모서리 6 |
| 비활성 덮개 | 비활성 그룹 위 --hm-sw-process-dim. 왼쪽 남는 폭 = 첫 그룹, 오른쪽 남는 폭 = 마지막 그룹 밝기. 화살표 칸: 꼭지(앞 그룹 비활성) / 홈(뒤 그룹 비활성) |
| 활성 그룹 면 | 기운 왼쪽 변 + 둥근 아래 모서리 도형(ProcessGlowLeft), 화살표 쪽으로 사라짐, 발광 Halo + 가장자리 빛. 첫 그룹은 왼쪽 40px, 마지막 그룹은 좌우 반전·오른쪽 44px 밖으로 |
| 비활성 그룹 면 | ProcessIdleLeft 도형, Blur + 어두운 Halo + 테두리 빛, 바깥쪽에서 안쪽으로 사라짐(첫 그룹 79.8 → 198.4, 마지막 그룹 236.7 → 273.1) |
| 사다리꼴 배치(확정 2026-11-03) | 그룹 면(사다리꼴)은 프로세스 영역의 시작(첫 그룹)과 끝(마지막 그룹)에만 둡니다. 그 그룹이 켜져 있으면 발광 면, 아니면 비활성 면. 가운데 그룹에는 켜져 있어도 도형이 없고 밝은 바탕(덮개 없음)만 보입니다(Figma 873:17421). 그룹 수와 관계없이 같으며 default·multi 공통. 화살표·비활성 덮개·선택 상태는 그대로 |
| 화살표 | 16 × 48, 앞 그룹 활성 = On(채움 #27D9A5), 아니면 Dim(채움 25%, 선 50%) |
| 번호 / 이름 | 12px, 줄 높이 14. 번호 500 #69C2A6. 이름 최대 56px 줄바꿈, 활성 700 흰색 / 비활성 500 #C3EBDF, 폭은 늘 700 기준. 위 여백 10.67(한 줄) / 8.67(두 줄) |
| 탭 | 높이 32, padding 0 12 2.67, 16px 700 #F0F7F5. 모서리: 단독 16, 첫 탭 16 4 4 16, 마지막 탭 4 16 16 4, 가운데 4 |
| 탭 상태 | 기본 층 Opacity 0.7. Hover = 글자 #FFC600. Checked = 층 1, 바탕·그늘·광택 교체, 위 5 · 왼쪽 6 안쪽 그림자 0.75, 테두리 2px 0.25, 글자 #FFC600. Disabled = 층 0.5, 글자 #64A491 |
Strana의 2그룹(01 Modeling: Structure · Loads / 02 Analysis Result: Results)이 확정 디자인입니다. 1그룹·3그룹 이상은 렌더링되지만 디자인 미확정입니다.
5. HM 규칙 적용으로 바뀐 점 (원본 대비 변경)
| 항목 | Strana 원본 | 구현 | 근거 |
|---|---|---|---|
| 파일명 | TextBox(말줄임 없음, 편집 가능하나 반영 안 됨) | 버튼 + 말줄임, title로 전체 이름 |
승인 7 |
| Focus | 표시 없음(FocusVisual null) | SW 2px #F0F7F5 outline |
승인 7 |
| 글자 외곽선 | DropShadow Blur 2 | SW --hm-sw-header-text-outline(14·16·18px) / --hm-sw-header-label-outline(10·12px). multi도 같음(Figma에는 Stroke 없음) |
승인 7, 2026-11-03 요청 |
| 창 이동 | 정적 장식(Motion 없음) | SWHeader 창 이동 Motion(1.4s 반복, Hover 해제 즉시 복원, 툴팁 창 이동, Drag 중 정지, Reduced Motion 정적) |
2026-11-03 요청(이전 승인 5의 "Motion 없음"을 대체) |
| 파일명 자간 | 0 | 0.06em(재사용한 SW 파일명 면) | 재사용 부품 |
| Layer 필드 | ToggleButton 안에 전구 ToggleButton 중첩 | 형제 button 2개(전구 · 필드), 보이는 모양 같음 | HTML은 버튼 중첩 불가 |
| 탭 3개 이상 가운데 탭 | 정의 없음(첫 탭 외 모두 오른쪽 모서리) | 모서리 4 | 일반화 |
| 테마 | 4개(Hanmac · Dark · Light · HighContrast) | Hanmac 값만 기본값, 나머지는 §7 Override | 승인 4, Light/Dark preset 금지 |
| 로고 메뉴 · Layer Popup | 있음 | 1차 범위 제외(버튼 상태·Callback만) | 승인 6 |
| 화면 분할 버튼 · 선택 Popup | 있음 | SWProcessSplitButton으로 추가(2026-11-03, §11). 버튼 아래 연장을 Header 끝까지 늘려 패널이 Header 아래에서 시작(Strana는 3px — 창 버튼 아래 끝을 가림) |
2026-11-03 요청 |
| 도구 아이콘 | DesignSystem Geometry | Storybook fixture(src/sw/sw-process-header-fixture.tsx)의 TSX 아이콘, 패키지는 tools 슬롯. 상태 색은 currentColor |
승인 8 |
| 좁은 폭의 탭 글자 · Layer 이름 | 잘림(Clip) | … 말줄임 |
HM 말줄임 규칙(승인 7과 같은 기준) |
| 그림 자산 | XAML Geometry · DrawingBrush | 파일 대신 인라인 SVG(TSX)와 CSS — 색을 토큰으로 바꿀 수 있게 | 승인 4(토큰 Override) |
| 도구 버튼 눌림 | 노란 링 + 아이콘 #FFC600 |
같음. SWToolButton pill에는 눌림 모양이 없어 이 Header 범위 안에서만 더함(SWHeader의 pill은 그대로) |
원본 재현 |
| 발광 · Blur | WPF DropShadow 30 / 15, Blur 10 / 8 / 2 | CSS drop-shadow 30 / 15px, blur 5 / 4 / 1px(반경 → 표준편차) |
근사값 — 1920 캡처 비교로 확인 |
6. 토큰
새 토큰은 --hm-sw-process-*이며 sw-process-header.css의 가장 바깥 SW 범위(sw-footer.css와 같은 규칙)에 선언합니다. 값이 SW 토큰과 같은 곳은 SW 토큰을 그대로 읽습니다(파일명 면·테두리, 객체 면·테두리, 속성 필드 면·그림자, Hover gradient, 선택 #FFC600, 제목·글자, 창 이동 줄무늬, 창 버튼 글리프·Hover 배경).
전체 목록과 Strana 원본 키는 §7 표의 왼쪽 두 열을 봅니다.
7. Strana 테마 → 토큰 Override 대응표
Hanmac 열이 기본값입니다. 다른 테마는 Header의 style(가장 바깥 SW 범위)에 같은 토큰을 지정합니다. 예: style={{ "--hm-sw-header-selected-color": "#FFC833" }}. Strana ARGB #AARRGGBB는 CSS rgba()로 바꿉니다. 아래 표는 Strana Styles/Theme/*/Colors.xaml(HEAD edda3cc)에서 생성했습니다.
| 토큰 | Strana Color.Header.* |
Hanmac(기본값) | Dark | Light | HighContrast |
|---|---|---|---|---|---|
--hm-sw-header-background |
Background | #060606 |
#0b0d11 |
#b9c7d7 |
#000000 |
--hm-sw-header-text-color |
Text | #f6f4f2 |
#f4f7fa |
#1c2430 |
#ffffff |
--hm-sw-header-title-color · --hm-sw-process-tab-text |
Title | #f0f7f5 |
#f4f7fa |
#1c2430 |
#ffffff |
--hm-sw-header-selected-color |
Selected | #ffc600 |
#ffc833 |
#0e54c6 |
#ffff00 |
--hm-sw-header-text-outline-color |
TextOutline | #000000 |
#000000 |
#ffffff |
#000000 |
--hm-sw-header-hover-border-gradient |
HoverBorderDark | #a67d03 |
#a67d03 |
#2f5faf |
#ffff00 |
| ″ | HoverBorder | #f2cc0f |
#f2cc0f |
#5c88d6 |
#ffff00 |
| ″ | HoverBorderLight | #fadd4f |
#fadd4f |
#8fb0e8 |
#ffff00 |
| ″ | HoverBorderDeep | #926e03 |
#926e03 |
#2f5faf |
#ffff00 |
--hm-sw-header-document-background |
DocFaceEdge | #081813 |
#12151a |
#c6d4e3 |
#000000 |
| ″ | DocFaceDeep | #09271e |
#181c23 |
#d0dce8 |
#000000 |
| ″ | DocFaceMid | #0b3227 |
#1e232b |
#d9e3ef |
#000000 |
| ″ | DocFaceLight | #0e3d2f |
#262c36 |
#e3ebf4 |
#000000 |
| ″ | DocFaceLow | #0a2b21 |
#1a1e25 |
#cfdae6 |
#000000 |
--hm-sw-header-document-border-gradient · --hm-sw-process-base-border-gradient |
DocBorderTop | #0e4c3a |
#3a4250 |
#ffffff |
#ffffff |
| ″ | DocBorderDark | #041611 |
#0b0d11 |
#afbecd |
#ffffff |
| ″ | DocBorderBottom | #18503f |
#3f4856 |
#9daec0 |
#ffffff |
--hm-sw-header-document-border-color |
DocBorderTop | #0e4c3a |
#3a4250 |
#ffffff |
#ffffff |
--hm-sw-header-object-background |
ObjFaceEdge | #144838 |
#1c2027 |
#d3dce6 |
#000000 |
| ″ | ObjFaceLow | #1a4536 |
#20252d |
#dae2eb |
#000000 |
| ″ | ObjFaceMid | #1f4134 |
#22272f |
#dde5ed |
#000000 |
| ″ | ObjFaceDeep | #10392d |
#1a1e25 |
#d3dce6 |
#000000 |
| ″ | ObjFaceLight | #296b55 |
#353d48 |
#f0f4f8 |
#000000 |
| ″ | ObjFaceUpper | #1f5a47 |
#2a3039 |
#e7edf3 |
#000000 |
| ″ | ObjFaceTop | #124133 |
#1e232b |
#d8e0ea |
#000000 |
--hm-sw-header-object-border-gradient |
ObjBorderTop | #135643 |
#313843 |
#c3cfdb |
#ffffff |
| ″ | ObjBorderDeep | #0e3d2f |
#1e232b |
#afbecd |
#ffffff |
| ″ | ObjBorderHighlight | #34daaa |
#74c4b4 |
#5c88d6 |
#ffffff |
| ″ | ObjBorderDark | #08241c |
#12151a |
#afbecd |
#ffffff |
| ″ | ObjBorderBottom | #1f5e4b |
#2e353f |
#c3cfdb |
#ffffff |
--hm-sw-header-property-background |
PropertyFace | rgba(0, 0, 0, 0.451) |
rgba(0, 0, 0, 0.451) |
rgba(255, 255, 255, 0.349) |
rgba(0, 0, 0, 1) |
| ″ | PropertyFaceLight | rgba(0, 0, 0, 0.4) |
rgba(0, 0, 0, 0.4) |
rgba(255, 255, 255, 0.302) |
rgba(0, 0, 0, 1) |
--hm-sw-header-property-shadow |
PropertyShadow | #09271e |
#000000 |
#5a6878 |
#000000 |
--hm-sw-header-stripe-color |
Stripe | rgba(28, 106, 81, 0.322) |
rgba(63, 72, 86, 0.322) |
rgba(47, 95, 175, 0.239) |
rgba(0, 0, 0, 0) |
--hm-sw-header-window-icon-color |
WindowIcon | #adb0af |
#adb0af |
#4a5868 |
#ffffff |
--hm-sw-header-window-icon-hover-color |
WindowIconHover | #ffffff |
#ffffff |
#1c2430 |
#000000 |
--hm-sw-header-window-button-hover-background |
WindowButtonHover | rgba(0, 0, 0, 0.2) |
rgba(0, 0, 0, 0.2) |
rgba(0, 0, 0, 0.122) |
#ffff00 |
--hm-sw-header-layer-icon-hover-background |
LayerIconHover | rgba(0, 0, 0, 0.251) |
rgba(0, 0, 0, 0.251) |
rgba(0, 0, 0, 0.149) |
rgba(255, 255, 255, 0.251) |
--hm-sw-process-base-background |
ProcessBaseEdge | #0c5941 |
#1c2027 |
#c6d4e3 |
#000000 |
| ″ | ProcessBaseLight | #19bf8d |
#4a5260 |
#f0f4f8 |
#000000 |
| ″ | ProcessBaseMid | #109069 |
#313843 |
#d9e3ef |
#000000 |
--hm-sw-process-dim |
ProcessDim | rgba(0, 68, 48, 0.902) |
rgba(11, 13, 17, 0.902) |
rgba(207, 218, 230, 0.902) |
rgba(0, 0, 0, 0) |
--hm-sw-process-number-color |
ProcessNumber | #69c2a6 |
#74c4b4 |
#2c5a88 |
#ffffff |
--hm-sw-process-group-color |
ProcessGroupText | #c3ebdf |
#c9d3de |
#4a5868 |
#ffffff |
--hm-sw-process-group-active-color |
ProcessGroupActive | #ffffff |
#ffffff |
#1c2430 |
#ffff00 |
--hm-sw-process-glow-face-base |
ProcessGlowFaceTop | #063b2b |
#1a1e25 |
#e3ebf4 |
rgba(0, 0, 0, 0) |
| ″ | ProcessGlowFaceFade | rgba(13, 101, 74, 0.051) |
rgba(42, 48, 57, 0.051) |
rgba(217, 227, 239, 0.051) |
rgba(0, 0, 0, 0) |
| ″ | ProcessGlowInner | rgba(24, 134, 101, 0.702) |
rgba(63, 111, 104, 0.702) |
rgba(92, 136, 214, 0.702) |
rgba(0, 0, 0, 0) |
| ″ | ProcessGlowInnerFade | rgba(24, 134, 101, 0) |
rgba(63, 111, 104, 0) |
rgba(92, 136, 214, 0) |
rgba(0, 0, 0, 0) |
--hm-sw-process-glow-face-rim |
ProcessGlowRim | #247a60 |
#3f6f68 |
#5c88d6 |
rgba(0, 0, 0, 0) |
| ″ | ProcessGlowFaceFade | rgba(13, 101, 74, 0.051) |
rgba(42, 48, 57, 0.051) |
rgba(217, 227, 239, 0.051) |
rgba(0, 0, 0, 0) |
--hm-sw-process-glow-face-sheen |
ProcessGlowSheenTop | #20322c |
#1e232b |
#ffffff |
rgba(0, 0, 0, 0) |
| ″ | ProcessGlowSheen | #3dc79d |
#74c4b4 |
#8fb0e6 |
rgba(0, 0, 0, 0) |
| ″ | ProcessGlowSheenFade | rgba(61, 199, 157, 0.051) |
rgba(116, 196, 180, 0.051) |
rgba(143, 176, 230, 0.051) |
rgba(0, 0, 0, 0) |
| ″ | ProcessGlowTint | rgba(0, 95, 66, 0.251) |
rgba(42, 48, 57, 0.251) |
rgba(92, 136, 214, 0.251) |
rgba(0, 0, 0, 0) |
--hm-sw-process-glow-halo (× 0.85) |
ProcessGlowHalo | #58b599 |
#74c4b4 |
#5c88d6 |
#000000 |
--hm-sw-process-glow-edge (× 0.65) |
ProcessGlowEdge | #58b599 |
#74c4b4 |
#5c88d6 |
rgba(0, 0, 0, 0) |
--hm-sw-process-idle-face |
ProcessIdleInset | rgba(0, 0, 0, 0.502) |
rgba(0, 0, 0, 0.502) |
rgba(0, 0, 0, 0.149) |
rgba(0, 0, 0, 0) |
| ″ | ProcessIdleInsetFade | rgba(0, 0, 0, 0) |
rgba(0, 0, 0, 0) |
rgba(0, 0, 0, 0) |
rgba(0, 0, 0, 0) |
| ″ | ProcessIdleShade | rgba(23, 42, 26, 0.251) |
rgba(0, 0, 0, 0.2) |
rgba(47, 74, 110, 0.078) |
rgba(0, 0, 0, 0) |
--hm-sw-process-idle-halo (× 0.8) |
ProcessIdleHalo | #11694f |
#3a4656 |
#8fb0e6 |
rgba(0, 0, 0, 0) |
--hm-sw-process-idle-rim |
ProcessIdleRim | rgba(4, 128, 90, 0.322) |
rgba(116, 196, 180, 0.302) |
rgba(255, 255, 255, 0.4) |
rgba(0, 0, 0, 0) |
--hm-sw-process-arrow-fill |
ProcessArrowFill | #27d9a5 |
#74c4b4 |
#5c88d6 |
rgba(0, 0, 0, 0) |
--hm-sw-process-arrow-dim-fill |
ProcessArrowDimFill | #abc7bf |
#c9d3de |
#8fa3b8 |
rgba(0, 0, 0, 0) |
--hm-sw-process-arrow-fill-fade |
ProcessArrowFillFade | rgba(16, 109, 81, 0) |
rgba(42, 48, 57, 0) |
rgba(217, 227, 239, 0) |
rgba(0, 0, 0, 0) |
--hm-sw-process-arrow-edge |
ProcessArrowEdge | #00432e |
#1a1e25 |
#afbecd |
#ffffff |
--hm-sw-process-arrow-mid |
ProcessArrowMid | #1c6a51 |
#4a5260 |
#5c88d6 |
#ffffff |
--hm-sw-process-arrow-light |
ProcessArrowLight | #3d9c7e |
#5e8f87 |
#8fb0e6 |
#ffffff |
--hm-sw-process-arrow-shadow |
ProcessArrowShadow | rgba(0, 0, 0, 0.502) |
rgba(0, 0, 0, 0.502) |
rgba(255, 255, 255, 0.502) |
rgba(0, 0, 0, 0) |
--hm-sw-process-shade · -shade-mid · -shade-fade |
ProcessShade | rgba(4, 34, 25, 0.878) |
rgba(11, 13, 17, 0.878) |
rgba(175, 190, 205, 0.878) |
rgba(0, 0, 0, 0) |
| ″ | ProcessShadeMid | rgba(4, 34, 25, 0.706) |
rgba(11, 13, 17, 0.706) |
rgba(175, 190, 205, 0.706) |
rgba(0, 0, 0, 0) |
| ″ | ProcessShadeFade | rgba(4, 34, 25, 0) |
rgba(11, 13, 17, 0) |
rgba(175, 190, 205, 0) |
rgba(0, 0, 0, 0) |
--hm-sw-process-tab-glow |
TabGlow | rgba(31, 148, 113, 0.702) |
rgba(74, 82, 96, 0.702) |
rgba(255, 255, 255, 0.702) |
rgba(0, 0, 0, 0) |
--hm-sw-process-tab-depth |
TabDepthFade | rgba(0, 61, 41, 0) |
rgba(26, 30, 37, 0) |
rgba(198, 212, 227, 0) |
rgba(0, 0, 0, 1) |
| ″ | TabDepth | rgba(0, 61, 41, 0.8) |
rgba(26, 30, 37, 0.8) |
rgba(198, 212, 227, 0.8) |
rgba(0, 0, 0, 1) |
--hm-sw-process-tab-shade |
TabShade | rgba(0, 0, 0, 0.4) |
rgba(0, 0, 0, 0.4) |
rgba(0, 0, 0, 0.149) |
rgba(0, 0, 0, 0) |
| ″ | TabShadeFade | rgba(0, 0, 0, 0) |
rgba(0, 0, 0, 0) |
rgba(0, 0, 0, 0) |
rgba(0, 0, 0, 0) |
--hm-sw-process-tab-sheen |
TabSheen | rgba(74, 255, 196, 0.502) |
rgba(116, 196, 180, 0.502) |
rgba(255, 255, 255, 0.502) |
rgba(0, 0, 0, 0) |
| ″ | TabSheenFade | rgba(74, 255, 196, 0) |
rgba(116, 196, 180, 0) |
rgba(255, 255, 255, 0) |
rgba(0, 0, 0, 0) |
--hm-sw-process-tab-border-gradient |
TabBorder | #001e15 |
#0b0d11 |
#9daec0 |
#ffffff |
| ″ | TabStrokeHighlight | #17a77b |
#4a5260 |
#ffffff |
#ffffff |
| ″ | TabStrokeLow | #0b533d |
#262c36 |
#9daec0 |
#ffffff |
--hm-sw-process-tab-ring |
TabRing | rgba(0, 0, 0, 0.251) |
rgba(0, 0, 0, 0.251) |
rgba(255, 255, 255, 0.251) |
rgba(0, 0, 0, 0) |
--hm-sw-process-tab-active-base |
TabActiveBase | rgba(0, 0, 0, 0.4) |
rgba(0, 0, 0, 0.4) |
rgba(255, 255, 255, 0.4) |
rgba(0, 0, 0, 1) |
| ″ | TabActiveBaseDeep | rgba(0, 0, 0, 0.451) |
rgba(0, 0, 0, 0.451) |
rgba(255, 255, 255, 0.451) |
rgba(0, 0, 0, 1) |
--hm-sw-process-tab-active-shade |
TabActiveShade | rgba(0, 0, 0, 0.6) |
rgba(0, 0, 0, 0.6) |
rgba(92, 136, 214, 0.2) |
rgba(0, 0, 0, 0) |
| ″ | TabShadeFade | rgba(0, 0, 0, 0) |
rgba(0, 0, 0, 0) |
rgba(0, 0, 0, 0) |
rgba(0, 0, 0, 0) |
--hm-sw-process-tab-active-sheen |
TabActiveSheenFade | rgba(122, 206, 179, 0) |
rgba(116, 196, 180, 0) |
rgba(92, 136, 214, 0) |
rgba(0, 0, 0, 0) |
| ″ | TabActiveSheen | rgba(122, 206, 179, 0.102) |
rgba(116, 196, 180, 0.102) |
rgba(92, 136, 214, 0.102) |
rgba(0, 0, 0, 0) |
--hm-sw-process-tab-inset (끝은 투명) |
TabInset | rgba(0, 0, 0, 0.659) |
rgba(11, 13, 17, 0.659) |
rgba(175, 190, 205, 0.659) |
rgba(0, 0, 0, 0) |
| ″ | TabInsetFade | rgba(0, 0, 0, 0) |
rgba(11, 13, 17, 0) |
rgba(175, 190, 205, 0) |
rgba(0, 0, 0, 0) |
--hm-sw-process-tab-disabled-shade |
TabShadeFade | rgba(0, 0, 0, 0) |
rgba(0, 0, 0, 0) |
rgba(0, 0, 0, 0) |
rgba(0, 0, 0, 0) |
| ″ | TabDisabledShade | rgba(0, 0, 0, 0.8) |
rgba(0, 0, 0, 0.8) |
rgba(0, 0, 0, 0.2) |
rgba(0, 0, 0, 0) |
--hm-sw-process-tab-disabled-sheen |
TabDisabledSheen | rgba(130, 181, 164, 0.502) |
rgba(128, 142, 156, 0.502) |
rgba(255, 255, 255, 0.502) |
rgba(0, 0, 0, 0) |
| ″ | TabActiveSheenFade | rgba(122, 206, 179, 0) |
rgba(116, 196, 180, 0) |
rgba(92, 136, 214, 0) |
rgba(0, 0, 0, 0) |
--hm-sw-process-tab-disabled-text |
TabDisabledText | #64a491 |
#6d7784 |
#8a97a6 |
#a0a0a0 |
--hm-sw-process-layer-border-gradient |
PropertyBorder | #08251c |
#12151a |
#afbecd |
#ffffff |
| ″ | ObjBorderDeep | #0e3d2f |
#1e232b |
#afbecd |
#ffffff |
| ″ | LayerFieldBorderHighlight | #1e7c60 |
#4a5260 |
#ffffff |
#ffffff |
| ″ | ObjBorderDark | #08241c |
#12151a |
#afbecd |
#ffffff |
| ″ | ToolBorderBottom | #051612 |
#0b0d11 |
#9daec0 |
#ffffff |
--hm-sw-process-pill-background |
PillFaceTop | rgba(0, 0, 0, 0.851) |
rgba(0, 0, 0, 0.851) |
rgba(224, 228, 232, 0.851) |
rgba(0, 0, 0, 1) |
| ″ | PillFace | rgba(0, 0, 0, 0.8) |
rgba(0, 0, 0, 0.8) |
rgba(246, 249, 252, 0.8) |
rgba(0, 0, 0, 1) |
--hm-sw-process-pill-border-gradient |
PillBorder | #092920 |
#1a1e25 |
#afbecd |
#ffffff |
| ″ | PillBorderBottom | #1c6f56 |
#313843 |
#c3cfdb |
#ffffff |
--hm-sw-process-tool-border-gradient |
PropertyBorder | #08251c |
#12151a |
#afbecd |
#ffffff |
| ″ | ToolBorderHighlight | #208769 |
#4a5260 |
#ffffff |
#ffffff |
| ″ | ObjBorderDark | #08241c |
#12151a |
#afbecd |
#ffffff |
| ″ | ToolBorderBottom | #051612 |
#0b0d11 |
#9daec0 |
#ffffff |
--hm-sw-process-tool-icon-color |
Icon | #b6d0c9 |
#c9d3de |
#26303c |
#ffffff |
--hm-sw-process-close-hover-background |
CloseHover | #e40000 |
#e40000 |
#e40000 |
#ff0000 |
--hm-sw-process-close-icon-hover |
CloseIconHover | #ffffff |
#ffffff |
#ffffff |
#ffffff |
--hm-sw-process-tool-open-background |
ToolOpenEdge | #081813 |
#0b0d11 |
#dce4ed |
#000000 |
| ″ | ToolOpenMid | #0b3227 |
#171b21 |
#e8eef5 |
#000000 |
| ″ | ToolOpenCenter | #0e3d2f |
#1e232b |
#f3f6fa |
#000000 |
--hm-sw-process-tool-open-edge |
ToolOpenEdge | #081813 |
#0b0d11 |
#dce4ed |
#000000 |
--hm-sw-process-split-panel-background |
SplitPanelFace | #082018 |
#14171c |
#ffffff |
#000000 |
--hm-sw-process-split-cell-pane |
SplitCellPane | #1b6b53 |
#3a4656 |
#e3ebf4 |
#000000 |
--hm-sw-process-split-icon-pane |
IconCell | #003425 |
#12151a |
#afbecd |
#000000 |
그라데이션 토큰은 여러 키의 조합입니다. 같은 위치(stop)에 테마 값을 넣어 토큰 전체를 지정합니다. 다음 키는 토큰이 아닙니다: HomeFaceBottom, HomeFaceDeep, HomeFaceMid, HomeFaceTop, HomeBorderTop, HomeBorderLight, HomeBorderDark, HomeBorderBottom, HomeShadow, IconOutline, WindowMoveIcon, LayerBulbBase, LayerBulbBar, LayerBulbOnLight, LayerBulbOnMid, LayerBulbOnDark, LayerBulbOffLight, LayerBulbOffMid, LayerBulbOffDark(SW 홈·전구·창 이동 그림 자산 — 테마 Override 대상 아님, Hanmac 그림), IconScreen, IconScreenDeep, IconMonitorScreen(Strana 도구 아이콘 전용 — 도구 아이콘은 소비 측이 그림).
8. 검증
2026-11-03 1차 구현(Chromium, Storybook 6024).
| 항목 | 결과 |
|---|---|
| 폭 측정(파일명 / 프로세스 / Layer 칸 / 창 영역) | 1920: 280.8 / 984.8 / 252.3 / 313 = Strana 별 비율 계산값. 1100: 129.1 / 416.9 / 152(최소) / 313. 1366: 178.3 / 625.4 / 160.3 / 313. 2560: 286(최대) / 1615 / 257(최대) / 313. 넘침 없음. 홈 80(SWHeader 기준, 2026-11-03) 이후 파일명 / 프로세스: 1100 133.7 / 437, 1366 182.9 / 641.4, 1920 285.4 / 1000.8, 2560 286 / 1639.7, 창 영역 312, 넘침 없음 |
| Strana 캡처 비교(1.5배, 나란히) | Hanmac 1920 Modeling · Results, 1366, 1100 — 배치·면·발광·화살표·탭·Layer·도구·창 버튼 일치. 차이: 글자 외곽선 두께, 파일명 자간, 좁은 폭의 말줄임(§5) |
| 동작 | 탭 클릭·Arrow·Home·End(비활성 건너뜀), 같은 탭 재클릭 시 Callback 없음, 전구·필드 분리, 최대화/복원 전환, 로고 열림 모양, 도구 눌림 모양, Hover(종료 #E40000, 최소화, Layer 링, 탭 글자), Focus outline |
기존 SWHeader |
변경 전후 빌드에서 SW/Header Overview · Responsive More 출력 HTML 동일, dist/styles.css의 기존 내용 동일(새 규칙만 추가) |
Strana 비교 캡처는 D:\Strana\_verification\header-rework-20261029\renders\Hanmac-{1100,1366,1920}-*.png(최종 Commit 7173e82 · b7f412c 이전 렌더)입니다. 분할 버튼 변경(b7f412c) 이후 최신 캡처와 다른 테마(Dark · Light · HighContrast)의 화면 비교는 미확인입니다. Firefox · Safari는 확인하지 않았습니다.
9. appearance="multi" — StrAna-SW 다중 Step (2026-11-03)
원본: Figma StrAna-SW 4y8lTK1E1hAGYiMcYZwuNe, 노드 873:16481 · 873:16254 · 873:17421(각 1920 × 49). Story: SW / Step Header › Step Header(Step Header 섹션 = default + Multi Step 섹션 = 세 시안, 노드 ID는 화면에 표시하지 않고 data-figma-node로만 유지), Responsive More, 검증용(숨김) Multi Step(세 시안 1920) · 화면 분할 · Fixed Widths · Process Bar · Many Groups · Many Steps · States · Theme Override.
9.1 시안 비교
세 시안은 같은 구조의 상태 차이입니다. 그래서 별도 컴포넌트 없이 SWProcessHeader / SWProcessBar의 appearance="multi" 하나로 구현했습니다.
| 시안 | 파일명 면 | 켜진 그룹 · 선택 단계 | 그룹 상태 |
|---|---|---|---|
| 873:16481 | 파일 없음: 프로젝트 생성 전 10px 700 #AEDBCC 오른쪽 위 |
01 데이터 구축 · 데이터입력 | 2·3 미진행(70%), 4·5 비활성(회색) |
| 873:16254 | 파일명 14px 700 #F0F7F5, 말줄임. 위치·여백은 SWHeader 우선(글자 x 70, padding 5 / 12, 자간 0.84px) |
01 데이터 구축 · 데이터입력 | 같음 |
| 873:17421 | 파일 없음 | 03 구조물 상세 및 계획 · 본체계획 | 1·2 진행 완료(덮개 + 밝은 탭), 4·5 비활성 |
9.2 default와 공유한 것 / 다른 것
- 사다리꼴 배치: 시작·끝 그룹에만(§4 확정 규칙). 873:17421처럼 가운데 그룹이 켜지면 도형 없이 밝은 바탕만 보입니다(2026-11-03 수정 — 이전 구현은 켜진 가운데 그룹에도 발광 면을 그렸음).
- 같은 구현(값 대조 일치): 프로세스 바탕(ProcessBase gradient · DocBorder · 위 모서리 6), 비활성 덮개(
#00443090%, 꼭지·홈 모양 포함), 활성 발광 면·비활성 바깥 면(같은 경로), 화살표(선·채움·그늘 같은 값, Dim 25% / 50%), 홈 도형, 파일명 면 gradient, 창 이동 아이콘·줄무늬 색, 캡션 버튼 글리프, 선택·키보드·Callback. - multi 전용 CSS: 49px 면, 파일명 최대 189·단색 테두리(로고·파일명 글자는 SWHeader 기준으로 default와 공통), SW 기본 pill(41 · 40 × 37), 창 이동 위치, 그룹 내용(제목 위 · 탭 25), 탭 4상태, 발광 면 위치(왼쪽 50 / 반전 48).
- 새로 만든 것: 진행 완료 상태(
data-progress="done", multi만),documentEmptyLabel,labelHidden,appearance. 컴포넌트 DOM은 하나이며 multi는 탭의 층 9개 대신 면 하나만 그립니다. - Figma 도구 아이콘 5개(help · view-3d · controls · menu-list · exit-arrow)는 Story fixture(
src/sw/assets/process-header/tool-*.svg, Figma 원본)에만 둡니다.
9.3 넘침 처리 — 확정 정책(2026-11-03 사용자 승인)
| 항목 | 결정 | 근거 |
|---|---|---|
| 그룹 폭 | 자연 폭(제목·탭 중 넓은 쪽 + 좌우 10)까지, 좁으면 자연 폭 비율로 함께 축소. 남는 폭은 양쪽에 같게 | default와 같은 규칙 |
| 탭 | 글자 폭 + 25, 좁으면 선택 탭 포함 글자 폭 비율로 축소 + …(2026-11-03 개정) |
옆 탭이 비정상적으로 좁아지지 않게 |
| 화살표 · 그룹 사이 36 | 고정 | Figma |
| 실제 넘침 | 그룹 접기·좌우 스크롤·More 메뉴를 두지 않음. 더 좁으면 영역 안에서 잘림 | 확정(Figma에 정의 없음) |
측정(1920): 5그룹 11탭은 말줄임 없음. 1366에서는 11탭 중 10개, 8그룹 · 16탭(1920)에서는 15개가 말줄임됩니다. 겹침 · 영역 밖 탭 0. (개정 후 1366에서는 선택 탭도 다른 탭과 함께 줄어 …가 될 수 있습니다.)
9.4 Figma와 남은 차이
- 873:17421의 켜진 그룹 미선택 탭은 70% ·
#0C3125·rgba(76,167,138,.8), 873:16481은 100% ·#0A5940·rgba(68,178,143,.8)로 시안끼리 다릅니다. 873:16481 값으로 통일했습니다. - 873:17421의 선택 탭에만 있는 안쪽 2px 링(
rgba(0,0,0,.25))은 넣지 않았습니다(873:16481 기준). - 비활성 글자는
#87AD9D(1개 탭) /#7D9F91(3개 탭) 두 값 중#7D9F91로 통일했습니다. - 873:17421의
선형입력위치(599)는 873:16481(592.8)과 다릅니다. 구현은 그룹 간격 36 규칙(592)입니다. - Figma 로고 자리는 글자
LOGO이며, Story는 임시 로고 이미지입니다(로고·파일명 글자는 SWHeader와 같은 크기·위치). 창 이동 아이콘 밝기 차이: SWHeader 창 이동 자산으로 바꾼 뒤 Figma와 같은 밝기(2026-11-03 해소).- 글자 외곽선: Figma multi 시안의 글자에는 Stroke가 없으나 SW 규칙에 맞춰 외곽선을 적용했습니다(의도된 차이, §10).
- 장치 배율 1.5에서 1px 테두리 반올림으로 창 영역이 366.3px로 측정됩니다(Figma 367).
10. SWHeader 스타일 정합 (2026-11-03)
SWHeader의 실제 구현(sw.css)과 브라우저 렌더를 기준으로 맞췄습니다. SWHeader·SWViewerHeader·sw.css는 바꾸지 않았고, 모두 default·multi 공통입니다.
| 항목 | SWHeader | Process Header(적용 결과) | 구현 |
|---|---|---|---|
| 글자 Stroke | 14·16px 검정 1.3px, 12px 1px(16방향 text-shadow), 말줄임 글자는 overflow-clip-margin 2px |
같음: 파일명·탭(선택·비선택·비활성)·Layer 값 1.3px, 그룹 제목·번호·Layer 라벨·파일 없음 표시 1px | SW 토큰 재사용. multi의 text-shadow: none 제거, 탭·제목 말줄임 글자는 clip margin 공통 |
| 창 이동 | 88 × 48, 줄무늬, 아이콘 → Hover 시 창·커서 1.4s 반복 + 툴팁, Drag 중 정지, 해제 즉시 복원, Reduced Motion 정적 | 같음(SWHeader 부품 그대로). multi만 Figma 위치로 줄무늬 82 × 39(3, 5)·아이콘 3px 위 |
SWHeaderWindowControls(창 이동만) 재사용, 정적 그림·CSS·토큰 2개 삭제 |
| 도구 버튼 Hover | 노란 gradient 링 + 노란 아이콘(hoverIcon 또는 색) |
같음. Default fixture 아이콘은 currentColor, Multi Figma 아이콘은 #B6D0C9 → #FFC600 Hover 그림(SW 인쇄 버튼 방식) |
SWToolButton hoverIcon, 눌림(pressed)도 링 + 노란 아이콘 |
| 로고 · 파일명 · 탭 Hover | 로고 금색 테두리, 파일명 글자 #FFC600 |
같음, 탭 글자 #FFC600, 비활성 탭은 변화 없음 |
재사용 |
| Layer 필드 Hover | 필드 노란 링 + 글자 #FFC600, 전구는 어두운 배경 |
같음 | 재사용 |
| 최소화 · 최대화 Hover | 배경 rgba(0,0,0,.2) + 흰 글리프(노랑 아님) |
같음 | SW 토큰 |
| 종료 Hover | #E40000 + 흰 X |
같음(유지) | 확정 디자인 |
| Focus | 2px #F0F7F5, -2px |
같음(탭·도구·캡션 버튼) | SW 공통 규칙 |
| 선택 · Disabled | — | 선택 탭 #FFC600(Hover와 같은 색이지만 면이 다름), Disabled는 Hover·Focus 없음 |
기존 확정 디자인 |
11. 선택 Step 표시 통일 · 아이콘 · 화면 분할 (2026-11-03)
11.1 선택 Step 폭(두 appearance 공통, 2026-11-03 개정)
- 개정: 선택된 탭도 다른 탭과 같은 비율로 줄어듭니다(이전 "선택 탭 전체 이름 유지" 폐기 — 선택 탭에 폭을 먼저 주느라 옆 탭이 24 · 27px까지 좁아지던 문제). 공간이 충분하면 전체 이름, 부족하면 모든 탭이 기본 폭 비율로 줄고
…입니다(default같은 폭,multi글자 폭 비례). 선택이 바뀌어도 탭 폭은 그대로입니다. - 그룹 최소 폭 = 좌우 여백 + (
default만) 이름 + 모든 탭 여백 + 간격(측정). 그 위에서는 그룹이 비례로 줄고, 아래로는 줄지 않아 겹치지 않습니다. - 탭 배치는 두 appearance 모두 flex 한 줄입니다(이전
default의 grid 대체). 넓을 때default탭은 127 균등 그대로입니다. - 측정(개정 후, 고정 폭,
*= 선택):default1100*61 61 | 45(이전*98 25 | 45), 1366*124 124 | 124, 1920 · 2560*127 127 | 127(변화 없음).multi1100 셋째 그룹*34 34 37 37(이전*77 25 25 25), 1366*54 54 61 61(이전*77 47 53 53), 1920 · 2560*77 77 89 89(변화 없음). 겹침 0. 그룹 최소 폭의 합이 프로세스 영역보다 크면 영역 끝에서 잘립니다.
11.2 도구 아이콘이 사라지던 문제
- 원인:
sw.css의:is(.hm-sw-header__action, …):hover:not(:disabled) > .hm-sw-header__icon { visibility: hidden }는 Hover 그림(hoverIcon)이 있다고 가정합니다.hoverIcon없이currentColor아이콘만 준SWToolButton은 Hover 순간 아이콘이 비었습니다(Default 도구 3개). - 해결(2026-11-03 공통 수정):
SWToolButton(pill)이hm-sw-header__iconclass를hoverIcon이 있을 때만 기본 아이콘에 붙입니다.hoverIcon이 없으면 기본 아이콘이 Hover에도 그대로 보이고 색만#FFC600입니다.SWHeader의tools도 같은 컴포넌트라 함께 해결되었고, 이 Header 범위의 보완 CSS 규칙은 삭제했습니다(sw.css변경 없음). 눌림은 Hover 그림이 있을 때만 그 그림으로 바뀝니다. Disabled·열림에서도 아이콘이 유지되며 크기 24 × 24로 같습니다.
11.3 화면 분할(SWProcessSplitButton)
원본: Strana ViewportSplitSelector.xaml, Styles/Controls/ViewportSplitStyles.xaml, HeaderStyles.xaml(ToolMenu · ToolOpenTail · ToolMenuStep · Icon.ToolLayout), MainWorkspaceWindowView 분할 Popup(HEAD edda3cc).
| 항목 | Strana | 구현 |
|---|---|---|
| 버튼 | 도구 버튼 40 × 38, 아이콘 = 현재 분할 모양 | SWToolButton pill + 분할 아이콘(Strana Geometry, 틀은 currentColor) |
| 열림 | 면 ToolOpenFace, 금색 위·양옆, 아래 열림, Hover 링 없음, 아이콘 노랑 | 같음 |
| 연장 | 버튼 아래 3px(버튼 아래 1px 겹침) | 버튼 아래 1px 겹침, 길이는 Header 끝까지(최소 3px) — 패널이 창 버튼을 가리지 않게 |
| 패널 | 233 × 36, 면 #082018, 금색 양옆·아래, radius 0 2 2 2, 그림자 0 2 4 60% |
같음, Header 바로 아래·버튼 왼쪽, 화면 밖이면 좌우만 보정(SW 필드 Dropdown 위치 훅) |
| 위 선 | 버튼 오른쪽 끝 ~ 패널 끝 금색 | 같음 |
| 칸 | 38 × 34 × 6(Type01~06), Hover·선택 = 창 노랑, Disabled 40% | 38.5 × 34(233 / 6), 같은 그림·상태. 틀 상자는 32 × 22 정수(Strana 31.6 × 21.73, 그림 29.6 × 19.73 그대로) |
| 동작 | Popup StaysOpen=False(바깥 클릭 닫힘), 칸 선택 시 열린 채 | 같음 + Escape 닫고 버튼으로, Tab 이탈 닫힘, ArrowDown 열기, 방향키 선택(radiogroup) |
값은 Props(value, open), 동작은 Callback(onValueChange, onOpenChange)이며 창을 실제로 나누는 일은 호스트가 합니다. tools 안에 두므로 버튼 순서는 호스트가 정하고 default·multi가 같은 컴포넌트를 씁니다. Figma 다중 Step 시안 3개에는 화면 분할 버튼이 없어 해당 Story에는 넣지 않았습니다(Step Header 페이지의 Step Header 섹션과 검증용 화면 분할 Story에서 Default·Multi Step을 확인).
- Popup 하단 잘림(2026-11-03, 수정): 재현 조건 = Popup 아래 공간이 부족한 화면(예: Storybook Canvas 높이 400px의 Multi Step). 공용 위치 훅
useSWFixedDropdownPosition이 아래 공간이 없으면 필드 위로 뒤집는데(flipped), 이 선택기는 꼬리·패널을 항상 아래쪽 모양으로 그려 Header 위에 겹치고 패널 하단이 Header에 가려졌습니다(아이콘 SVG·viewBox·overflow·세로 정렬은 정상). 수정:SWProcessSplitButton이 뒤집힘을 쓰지 않고 항상 버튼 아래에 붙습니다(좌우 보정은 훅 그대로). 아래 공간이 부족하면 화면 끝에서 잘리지만 페이지 스크롤에 따라 위치가 갱신되어 스크롤하면 전체가 보입니다. Default·Multi 공통(같은 컴포넌트), CSS 변경 없음. - 아이콘 하단 잘림(2026-11-03, 수정): 사용자 Chrome(배율 100%, 화면 1920 × 1080, Storybook 미리보기 1620 × 871)에서 재현. Responsive 1366 · 1100 행의 Popup 첫 열림에서 6개 틀이 21줄로 그려져 판 아래 밝은 테두리가 사라짐(아래가 잘린 것처럼 보임). 원인: 틀 상자 31.6 × 21.73(소수)이 화면 픽셀로 맞춰질 때 위치에 따라 21줄 또는 22줄이 되고, SVG 판은 크기가 고정이라 21줄일 때 아래 밝은 줄이 덮임. 패널 하단 테두리(
#926E03)는 수정 전에도 그려져 있었음. 수정: 틀 상자를 32 × 22 정수로(그림 SVG·패널·위치 변경 없음). 수정 후 같은 Chrome에서 1366 · 1100 행, Default · Multi Step, 1920 고정 폭 Header 모두 틀 22줄 · 아래 밝은 줄 표시 확인.