Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a09386b11 | ||
|
|
2690a160e9 | ||
|
|
d159ad58af | ||
|
|
b6b7c656e3 | ||
|
|
6eae67628c | ||
|
|
4fc37c7dd3 |
No files matched your search
@@ -90,7 +90,7 @@ Collapsible의 trigger/content는 `asChild`로 자체 `button`/`div`를 써서 C
|
||||
| 연결선 | 3뎁스만, 1px, 세로선 x 68, 가로 12, 글자까지 8, 행 세로 중앙, 마지막 행 중앙에서 끝 | 행마다 background 2겹(세로 `1px × 100%`, 마지막 행 `1px × 50%`, 가로 `12 × 1` at `calc(50% - 0.5px)`). DOM·tab stop·aria 없음 | MATCHED / IMPLEMENTATION_CHANGE(명세의 "배경 레이어" 방식) |
|
||||
| 연결선 색 | 기본·hover `--hm-green-tint` 40%, 선택 `--hm-green-light-2` 40% | 같음. 선택 색은 선택된 행의 선에만 적용(나머지 행은 기본색) | MATCHED |
|
||||
| 접힘 | 하위 메뉴·연결선 함께 숨김 | Radix가 내용을 unmount → 선도 같이 사라짐 | MATCHED |
|
||||
| 중첩 그룹 | 배경·테두리·padding 중복, 폭 감소, 오른쪽 틈 금지 | 안쪽 그룹은 면·선·padding-bottom 없음, 폭 100%(바깥 그룹과 같은 252), 선택 항목 오른쪽 273(1px 겹침) | MATCHED |
|
||||
| 중첩 그룹 | 배경·테두리·padding 중복, 폭 감소, 오른쪽 틈 금지 | 안쪽 그룹은 면·선·padding-bottom 없음, 폭 100%(바깥 그룹과 같은 220), 선택 항목 오른쪽 241(1px 겹침). 2026-11-10 240px 열 실측(Third Level, 1440); 이전 252/273은 272px 열 기준 | MATCHED |
|
||||
| 안쪽 그룹 이름 위치 | 명세 수치 없음, 이미지상 2뎁스 글자와 같은 시작선 | trigger padding-left = 68 − 화살표 18 − gap 12 = 38 → 글자 x 68 | INTENTIONAL_CHANGE(이미지 기준) |
|
||||
|
||||
모바일(<1024): LNB ONLY는 Desktop 열을 숨기고 같은 메뉴를 독립 `LNBMobileHeader / LNBMobileMenu`로 표시한다(§3b). GNB ONLY 내부 Sidebar는 [GNB §15](gnb.md#15-gnb-only-sidebar-composition-2026-10-08)의 GNB 단일 Sheet를 사용하며 독립 LNB Mobile Header/Menu를 추가하지 않는다. DASHBOARD ONLY 운영 화면에는 Sidebar가 없다([Dashboard 현재 계약](dashboard-layout.md#1-현재-계약)).
|
||||
|
||||
@@ -90,9 +90,17 @@ Strana의 2그룹(01 Modeling: Structure · Loads / 02 Analysis Result: Results)
|
||||
|
||||
전체 목록과 Strana 원본 키는 §7 표의 왼쪽 두 열을 봅니다.
|
||||
|
||||
**Step Header 전용 alias (2026-11-10).** Step Header 규칙이 칠하는 SWHeader 토큰 18개는 같은 이름의 `--hm-sw-process-*`(가장 바깥 SW 범위, 기본값 `var(--hm-sw-header-…)`)를 거쳐 읽습니다: `background`, `height`, `text-color`, `selected-color`, `hover-border-gradient`, `hover-border-width`, `document-background`, `document-border-gradient`, `document-border-color`, `object-background`, `object-border-gradient`, `object-radius`, `property-background`, `property-radius`, `property-shadow`, `window-icon-color`, `window-icon-hover-color`, `window-button-hover-background`. `--hm-sw-process-base-border-gradient`는 `--hm-sw-process-document-border-gradient`를 거쳐 같은 기본값을 씁니다.
|
||||
|
||||
- 기본(상속): `--hm-sw-process-*`가 없으면 가장 바깥 범위의 SWHeader 토큰 값이 그대로 적용됩니다. 계산값은 변경 전과 같습니다.
|
||||
- Step Header만 변경: 가장 바깥 SW 범위에 `--hm-sw-process-*`를 지정합니다(SWHeader 토큰보다 우선, SWHeader·Viewer 영향 없음).
|
||||
- 제약: alias는 가장 바깥 범위에서 계산되므로 안쪽 요소에서 바꾼 `--hm-sw-header-*`는 이 18개에 전달되지 않습니다.
|
||||
- 공유 유지: 바 높이·필드·로고 높이, 글자 크기, 글자 외곽선, 재사용 부품(홈·로고, 파일명 버튼, 창 이동, 도구 pill)의 `sw.css` 규칙.
|
||||
- `--hm-sw-process-text-color`는 Step Header 자체 글자 규칙(글자 로고, 레이어 필드)에만 적용됩니다. 루트의 기본 글자색은 SW 공통 규칙(`sw.css` `.hm-sw-header, .hm-sw-theme`)이 `--hm-sw-header-text-color`로 정하므로, 이를 상속하는 영역과 재사용 부품은 Header 토큰을 따릅니다.
|
||||
|
||||
## 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`)에서 생성했습니다.
|
||||
Hanmac 열이 기본값입니다. 다른 테마는 Header의 `style`(가장 바깥 SW 범위)에 같은 토큰을 지정합니다. 예: `style={{ "--hm-sw-header-selected-color": "#FFC833" }}`. Strana ARGB `#AARRGGBB`는 CSS `rgba()`로 바꿉니다. 아래 표는 Strana `Styles/Theme/*/Colors.xaml`(HEAD `edda3cc`)에서 생성했습니다. 표의 `--hm-sw-header-*` 중 §6의 18개는 Step Header만 바꿀 때 같은 이름의 `--hm-sw-process-*`(예: `--hm-sw-process-selected-color`)에 지정해도 됩니다(2026-11-10).
|
||||
|
||||
<!-- theme-table:start -->
|
||||
| 토큰 | Strana `Color.Header.*` | Hanmac(기본값) | Dark | Light | HighContrast |
|
||||
|
||||
@@ -121,7 +121,7 @@ import "@hm-design/sw/styles.css" // 앱 진입점에서 한 번
|
||||
|
||||
### 3.1 테마(토큰) 설정 — 공식 방법 (2026-10-20)
|
||||
|
||||
SW 토큰(`--hm-sw-header-*`, `--hm-sw-property-*`, `--hm-sw-popup-*`, `--hm-sw-viewer-*`, `--hm-sw-footer-*`)의 기본값은 **가장 바깥 SW 범위**(`<SWTheme>`, 또는 단독으로 쓴 `SWHeader`·`SWViewerHeader`·`SWViewerPanel`·`SWViewerMenu`·`SWFooter`의 루트)에서 한 번만 선언됩니다. 그 안의 SW 요소 — Header의 Dropdown·More, `SWViewerMenu`, `SWViewerPanel`과 안쪽 컨트롤, `SWFooterCommandLine`, Tooltip·안내 — 는 자기 루트에 `hm-sw-theme`이 있어도 값을 다시 정하지 않고 **상속**합니다.
|
||||
SW 토큰(`--hm-sw-header-*`, `--hm-sw-property-*`, `--hm-sw-popup-*`, `--hm-sw-viewer-*`, `--hm-sw-footer-*`, `--hm-sw-process-*`, `--hm-sw-switch-*`, `--hm-sw-slider-*`, `--hm-sw-select-*`)의 기본값은 **가장 바깥 SW 범위**(`<SWTheme>`, 또는 단독으로 쓴 `SWHeader`·`SWProcessHeader`·`SWViewerHeader`·`SWViewerPanel`·`SWViewerMenu`·`SWFooter`의 루트)에서 한 번만 선언됩니다. 예외: 홈 메뉴 토큰(`--hm-sw-home-menu-*`, `--hm-sw-recent-files-*`)은 `SWHomeMenu` 루트(`.hm-sw-home-menu`)에 선언되어 `<SWTheme style>`로는 바뀌지 않습니다. `SWHomeMenu`의 `style`(SWHeader에서는 `homeMenu.style`)로 정합니다([DESIGN_RULES.md](./DESIGN_RULES.md) §12). 그 안의 SW 요소 — Header의 Dropdown·More, `SWViewerMenu`, `SWViewerPanel`과 안쪽 컨트롤, `SWFooterCommandLine`, Tooltip·안내 — 는 자기 루트에 `hm-sw-theme`이 있어도 값을 다시 정하지 않고 **상속**합니다.
|
||||
|
||||
1. 화면 전체(Header·Panel·Menu가 같은 값)를 바꾸려면 화면을 `<SWTheme style={theme}>` 하나로 감싸고 거기서만 토큰을 정합니다. 프로그램 class(`<SWTheme className="app-sw-theme">` + `.app-sw-theme { --hm-sw-header-font-family: … }`)도 됩니다.
|
||||
```tsx
|
||||
@@ -136,6 +136,7 @@ SW 토큰(`--hm-sw-header-*`, `--hm-sw-property-*`, `--hm-sw-popup-*`, `--hm-sw-
|
||||
4. 영역마다 다른 값이 필요하면 영역별 `<SWTheme>`을 둡니다. 옆 영역끼리 섞이지 않고, 안쪽 `<SWTheme style>`은 그 안에서만 적용됩니다.
|
||||
5. **하지 않음:** `hm-sw-*` class를 선택자로 쓴 CSS(`.hm-sw-header, .hm-sw-theme { --… }` 등), `!important`, 안쪽 컴포넌트마다 같은 값을 반복 지정, SW 범위가 아닌 조상(`body`, 앱 루트 `div`)에만 토큰 지정(SW 범위가 기본값을 다시 선언하므로 적용되지 않음).
|
||||
- 다른 토큰을 참조하는 토큰(예: `--hm-sw-viewer-panel-background` = `var(--hm-sw-property-surface)`)은 가장 바깥 범위에서 계산됩니다. 참조되는 토큰을 바꿀 때도 가장 바깥 범위에서 바꿉니다.
|
||||
- Step Header(`SWProcessHeader`, 2026-11-10): 칠하는 SWHeader 토큰 18개를 `--hm-sw-process-*` alias(기본값 `var(--hm-sw-header-…)`)로 읽습니다. 아무것도 정하지 않으면 SWHeader 테마를 따르고, `--hm-sw-process-selected-color` 등을 가장 바깥 범위에 정하면 Step Header만 바뀝니다. 위 규칙대로 안쪽 요소에서 바꾼 `--hm-sw-header-*`는 이 alias에 전달되지 않습니다. 목록·공유 유지 토큰은 [DESIGN_RULES.md](./DESIGN_RULES.md) §14.
|
||||
|
||||
## 4. 판단 기록 남기기
|
||||
|
||||
|
||||
@@ -193,6 +193,7 @@
|
||||
|
||||
- Home On(Figma 662:67267, 2026-10-19 사용자 확정): 메뉴가 열려 있는 동안 SWHeader가 자동으로 로고에 노란 Hover 테두리를 유지하고, Header 전폭 상·하단에 `#5F4B0F` 2px 선(`--hm-sw-header-home-on-line-color` / `-width`)을 그립니다. 닫으면 기본 상태로 돌아갑니다. 크기·위치 변화가 없고 SW 일반 모드 전용입니다(뷰어 모드에는 없음). 소비 코드에서 별도 class나 상태를 추가하지 않습니다.
|
||||
- 새 색·크기는 위 토큰만 `style`로 덮어씁니다. `hm-sw-home-menu*` class를 소비 코드에 쓰지 않습니다.
|
||||
- 덮어쓰는 위치(2026-11-10 구현 확인): 홈 메뉴 토큰 39개(`--hm-sw-home-menu-*` 28, `--hm-sw-recent-files-*` 11)는 가장 바깥 SW 범위가 아니라 `SWHomeMenu` 루트(`.hm-sw-home-menu`, `sw-home-menu.css`)에 선언됩니다. 루트가 값을 다시 정하므로 `<SWTheme style>`이나 `SWHeader`의 `style`로 지정한 `--hm-sw-home-menu-*`·`--hm-sw-recent-files-*`는 적용되지 않습니다. `SWHomeMenu`의 `style`, 또는 `SWHeader`에서는 `homeMenu.style`(그대로 `SWHomeMenu`에 전달)로 지정합니다. 단, 홈 메뉴 토큰 중 `--hm-sw-header-layer-*`·`--hm-sw-header-text-color`를 참조하는 값(더보기 Dropdown·최근파일 면·행 Hover 등)은 홈 메뉴 요소에서 계산되므로 바깥 SW 범위에서 그 Header 토큰을 바꾸면 따라 바뀝니다. 선언 위치는 현재 구조를 유지합니다.
|
||||
|
||||
## 13. Footer (`SWFooter*`, Figma 26:10097) — SW 일반 모드 전용
|
||||
|
||||
@@ -204,7 +205,7 @@
|
||||
| 배치(1920, Figma) | 보조기능 버튼 0~74 → 2 → 보조기능 격자 76~392(위 2) → 4 → 명령 그룹 396~(위 1) → 명령어 전체보기(명령 칸 바로 뒤, 1552~1626) → 나머지 bg_vacant | — |
|
||||
| 측면 버튼 | 74 × 71, 자산 면(그라데이션 + 그라데이션 테두리), 그림자 8px 60% 검정(왼쪽 버튼 → 오른쪽 5px, 오른쪽 버튼 → 왼쪽 5px). 제목 14px 1.3px 외곽선, 부제 14px 1px 외곽선, 위 18 · 줄 간격 2. Hover·Pressed·Expanded = 제목·부제 `#FF5C00` + 1px 주황 그라데이션 테두리(`--hm-sw-footer-gra-border-orange`), Disabled 기본 모양 | `--hm-sw-footer-side-*` |
|
||||
| 보조기능 | 104 × 33, gap 2, radius 3, 면 `linear-gradient(0deg, #262218 8%, #4A3C2B 34%, #594C31 55%, #312A1B 90%)`, 아이콘 중심 (19.5, 16.5), 라벨 13px 왼쪽 36 폭 57 가운데. 테두리 1px 그라데이션(위 → 아래): Default `#886D35 0%, #423625 8.85%, #F3DBA8 26.17%, #0E0B06 83.79%, #574B30 100%`(Figma btn_* 26:9248 …) | `--hm-sw-footer-aux-*`, `--hm-sw-footer-aux-border` |
|
||||
| 보조기능 상태 | Hover(선택 아님) = 테두리만 노랑 `--hm-sw-header-hover-border-gradient`(`#A67D03 → #F2CC0F → #FADD4F → #F2CC0F → #926E03`, SW Header Figma Hover 재사용 — 버튼 세트에는 Hover 없음, 사용자 확정 2026-10-20). Selected(`aria-pressed="true"`, Figma Variant2 = Variant3) = 테두리 `#DA6813 8.52%, #FFBB89 37.93%, #E5690D 71.85%, #AE3607 92.77%`(Figma `gra-border-orange` = `--hm-sw-footer-gra-border-orange`) + 아이콘 채움·글자 `#FF5C00`, Hover보다 우선. 아이콘 외곽선 검정 90% 1px는 모든 상태 동일 | `--hm-sw-footer-aux-hover-border`, `--hm-sw-footer-aux-pressed-border`, `--hm-sw-footer-aux-icon-color`(`#F1EDE5`) |
|
||||
| 보조기능 상태 | Hover(선택 아님) = 테두리만 노랑 `--hm-sw-header-hover-border-gradient`(`#A67D03 → #F2CC0F → #FADD4F → #F2CC0F → #926E03`, SW Header Figma Hover 재사용 — 버튼 세트에는 Hover 없음, 사용자 확정 2026-10-20). Selected(`aria-pressed="true"`, Figma Variant2 = Variant3) = 테두리 `#DA6813 8.52%, #FFBB89 37.93%, #E5690D 71.85%, #AE3607 92.77%`(Figma `gra-border-orange` = `--hm-sw-footer-gra-border-orange`) + 아이콘 채움·글자 `#FF5C00`, Hover보다 우선. 선택 면 = `--hm-sw-footer-aux-pressed-background`(`linear-gradient(0deg, #594931 0%, #3B3021 23%, #1B170F 58%, #0D0B07 100%)`, Figma 없음 — 실제 프로그램 화면 샘플링 근사값, 사용자 확정 2026-11-09). 아이콘 외곽선 검정 90% 1px는 모든 상태 동일 | `--hm-sw-footer-aux-hover-border`, `--hm-sw-footer-aux-pressed-border`, `--hm-sw-footer-aux-pressed-background`, `--hm-sw-footer-aux-icon-color`(`#F1EDE5`) |
|
||||
| 명령 그룹 면 | 테두리 위·왼쪽·아래 1px `#886D35`, 왼쪽 위 radius 4, 면 `linear-gradient(0deg, #2F281A 0%, #312A1B 5%, #332B1C 36%, #463C29 58%, #38301F 79%, #19160E 98%)`. 마지막 명령 뒤(빈 칸·나머지)는 bg_vacant `linear-gradient(0deg, #18150E 28%, #262015 41%, #332B1C 58%, #38301F 81%, #18150E 100%)`, 명령 칸 높이까지만(2026-10-20: 분류 행 아래로 내려가지 않아 선택 탭 Pocket 면이 어느 탭에서나 같음) | `--hm-sw-footer-group-*`, `--hm-sw-footer-vacant-background` |
|
||||
| 명령 칸 | 68 × 47, 라벨 14px `#F6F4F2` 위 6(좌우 2, 넘치면 `…` 말줄임 — 칸·글자 크기 그대로, 2026-10-20), 아이콘 18 × 18 위 26 가운데. 칸 사이 구분선 1 × 37(위 5) `linear-gradient(0deg, #20190E, #8A7A5B 45%, #F3DBA8 70%, #423625)` + `-1px 0 1px #000` | `--hm-sw-footer-slot-*`, `--hm-sw-footer-divider*` |
|
||||
| 명령 상태 | Hover = 선택(Figma Variant2 = Variant3): 글자 `#FF5C00` + 주황 아이콘. 색·아이콘만 바뀌고 아이콘·라벨 위치·크기·간격과 칸 크기는 그대로. Disabled 40%(Figma 없음) | `--hm-sw-footer-accent` |
|
||||
@@ -226,6 +227,13 @@
|
||||
|
||||
원본 `src/theme/sw-process-header.css`(2026-11-03). Strana Main Header(Hanmac 테마)의 값입니다. 새 토큰은 `--hm-sw-process-*`(`.hm-sw-header`, `.hm-sw-theme` 범위)이고, 값이 같은 곳은 SW Header 토큰을 그대로 읽습니다(파일명 면·테두리, 객체 면·테두리, 속성 필드 면·그림자, Hover gradient, 선택 `#FFC600`, 제목·글자, 창 이동 줄무늬, 창 버튼 글리프 `#ADB0AF`·Hover 배경). Light/Dark preset은 없으며 다른 Strana 테마는 가장 바깥 범위 `style`에 같은 토큰을 지정합니다([AI_USAGE.md](./AI_USAGE.md) §3.1).
|
||||
|
||||
Step Header 전용 토큰(2026-11-10): Step Header 규칙이 칠하는 SWHeader 토큰 18개는 같은 이름의 `--hm-sw-process-*`를 거쳐 읽습니다 — `background`·`height`·`text-color`·`selected-color`·`hover-border-gradient`·`hover-border-width`·`document-background`·`document-border-gradient`·`document-border-color`·`object-background`·`object-border-gradient`·`object-radius`·`property-background`·`property-radius`·`property-shadow`·`window-icon-color`·`window-icon-hover-color`·`window-button-hover-background`(예: `--hm-sw-process-selected-color`). 기본값은 각각 `var(--hm-sw-header-…)`라 계산값은 그대로입니다. `--hm-sw-process-base-border-gradient`도 `--hm-sw-process-document-border-gradient`를 거쳐 같은 기본값을 씁니다.
|
||||
- 기본 동작(상속): `--hm-sw-process-*`를 정하지 않으면 가장 바깥 SW 범위의 `--hm-sw-header-*` 값이 Step Header에도 적용됩니다.
|
||||
- Step Header만 바꾸기: 가장 바깥 SW 범위(`<SWTheme style>` 또는 단독 `SWProcessHeader`의 `style`)에 `--hm-sw-process-*`를 지정합니다. SWHeader·Viewer에는 영향이 없습니다.
|
||||
- 제약: alias는 가장 바깥 범위에서 계산되므로 안쪽 요소(예: `<SWTheme>` 안 `SWProcessHeader`의 `style`)에서 `--hm-sw-header-*`만 바꾸면 이 18개에는 전달되지 않습니다. 이 경우 같은 위치에 `--hm-sw-process-*`를 지정하거나 바깥 범위에서 바꿉니다.
|
||||
- 공유 유지: 바 높이(`--hm-sw-header-bar-height`)·필드·로고 높이, 글자 크기, 글자 외곽선(`text-outline`·`label-outline`·`text-outline-room`)과 재사용 부품(홈·로고, 파일명 버튼, 창 이동, 도구 pill)의 `sw.css` 규칙은 SWHeader 토큰을 그대로 읽습니다.
|
||||
- `--hm-sw-process-text-color` 범위: Step Header 자체 글자 규칙(글자 로고, 레이어 필드)에만 적용됩니다. Step Header 루트의 기본 글자색은 SW 공통 규칙(`sw.css`의 `.hm-sw-header, .hm-sw-theme { color: var(--hm-sw-header-text-color) }`)이 정하므로, 그 색을 상속하는 영역과 재사용 부품의 글자는 `--hm-sw-header-text-color`를 따릅니다.
|
||||
|
||||
| 항목 | 값 | 토큰 |
|
||||
|---|---|---|
|
||||
| 배치 | 홈·파일명 글자 = SWHeader 그대로(두 타입 공통, 2026-11-03): 홈 12 + 로고(32 × 17 → 80) + 36, 파일명은 홈과 15 겹친 뒤 2 간격(x 65), padding 5 / 12, 14px 700·자간 0.06em·SW 외곽선, 화살표 없음(글자 로고 18px 700, 최소 58px 칸 → 홈 최소 106, 더 긴 글자는 글자 폭만큼 넓어짐) → 프로세스 → Layer 면(객체 면, 양옆 1) → 창 영역(파일명 면 + 테두리 1px) | SW 토큰 |
|
||||
|
||||
@@ -109,11 +109,16 @@ export function useSWHeaderDropdown({ open: openProp, onOpenChange, disabled = f
|
||||
/** Focusable entries of the panel in DOM order (rows, inputs, footer). */
|
||||
const entries = () => Array.from(panelRef.current?.querySelectorAll<HTMLElement>("button:not(:disabled), input:not(:disabled)") ?? [])
|
||||
|
||||
// ArrowDown on the closed trigger asks for the entry focus. A fixed overlay (property window, SW popup) stays
|
||||
// visibility: hidden until its position is measured, and focus() on it is a no-op: the request stays pending and is
|
||||
// retried on the render that shows the list (the position update), then cleared once focus moved; closing drops it.
|
||||
useEffect(() => {
|
||||
if (open && focusEntryRef.current) {
|
||||
focusEntryRef.current = false
|
||||
if (!open) { focusEntryRef.current = false; return }
|
||||
if (focusEntryRef.current) {
|
||||
const current = panelRef.current?.querySelector<HTMLElement>('[aria-current="true"]:not(:disabled), [aria-pressed="true"]:not(:disabled)')
|
||||
;(current ?? entries()[0])?.focus()
|
||||
const target = current ?? entries()[0]
|
||||
target?.focus()
|
||||
if (!target || document.activeElement === target) focusEntryRef.current = false
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
--hm-sw-footer-aux-height: 33px;
|
||||
--hm-sw-footer-aux-gap: 2px;
|
||||
--hm-sw-footer-aux-background: linear-gradient(0deg, #262218 8%, #4a3c2b 34%, #594c31 55%, #312a1b 90%);
|
||||
/* Selected face (not in Figma: sampled from the reference program screen, user decision 2026-11-09; stops approximate). */
|
||||
--hm-sw-footer-aux-pressed-background: linear-gradient(0deg, #594931 0%, #3b3021 23%, #1b170f 58%, #0d0b07 100%);
|
||||
--hm-sw-footer-aux-radius: 3px;
|
||||
--hm-sw-footer-aux-font-size: 13px;
|
||||
/* 1px borders (Plugin API, btn_객체스냅 26:9248 … btn_선가중치 26:9412): Default = brown gradient, Variant2 / Variant3
|
||||
@@ -242,7 +244,7 @@
|
||||
/* Selected (Figma Variant2 / Variant3): orange border, icon and text together; it stays while hovered and after
|
||||
the pointer leaves. Icons follow through currentColor (swFooterAuxIcons, or any currentColor icon). */
|
||||
.hm-sw-footer-aux__button[aria-pressed="true"] {
|
||||
background: var(--hm-sw-footer-aux-background) padding-box, var(--hm-sw-footer-aux-pressed-border) border-box;
|
||||
background: var(--hm-sw-footer-aux-pressed-background) padding-box, var(--hm-sw-footer-aux-pressed-border) border-box;
|
||||
}
|
||||
.hm-sw-footer-aux__button[aria-pressed="true"] :is(.hm-sw-footer-aux__icon, .hm-sw-footer-aux__label) { color: var(--hm-sw-footer-accent); }
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
:is(.hm-sw-header, .hm-sw-theme):not(:where(.hm-sw-header, .hm-sw-theme) *) {
|
||||
/* Process area base (Header.Border.ProcessBase): ProcessBase gradient, DocBorder 1px, top corners 6. */
|
||||
--hm-sw-process-base-background: linear-gradient(180deg, #0c5941 0%, #19bf8d 10.1%, #109069 18.27%, #0c5941 100%);
|
||||
--hm-sw-process-base-border-gradient: var(--hm-sw-header-document-border-gradient);
|
||||
--hm-sw-process-base-border-gradient: var(--hm-sw-process-document-border-gradient);
|
||||
/* Cover over an unlit group, its arrow side and its outer rest (ProcessDim #E6004430). */
|
||||
--hm-sw-process-dim: rgba(0, 68, 48, 0.902);
|
||||
|
||||
@@ -93,6 +93,30 @@
|
||||
--hm-sw-process-multi-tab-disabled-border: #021912;
|
||||
--hm-sw-process-multi-tab-disabled-shadow: 1px 1px 1px 0 rgba(31, 148, 113, 0.7), inset 3px 2px 3px 0 rgba(9, 39, 30, 0.42), inset 3px 2px 3px 0 rgba(9, 39, 30, 0.42);
|
||||
--hm-sw-process-multi-tab-disabled-text: #7d9f91;
|
||||
|
||||
/* Step Header's own copies of the SWHeader tokens it paints with (2026-11-10). Each defaults to the SWHeader token, so a
|
||||
theme that sets --hm-sw-header-* on the outermost SW scope still reaches the Step Header; set --hm-sw-process-* there to
|
||||
change the Step Header alone. Resolved on the outermost scope like every alias here: a --hm-sw-header-* value set on an
|
||||
inner element no longer reaches these. The reused SW parts (home / logo, file-name button, window move) keep reading
|
||||
--hm-sw-header-* in sw.css. */
|
||||
--hm-sw-process-background: var(--hm-sw-header-background);
|
||||
--hm-sw-process-height: var(--hm-sw-header-height);
|
||||
--hm-sw-process-text-color: var(--hm-sw-header-text-color);
|
||||
--hm-sw-process-selected-color: var(--hm-sw-header-selected-color);
|
||||
--hm-sw-process-hover-border-gradient: var(--hm-sw-header-hover-border-gradient);
|
||||
--hm-sw-process-hover-border-width: var(--hm-sw-header-hover-border-width);
|
||||
--hm-sw-process-document-background: var(--hm-sw-header-document-background);
|
||||
--hm-sw-process-document-border-gradient: var(--hm-sw-header-document-border-gradient);
|
||||
--hm-sw-process-document-border-color: var(--hm-sw-header-document-border-color);
|
||||
--hm-sw-process-object-background: var(--hm-sw-header-object-background);
|
||||
--hm-sw-process-object-border-gradient: var(--hm-sw-header-object-border-gradient);
|
||||
--hm-sw-process-object-radius: var(--hm-sw-header-object-radius);
|
||||
--hm-sw-process-property-background: var(--hm-sw-header-property-background);
|
||||
--hm-sw-process-property-radius: var(--hm-sw-header-property-radius);
|
||||
--hm-sw-process-property-shadow: var(--hm-sw-header-property-shadow);
|
||||
--hm-sw-process-window-icon-color: var(--hm-sw-header-window-icon-color);
|
||||
--hm-sw-process-window-icon-hover-color: var(--hm-sw-header-window-icon-hover-color);
|
||||
--hm-sw-process-window-button-hover-background: var(--hm-sw-header-window-button-hover-background);
|
||||
}
|
||||
|
||||
/* ---------- Header frame ---------- */
|
||||
@@ -102,8 +126,8 @@
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
height: var(--hm-sw-header-height);
|
||||
background: var(--hm-sw-header-background);
|
||||
height: var(--hm-sw-process-height);
|
||||
background: var(--hm-sw-process-background);
|
||||
overflow: clip;
|
||||
}
|
||||
.hm-sw-process-header__bar { display: flex; align-items: stretch; height: var(--hm-sw-header-bar-height); min-width: 0; }
|
||||
@@ -120,7 +144,7 @@
|
||||
justify-content: center;
|
||||
width: max-content;
|
||||
min-width: 58px;
|
||||
color: var(--hm-sw-header-text-color);
|
||||
color: var(--hm-sw-process-text-color);
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
line-height: var(--hm-sw-header-logo-height);
|
||||
@@ -157,8 +181,8 @@
|
||||
position: absolute;
|
||||
inset: 0 1px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--hm-sw-header-object-radius);
|
||||
background: var(--hm-sw-header-object-background) padding-box, var(--hm-sw-header-object-border-gradient) border-box;
|
||||
border-radius: var(--hm-sw-process-object-radius);
|
||||
background: var(--hm-sw-process-object-background) padding-box, var(--hm-sw-process-object-border-gradient) border-box;
|
||||
}
|
||||
/* Layer field (Header.ToggleButton.LayerField): 38px, at most 234, property surface + layer border gradient.
|
||||
The bulb and the field are sibling buttons; the field button covers the whole field under the bulb. */
|
||||
@@ -174,18 +198,18 @@
|
||||
margin: 0 9px;
|
||||
padding: 0 10px 0 12px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--hm-sw-header-property-radius);
|
||||
background: var(--hm-sw-header-property-background) padding-box, var(--hm-sw-process-layer-border-gradient) border-box;
|
||||
box-shadow: var(--hm-sw-header-property-shadow);
|
||||
color: var(--hm-sw-header-text-color);
|
||||
border-radius: var(--hm-sw-process-property-radius);
|
||||
background: var(--hm-sw-process-property-background) padding-box, var(--hm-sw-process-layer-border-gradient) border-box;
|
||||
box-shadow: var(--hm-sw-process-property-shadow);
|
||||
color: var(--hm-sw-process-text-color);
|
||||
}
|
||||
.hm-sw-process-header__layer-field::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -1px;
|
||||
padding: var(--hm-sw-header-hover-border-width);
|
||||
padding: var(--hm-sw-process-hover-border-width);
|
||||
border-radius: inherit;
|
||||
background: var(--hm-sw-header-hover-border-gradient);
|
||||
background: var(--hm-sw-process-hover-border-gradient);
|
||||
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
|
||||
@@ -195,7 +219,7 @@
|
||||
.hm-sw-process-header__layer-field:not([data-disabled]):hover::after,
|
||||
.hm-sw-process-header__layer-field[data-open]::after { opacity: 1; }
|
||||
.hm-sw-process-header__layer-field:not([data-disabled]):hover,
|
||||
.hm-sw-process-header__layer-field[data-open] { color: var(--hm-sw-header-selected-color); }
|
||||
.hm-sw-process-header__layer-field[data-open] { color: var(--hm-sw-process-selected-color); }
|
||||
.hm-sw-process-header__layer-field[data-disabled] { opacity: 0.4; }
|
||||
.hm-sw-process-header .hm-sw-process-header__layer-field button:disabled { opacity: 1; }
|
||||
.hm-sw-process-header__layer-button { position: absolute; inset: -1px; z-index: 0; border-radius: inherit; }
|
||||
@@ -221,7 +245,7 @@
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border: 1px solid transparent;
|
||||
background: var(--hm-sw-header-document-background) padding-box, var(--hm-sw-header-document-border-gradient) border-box;
|
||||
background: var(--hm-sw-process-document-background) padding-box, var(--hm-sw-process-document-border-gradient) border-box;
|
||||
pointer-events: none;
|
||||
}
|
||||
.hm-sw-process-header__window > * { position: relative; }
|
||||
@@ -238,11 +262,11 @@
|
||||
}
|
||||
.hm-sw-process-header:not([data-appearance="multi"]) .hm-sw-process-header__tools .hm-sw-tool__pill-button {
|
||||
border-color: transparent;
|
||||
background: var(--hm-sw-header-document-background) padding-box, var(--hm-sw-process-tool-border-gradient) border-box;
|
||||
background: var(--hm-sw-process-document-background) padding-box, var(--hm-sw-process-tool-border-gradient) border-box;
|
||||
}
|
||||
.hm-sw-process-header .hm-sw-process-header__tools .hm-sw-tool__pill-button { color: var(--hm-sw-process-tool-icon-color); }
|
||||
.hm-sw-process-header .hm-sw-process-header__tools .hm-sw-tool__pill-button:hover:not(:disabled),
|
||||
.hm-sw-process-header .hm-sw-process-header__tools .hm-sw-tool__pill-button[aria-pressed="true"] { color: var(--hm-sw-header-selected-color); }
|
||||
.hm-sw-process-header .hm-sw-process-header__tools .hm-sw-tool__pill-button[aria-pressed="true"] { color: var(--hm-sw-process-selected-color); }
|
||||
.hm-sw-process-header .hm-sw-process-header__tools .hm-sw-tool__pill-button[aria-pressed="true"]::after { opacity: 1; }
|
||||
/* Pressed (not disabled) shows the hover picture too, like a hover that stays (SW pill hover icon = `hoverIcon`). */
|
||||
.hm-sw-process-header .hm-sw-process-header__tools .hm-sw-tool__pill-button[aria-pressed="true"]:not(:disabled) > .hm-sw-header__icon { visibility: hidden; }
|
||||
@@ -251,9 +275,9 @@
|
||||
.hm-sw-process-header .hm-sw-process-header__tools .hm-sw-tool__icon > :is(svg, img) { width: 24px; height: 24px; max-width: none; max-height: none; }
|
||||
|
||||
/* Caption buttons (Header.WindowCaption, 32 x 48): glyph #ADB0AF; hover dark + white; close hover red + white. */
|
||||
.hm-sw-process-header__caption { display: block; flex: 0 0 32px; width: 32px; height: var(--hm-sw-header-bar-height); color: var(--hm-sw-header-window-icon-color); }
|
||||
.hm-sw-process-header__caption { display: block; flex: 0 0 32px; width: 32px; height: var(--hm-sw-header-bar-height); color: var(--hm-sw-process-window-icon-color); }
|
||||
.hm-sw-process-header__caption > svg { display: block; }
|
||||
.hm-sw-process-header__caption:hover:not(:disabled) { background: var(--hm-sw-header-window-button-hover-background); color: var(--hm-sw-header-window-icon-hover-color); }
|
||||
.hm-sw-process-header__caption:hover:not(:disabled) { background: var(--hm-sw-process-window-button-hover-background); color: var(--hm-sw-process-window-icon-hover-color); }
|
||||
.hm-sw-process-header__caption[data-kind="close"]:hover:not(:disabled) { background: var(--hm-sw-process-close-hover-background); color: var(--hm-sw-process-close-icon-hover); }
|
||||
|
||||
/* ---------- Process area (SWProcessBar) ---------- */
|
||||
@@ -374,8 +398,8 @@
|
||||
.hm-sw-process-bar__step-box > [data-layer="inset-left"] { background: linear-gradient(90deg, var(--hm-sw-process-tab-inset) 0, transparent 6px); }
|
||||
.hm-sw-process-bar__step-box > [data-layer="ring"] { border: 2px solid var(--hm-sw-process-tab-ring); }
|
||||
|
||||
.hm-sw-process-bar__step:hover:not(:disabled) { color: var(--hm-sw-header-selected-color); }
|
||||
.hm-sw-process-bar__step[aria-checked="true"] { color: var(--hm-sw-header-selected-color); }
|
||||
.hm-sw-process-bar__step:hover:not(:disabled) { color: var(--hm-sw-process-selected-color); }
|
||||
.hm-sw-process-bar__step[aria-checked="true"] { color: var(--hm-sw-process-selected-color); }
|
||||
.hm-sw-process-bar__step[aria-checked="true"] > .hm-sw-process-bar__step-box { opacity: 1; }
|
||||
.hm-sw-process-bar__step[aria-checked="true"] [data-layer="depth"] { background: var(--hm-sw-process-tab-active-base); }
|
||||
.hm-sw-process-bar__step[aria-checked="true"] [data-layer="shade"] { background: var(--hm-sw-process-tab-active-shade); }
|
||||
@@ -490,11 +514,11 @@
|
||||
/* File name: 189 at most (Figma 873:16254), solid #0E4C3A border. */
|
||||
.hm-sw-process-header[data-appearance="multi"] .hm-sw-process-header__bar > .hm-sw-header__document { flex: 189 1 0; max-width: 189px; }
|
||||
.hm-sw-process-header[data-appearance="multi"] .hm-sw-process-header__bar > .hm-sw-header__document .hm-sw-header__document-button {
|
||||
background: var(--hm-sw-header-document-background) padding-box, linear-gradient(var(--hm-sw-header-document-border-color) 0 0) border-box;
|
||||
background: var(--hm-sw-process-document-background) padding-box, linear-gradient(var(--hm-sw-process-document-border-color) 0 0) border-box;
|
||||
}
|
||||
.hm-sw-process-header[data-appearance="multi"] .hm-sw-process-header__center { flex: 1278 1 0; }
|
||||
.hm-sw-process-header[data-appearance="multi"] .hm-sw-process-header__window::before {
|
||||
background: var(--hm-sw-header-document-background) padding-box, linear-gradient(var(--hm-sw-header-document-border-color) 0 0) border-box;
|
||||
background: var(--hm-sw-process-document-background) padding-box, linear-gradient(var(--hm-sw-process-document-border-color) 0 0) border-box;
|
||||
}
|
||||
/* SW pill at the Figma place: 41 high, 4 from the top, 5 from the window start, 2 before the window move. */
|
||||
.hm-sw-process-header[data-appearance="multi"] .hm-sw-process-header__window .hm-sw-process-header__tools { height: 41px; margin: 4px 2px 0 5px; }
|
||||
@@ -609,7 +633,7 @@
|
||||
.hm-sw-process-bar[data-appearance="multi"] .hm-sw-process-bar__step:disabled { color: var(--hm-sw-process-multi-tab-disabled-text); }
|
||||
.hm-sw-process-bar[data-appearance="multi"] .hm-sw-process-bar__group .hm-sw-process-bar__step:disabled > .hm-sw-process-bar__step-box {
|
||||
border-color: var(--hm-sw-process-multi-tab-disabled-border);
|
||||
background: var(--hm-sw-header-property-background);
|
||||
background: var(--hm-sw-process-property-background);
|
||||
box-shadow: var(--hm-sw-process-multi-tab-disabled-shadow);
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -627,8 +651,8 @@
|
||||
border-color: transparent;
|
||||
border-bottom-width: 0;
|
||||
border-radius: 2px 2px 0 0;
|
||||
background: var(--hm-sw-process-tool-open-background) padding-box, var(--hm-sw-header-hover-border-gradient) border-box;
|
||||
color: var(--hm-sw-header-selected-color);
|
||||
background: var(--hm-sw-process-tool-open-background) padding-box, var(--hm-sw-process-hover-border-gradient) border-box;
|
||||
color: var(--hm-sw-process-selected-color);
|
||||
}
|
||||
.hm-sw-process-header .hm-sw-process-header__tools .hm-sw-process-split[data-open] > .hm-sw-tool__pill-button::after { opacity: 0; }
|
||||
|
||||
@@ -644,7 +668,7 @@
|
||||
height: var(--hm-sw-process-split-tail, 3px);
|
||||
border: solid transparent;
|
||||
border-width: 0 1px;
|
||||
background: linear-gradient(var(--hm-sw-process-tool-open-edge), var(--hm-sw-process-tool-open-edge)) padding-box, var(--hm-sw-header-hover-border-gradient) border-box;
|
||||
background: linear-gradient(var(--hm-sw-process-tool-open-edge), var(--hm-sw-process-tool-open-edge)) padding-box, var(--hm-sw-process-hover-border-gradient) border-box;
|
||||
}
|
||||
.hm-sw-process-split__panel {
|
||||
position: absolute;
|
||||
@@ -658,7 +682,7 @@
|
||||
border: 1px solid transparent;
|
||||
border-top-width: 0;
|
||||
border-radius: 0 2px 2px 2px;
|
||||
background: linear-gradient(var(--hm-sw-process-split-panel-background), var(--hm-sw-process-split-panel-background)) padding-box, var(--hm-sw-header-hover-border-gradient) border-box;
|
||||
background: linear-gradient(var(--hm-sw-process-split-panel-background), var(--hm-sw-process-split-panel-background)) padding-box, var(--hm-sw-process-hover-border-gradient) border-box;
|
||||
box-shadow: var(--hm-sw-process-split-panel-shadow);
|
||||
}
|
||||
/* Gold line on the panel top, from the button right edge to the panel end (Header.Border.ToolMenuStep). */
|
||||
@@ -669,7 +693,7 @@
|
||||
right: 0;
|
||||
height: 1px;
|
||||
border-top-right-radius: 2px;
|
||||
background: var(--hm-sw-header-hover-border-gradient) 0 0 / 100% 36px no-repeat;
|
||||
background: var(--hm-sw-process-hover-border-gradient) 0 0 / 100% 36px no-repeat;
|
||||
pointer-events: none;
|
||||
}
|
||||
.hm-sw-process-split__cell { display: grid; place-items: center; height: 34px; }
|
||||
@@ -685,4 +709,4 @@
|
||||
}
|
||||
.hm-sw-process-split__frame > svg { display: block; overflow: visible; }
|
||||
.hm-sw-process-split__pane { fill: var(--hm-sw-process-split-cell-pane); stroke: rgba(0, 0, 0, 0.3); stroke-width: 1; }
|
||||
.hm-sw-process-split__cell:is(:hover, [aria-checked="true"]) .hm-sw-process-split__pane { fill: var(--hm-sw-header-selected-color); }
|
||||
.hm-sw-process-split__cell:is(:hover, [aria-checked="true"]) .hm-sw-process-split__pane { fill: var(--hm-sw-process-selected-color); }
|
||||
@@ -196,10 +196,45 @@ async function dropdownColors(page, def, themed) {
|
||||
await page.close()
|
||||
}
|
||||
|
||||
// ---------- 1b. Step Header (2026-11-10): --hm-sw-process-* copies of the SWHeader tokens it paints with ----------
|
||||
// Without a Step Header value an SWHeader theme on the outermost SW scope still reaches the Step Header; a
|
||||
// --hm-sw-process-* value there changes the Step Header alone (it wins over the SWHeader token) and leaves SWHeader as is.
|
||||
{
|
||||
const page = await newPage()
|
||||
await open(page, 'sw-step-header--process-header')
|
||||
const outerOf = selector => page.locator(selector).first().evaluateHandle(element => { let outer = element; for (let e = element; e; e = e.parentElement) if (e.matches?.('.hm-sw-header, .hm-sw-theme')) outer = e; return outer })
|
||||
const scope = await outerOf('.hm-sw-process-header')
|
||||
const set = async (values) => scope.evaluate((outer, values) => Object.entries(values).forEach(([token, value]) => value === null ? outer.style.removeProperty(token) : outer.style.setProperty(token, value)), values)
|
||||
const read = () => page.evaluate(() => {
|
||||
const header = getComputedStyle(document.querySelector('.hm-sw-process-header'))
|
||||
const step = document.querySelector('.hm-sw-process-bar__step[aria-checked="true"]')
|
||||
return { background: `${header.backgroundColor} ${header.backgroundImage}`, selected: step ? getComputedStyle(step).color : null }
|
||||
})
|
||||
const base = await read()
|
||||
await set({ '--hm-sw-header-background': 'rgb(44, 55, 66)', '--hm-sw-header-selected-color': 'rgb(11, 22, 33)' })
|
||||
const inherited = await read()
|
||||
check('Step Header: 바깥 범위의 SWHeader 토큰(배경·선택 색)이 그대로 전달', base.selected !== null && inherited.background.includes('rgb(44, 55, 66)') && inherited.selected === 'rgb(11, 22, 33)', JSON.stringify({ base, inherited }))
|
||||
await set({ '--hm-sw-process-background': 'rgb(1, 2, 3)', '--hm-sw-process-selected-color': 'rgb(77, 88, 99)' })
|
||||
const own = await read()
|
||||
check('Step Header: --hm-sw-process-* 지정이 SWHeader 토큰보다 우선', own.background.includes('rgb(1, 2, 3)') && own.selected === 'rgb(77, 88, 99)', JSON.stringify(own))
|
||||
await set({ '--hm-sw-header-background': null, '--hm-sw-header-selected-color': null, '--hm-sw-process-background': null, '--hm-sw-process-selected-color': null })
|
||||
check('Step Header: 지정을 지우면 기본값으로 복귀', JSON.stringify(await read()) === JSON.stringify(base), JSON.stringify(base))
|
||||
await page.close()
|
||||
// The same Step Header tokens on an SWHeader page change nothing there.
|
||||
const header = await newPage()
|
||||
await open(header, 'sw-header--overview')
|
||||
const paint = () => header.evaluate(() => [...document.querySelectorAll('.hm-sw-header, .hm-sw-header *')].map(element => { const s = getComputedStyle(element); return `${s.color}|${s.backgroundColor}|${s.backgroundImage}|${s.boxShadow}|${s.borderTopColor}` }).join('\n'))
|
||||
const before = await paint()
|
||||
await header.locator('.hm-sw-header').first().evaluate(element => { let outer = element; for (let e = element; e; e = e.parentElement) if (e.matches?.('.hm-sw-header, .hm-sw-theme')) outer = e; ['background', 'height', 'text-color', 'selected-color', 'hover-border-gradient', 'document-background', 'object-background', 'property-background', 'window-icon-color'].forEach(name => outer.style.setProperty(`--hm-sw-process-${name}`, 'rgb(1, 2, 3)')) })
|
||||
check('Step Header 토큰은 SWHeader 화면에 영향 없음', before === await paint(), `${before.split('\n').length} elements`)
|
||||
await header.close()
|
||||
}
|
||||
|
||||
// ---------- 2. Regression: new cascade vs legacy cascade on every existing SW Story ----------
|
||||
const here = path.dirname(fileURLToPath(import.meta.url))
|
||||
const outermost = ':is(.hm-sw-header, .hm-sw-theme):not(:where(.hm-sw-header, .hm-sw-theme) *)'
|
||||
const tokenBlocks = (await Promise.all(['sw.css', 'sw-viewer.css', 'sw-footer.css'].map(file => readFile(path.join(here, '../src/theme', file), 'utf8'))))
|
||||
// 2026-11-10: sw-process-header.css (Step Header aliases) and the Step Header Stories joined the comparison.
|
||||
const tokenBlocks = (await Promise.all(['sw.css', 'sw-viewer.css', 'sw-footer.css', 'sw-process-header.css'].map(file => readFile(path.join(here, '../src/theme', file), 'utf8'))))
|
||||
.map(text => {
|
||||
const start = text.indexOf(`${outermost} {`)
|
||||
const end = text.indexOf('\n}', start)
|
||||
@@ -212,7 +247,7 @@ const legacy = tokenBlocks.map(block => `.hm-sw-header,\n.hm-sw-theme ${block}`)
|
||||
// command row) even though no value changes, so the legacy shot is also compared with this control shot.
|
||||
const control = tokenBlocks.map(block => `${outermost} ${block}`).join('\n')
|
||||
const index = await (await fetch(`${storybook}/index.json`)).json()
|
||||
const ids = Object.values(index.entries).filter(entry => entry.type === 'story' && /^sw-(header|popup|viewer|footer|home-menu)/.test(entry.id)).map(entry => entry.id)
|
||||
const ids = Object.values(index.entries).filter(entry => entry.type === 'story' && /^sw-(header|popup|viewer|footer|home-menu|step-header)/.test(entry.id)).map(entry => entry.id)
|
||||
const snapshot = page => page.evaluate(() => [...document.querySelectorAll('.hm-sw-header, .hm-sw-theme')].flatMap(root => [root, ...root.querySelectorAll('*')]).map(element => {
|
||||
const style = getComputedStyle(element), rect = element.getBoundingClientRect()
|
||||
return `${style.fontFamily}|${style.fontSize}|${style.color}|${style.backgroundImage.slice(0, 80)}|${Math.round(rect.x * 10)},${Math.round(rect.y * 10)},${Math.round(rect.width * 10)},${Math.round(rect.height * 10)}`
|
||||
|
||||
@@ -30,7 +30,7 @@ Brown·Orange 계열 팔레트(`--hm-sem-palette-brown-*`, `--hm-sem-palette-ora
|
||||
| 페이지·콘텐츠 배경 | `--hm-sem-bg-content` | Beige |
|
||||
| 카드·팝업 표면 | `--hm-sem-bg-surface` | White |
|
||||
| 보조 면 | `--hm-sem-bg-subtle` | warm-neutral-muted |
|
||||
| 브랜드 배경(gradient) | `--hm-sem-bg-brand`, `--hm-sem-bg-brown`, `--hm-sem-bg-orange` | 세로 gradient(GNB·Brand 버튼 면) |
|
||||
| 브랜드 배경(gradient) | `--hm-sem-bg-brand`, `--hm-sem-bg-brown`, `--hm-sem-bg-orange` | 공통 세로 gradient 배경(소비 화면 배경용). 패키지 컴포넌트(GNB·Button·Dashboard)에는 자동 연결되지 않음 — GNB 면은 `--hm-gnb-background`(저장소 `docs/guidelines/runtime-theme-contract.md` "Brown·Orange 공통 Background") |
|
||||
| 기본 글자 | `--hm-sem-fg-primary` | green-dark-1 |
|
||||
| 보조 글자 | `--hm-sem-fg-secondary` | warm-neutral-dark |
|
||||
| placeholder | `--hm-sem-fg-placeholder` | gray-500 |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* LNB pattern (layout visual reference: D:\컴포넌트\ui-viz Patterns/LNB, implementation: HM Collapsible + plain elements).
|
||||
A 272px green column: horizontal 5-stop surface, brand on top, menu in the middle, text-only profile at the bottom. The current
|
||||
A 240px green column (--hm-lnb-width; 272px before 2026-11-09): horizontal 5-stop surface, brand on top, menu in the middle, text-only profile at the bottom. The current
|
||||
item is a Beige face (left corners rounded) that runs to the content edge and joins it through two concave corners; hover is
|
||||
Black 10% to the same edge; an open group is a Black 8% face with a 1px green line. Spacing values are pattern-local. */
|
||||
:root, [data-hm-theme] {
|
||||
|
||||
@@ -50,7 +50,9 @@ export const AUX_ITEMS: SWFooterAuxItem[] = [
|
||||
* Labels, categories and the extra command are program data; WIPEOUT and 객체스냅추적 have no Figma icon, so the
|
||||
* Story passes its own (lucide) icons the way a program would.
|
||||
*/
|
||||
const lucide = (node: ReactNode) => <span className="sw-footer-story-lucide">{node}</span>
|
||||
const lucide = (node: ReactNode, outlined = false) => (
|
||||
<span className={outlined ? "sw-footer-story-lucide sw-footer-story-lucide--outlined" : "sw-footer-story-lucide"}>{node}</span>
|
||||
)
|
||||
export const REFERENCE_TOOLS: SWFooterTool[] = [
|
||||
{ id: "line", label: "LINE", ...swFooterToolIcons.line },
|
||||
{ id: "pline", label: "PLINE", ariaLabel: "Polyline", ...swFooterToolIcons.pline },
|
||||
@@ -74,7 +76,7 @@ export const REFERENCE_AUX: SWFooterAuxItem[] = [
|
||||
{ id: "lineWeight", label: "선가중치", icon: swFooterAuxIcons.lineWeight, pressed: false },
|
||||
{ id: "gridBackground", label: "격자배경", icon: swFooterAuxIcons.gridBackground, pressed: true },
|
||||
{ id: "gridSnap", label: "격자스냅", icon: swFooterAuxIcons.gridSnap, pressed: false },
|
||||
{ id: "objectTrack", label: "객체스냅추적", icon: lucide(<PenLine size={14} />), pressed: true },
|
||||
{ id: "objectTrack", label: "객체스냅추적", icon: lucide(<PenLine size={14} />, true), pressed: true },
|
||||
]
|
||||
export const REFERENCE_HISTORY = [
|
||||
"Raster image support loaded.",
|
||||
|
||||
@@ -27,6 +27,11 @@
|
||||
.sw-footer-story-guide-frame { display: block; width: 194px; height: 111px; background-repeat: no-repeat; background-size: 250px 1179px; }
|
||||
.sw-footer-story-guide-text { display: grid; place-items: center; height: 100%; padding: 8px; font-size: 12px; color: #d4cbbd; text-align: center; }
|
||||
.sw-footer-story-lucide { display: grid; place-items: center; }
|
||||
/* Aux icons carry a 1px OUTSIDE black 90% outline (AuxIcon); a stroke icon has none, so the same outline is drawn around its alpha. */
|
||||
.sw-footer-story-lucide--outlined svg {
|
||||
filter: drop-shadow(1px 0 0 rgba(0, 0, 0, .9)) drop-shadow(-1px 0 0 rgba(0, 0, 0, .9))
|
||||
drop-shadow(0 1px 0 rgba(0, 0, 0, .9)) drop-shadow(0 -1px 0 rgba(0, 0, 0, .9));
|
||||
}
|
||||
.sw-footer-story-status { padding: 0 12px; font-size: 12px; color: #d4cbbd; }
|
||||
.sw-footer-story-row { display: flex; align-items: flex-start; gap: 16px; }
|
||||
.sw-footer-story-row--tight { gap: 0; }
|
||||
|
||||
Reference in new issue
Block a user