Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
228d8872fc | ||
|
|
28e00f2870 |
No files matched your search
@@ -557,7 +557,8 @@ Composed Controls의 기존 레이어·색상·상세보기 예제는 보존하
|
||||
| `--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-more-item-hover-background` 기본값) |
|
||||
| `--hm-sw-header-more-menu-background` / `-more-menu-shadow` / `-more-menu-border-color` / `-more-item-hover-background` | `#0E3D2F` / `0 4px 12px rgba(0,0,0,.5)` / `var(--hm-sw-header-object-border-color)` / `var(--hm-sw-header-hover)` | 더보기(`...`) 메뉴 면·그림자·테두리·항목 Hover(2026-11-10 개별 토큰, 값 변경 없음). 뷰어 Header Dropdown·로고 메뉴도 사용 |
|
||||
| `--hm-sw-header-more-menu-background` / `-more-menu-shadow` / `-more-menu-border-color` / `-more-item-hover-background` | `#0E3D2F` / `0 4px 12px rgba(0,0,0,.5)` / `var(--hm-sw-header-object-border-color)` / `var(--hm-sw-header-hover)` | 더보기(`...`) 메뉴 면·그림자·테두리·항목 Hover(2026-11-10 개별 토큰, 값 변경 없음). 뷰어 Header Dropdown·로고 메뉴는 이 값을 기본값으로 하는 `--hm-sw-viewer-header-menu-*`를 읽음 |
|
||||
| `--hm-sw-header-k-guide-border-color` / `-k-guide-background` | `var(--hm-sw-property-border)`(`#A67D03`) / `#0E3C2E` | K 가이드 패널 테두리·면(2026-11-10 개별 토큰, 값 변경 없음). 속성창 테두리와 독립 |
|
||||
| `--hm-sw-header-hover-border-gradient` / `-hover-border-width` | Figma 홈 hover gradient / 1px | Hover 테두리(§11.1) |
|
||||
| `--hm-sw-header-home-on-line-color` / `-home-on-line-width` | `#5F4B0F` / 2px | Home On(로고 메뉴 열림, Figma 662:67267): Header 전폭 상·하단 선. 로고는 Hover 테두리 유지 |
|
||||
| `--hm-sw-header-document-menu-width` / `-document-menu-background` / `-document-menu-border-color` / `-document-menu-border-width` | 310px / `#103127` / `#041611` / 1px | 문서 Dropdown(§5.1) |
|
||||
@@ -589,7 +590,7 @@ Composed Controls의 기존 레이어·색상·상세보기 예제는 보존하
|
||||
| `--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 색 포함 |
|
||||
| `--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 색 포함. Popup(속성창·`SWPopupRow`·뷰어 패널) 값 글자색은 이 값을 기본값으로 하는 `--hm-sw-popup-value-color`(2026-11-10)를 읽음 |
|
||||
|
||||
## 15. Figma 비교 결과와 의도적 차이
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ import "@hm-design/sw/styles.css" // 앱 진입점에서 한 번
|
||||
- SW 화면은 `SWTheme`(또는 SW 컴포넌트 루트)와 SW 토큰의 글꼴을 그대로 쓴다. Web UI도 Noto Sans KR이지만 Web 토큰(`--hm-font-family-base`)이나 Web `theme.css`의 글꼴 설정을 SW 화면에 복사하지 않고, SW 컴포넌트에 `font-family`를 개별 지정하지 않는다. 프로그램(사용자)이 다른 글꼴을 정한 경우에만 §3.1 방법으로 `--hm-sw-header-font-family`를 바꾸고 결과 보고에 "글꼴 변경"으로 적는다.
|
||||
- Weight 범위(2026-10-20 사용자 확정): SWHeader 안의 필드 값은 400, Composed Controls(SWHeader 밖에 단독 배치한 `SWHeaderLayerMenu` 등)의 필드 값은 700. 파일명·Footer 보조기능/명령어 제목·Footer 분류 탭·Viewer Header 파일명/버튼 라벨/Model·Layout·최근파일 이름/경로/미리보기 제목, Footer Model/Layout 탭 라벨(모든 상태, 2026-10-22 사용자 확정, 이름 편집 입력은 400)은 700, Footer `전체보기`·명령·보조기능 라벨은 400. 임의로 일괄 변경하지 않는다. Figma 고정 폭 칸(예: 가중치·스케일 38px)에 그보다 긴 값(예: `ByLayer`)이 들어가면 말줄임 + `title`이 된다. Figma 레이어 이름의 `Dotum/bold/14`를 보고 700으로 바꾸거나 겹침·외곽선을 따로 만들지 않는다.
|
||||
- CSS는 전역(`html`, `body`, `*`)을 건드리지 않는다. `body` margin 등 페이지 기본값은 소비 앱이 정한다.
|
||||
- 스크롤바: SW 전용 스크롤바 디자인은 없다. 기존 SW 컴포넌트는 브라우저 기본 스크롤바를 쓰며(속성Bar만 `scrollbar-width: thin`), Popup 본문 스크롤은 `SWPopup`이 처리한다. 새 스크롤 영역에 임의 스크롤바 색·두께를 만들지 않는다.
|
||||
- 스크롤바: 일반 스크롤바 디자인은 없다. 패키지가 정한 곳만 `scrollbar-width: thin`이다 — 속성Bar, Popup 본문(`SWPopup`·`SWViewerPanel`·속성창, `--hm-sw-popup-scrollbar-thumb`·`-track`), Footer 명령창 기록(`--hm-sw-footer-command-scrollbar-thumb`·`-track`, 2026-11-10). 그 밖의 SW 컴포넌트는 브라우저 기본 스크롤바를 쓴다. 새 스크롤 영역에 임의 스크롤바 색·두께를 만들지 않는다.
|
||||
- TypeScript에서 CSS side-effect import 오류가 나면 Vite의 `vite/client` 타입을 포함한다.
|
||||
|
||||
## 3. 스타일 규칙
|
||||
@@ -138,6 +138,12 @@ SW 토큰(`--hm-sw-header-*`, `--hm-sw-property-*`, `--hm-sw-popup-*`, `--hm-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.
|
||||
- Viewer Header(`SWViewerHeader`, 2026-11-10): 파일명·객체 수정·속성 면의 SWHeader 토큰 10개를 `--hm-sw-viewer-document-*`·`--hm-sw-viewer-object-*`·`--hm-sw-viewer-property-*` alias(기본값 `var(--hm-sw-header-…)`)로 읽습니다. 기본은 SWHeader 테마 공유 그대로이고, 가장 바깥 범위에 `--hm-sw-viewer-*`를 정하면 Viewer Header만 바뀝니다. 안쪽 요소에서 바꾼 `--hm-sw-header-*`는 이 alias에 전달되지 않습니다. 목록·적용 범위는 [DESIGN_RULES.md](./DESIGN_RULES.md) §11.
|
||||
- 컴포넌트 전용 색 토큰(2026-11-10, 값 변경 없음): 아래 9개는 같은 방식(가장 바깥 범위에서 정함, 기본값은 기존 토큰 또는 기존 값)이며, 정하지 않으면 지금 모양 그대로이고 정하면 해당 부분만 바뀝니다.
|
||||
- Viewer Header 메뉴(로고 메뉴·Header 안 Dropdown·More): `--hm-sw-viewer-header-menu-background`·`-border-color`·`-shadow`·`-item-hover-background`(기본 SWHeader `--hm-sw-header-more-*`). SWHeader `...` 메뉴는 그대로입니다. [DESIGN_RULES.md](./DESIGN_RULES.md) §11.
|
||||
- SWHeader K 가이드 패널: `--hm-sw-header-k-guide-border-color`(기본 `var(--hm-sw-property-border)`)·`--hm-sw-header-k-guide-background`(`#0e3c2e`). 속성창 테두리는 그대로입니다. §5.
|
||||
- Popup 값 글자색: `--hm-sw-popup-value-color`(기본 `var(--hm-sw-header-input-color)`). 속성Bar 값은 그대로입니다. §8.
|
||||
- Footer 명령창 기록 스크롤바: `--hm-sw-footer-command-scrollbar-thumb`(`#574b30`)·`-track`(`transparent`). §13.
|
||||
- 공유 유지(전용 토큰 없음): Focus 링(`--hm-sw-header-focus-color`, Footer 포함), 글자색·글자 외곽선, Viewer 메뉴 구분선.
|
||||
|
||||
## 4. 판단 기록 남기기
|
||||
|
||||
|
||||
@@ -71,7 +71,8 @@
|
||||
- 목록 최대 높이 240px(8행), 넘치면 내부 스크롤. 색상 팔레트 118px.
|
||||
- 속성창·K 가이드는 `position: fixed` Overlay(뷰포트 안 보정, Portal·Dim 없음).
|
||||
- 한 번에 하나의 Dropdown(SWHeader). 단독 사용 시 각 컴포넌트가 바깥 클릭으로 스스로 닫힙니다.
|
||||
- More(`...`) 메뉴 색(2026-11-10 개별 토큰, 값 변경 없음): 면 `--hm-sw-header-more-menu-background`(`#0E3D2F`), 그림자 `--hm-sw-header-more-menu-shadow`(`0 4px 12px rgba(0,0,0,.5)`), 테두리 `--hm-sw-header-more-menu-border-color`(기본 `var(--hm-sw-header-object-border-color)`), 항목 Hover `--hm-sw-header-more-item-hover-background`(기본 `var(--hm-sw-header-hover)`). 뷰어 Header의 Dropdown·로고 메뉴도 같은 토큰을 읽습니다([VIEWER.md](./VIEWER.md)).
|
||||
- More(`...`) 메뉴 색(2026-11-10 개별 토큰, 값 변경 없음): 면 `--hm-sw-header-more-menu-background`(`#0E3D2F`), 그림자 `--hm-sw-header-more-menu-shadow`(`0 4px 12px rgba(0,0,0,.5)`), 테두리 `--hm-sw-header-more-menu-border-color`(기본 `var(--hm-sw-header-object-border-color)`), 항목 Hover `--hm-sw-header-more-item-hover-background`(기본 `var(--hm-sw-header-hover)`). 뷰어 Header의 Dropdown·로고 메뉴는 기본값이 이 4개인 `--hm-sw-viewer-header-menu-*` alias를 읽습니다(§11, [VIEWER.md](./VIEWER.md)). 정하지 않으면 같은 색이고, alias를 정하면 뷰어 메뉴만 바뀝니다.
|
||||
- K 가이드 패널 색(2026-11-10 개별 토큰, 값 변경 없음): 테두리 `--hm-sw-header-k-guide-border-color`(기본 `var(--hm-sw-property-border)`, `#A67D03`), 면 `--hm-sw-header-k-guide-background`(`#0E3C2E`). 정하면 K 가이드만 바뀌고 속성창 테두리(`--hm-sw-property-border`)는 그대로입니다. 문구 글자·외곽선은 `--hm-sw-header-text-color`·`--hm-sw-header-label-outline` 그대로입니다.
|
||||
|
||||
## 6. Motion
|
||||
|
||||
@@ -111,7 +112,8 @@
|
||||
| 구분선 | 개수 목록 아래선 검정 50%, 위치 표 선 검정 40% | `--hm-sw-popup-divider-color`, `--hm-sw-popup-table-line-color` |
|
||||
| Switch 그림자 | Off·On 안쪽 그림자 + 테두리 링 + 1px `#FCF0D8` 하이라이트, Thumb `0 .5px 1px rgba(0,0,0,.6)` | `--hm-sw-switch-off-shadow`, `--hm-sw-switch-on-shadow`, `--hm-sw-switch-thumb-shadow` |
|
||||
| 속성 행(`SWPopupPropertyRow`) | 속성창 행 그대로(라벨 72px 700, 값 상자 28px, `picker` 필드). 데이터 = `SWHeaderProperty` + `menus` | `--hm-sw-property-*`, `--hm-sw-property-field-*` |
|
||||
| 행(`SWPopupRow`) | 최소 32px, 라벨 72px 700 검정, 값 글자 `#E60403` 500, 단위 12px | `--hm-sw-property-label`, `--hm-sw-header-input-color` |
|
||||
| 행(`SWPopupRow`) | 최소 32px, 라벨 72px 700 검정, 값 글자 `#E60403` 500, 단위 12px | `--hm-sw-property-label`, `--hm-sw-popup-value-color` |
|
||||
| 값 글자색(2026-11-10 개별 토큰, 값 변경 없음) | 편집 가능한 값 `#E60403`: 속성창·`SWPopupRow`의 값 Input(값·게이지 값·색상 값 칸), 값 `select`, 속성창 안 `SWSelect`(사용 가능), Dropdown Trigger, `SWViewerPanel`의 `SWViewerInput`(검색 제외). 읽기 전용 값은 라벨색 그대로. 기본 `var(--hm-sw-header-input-color)`라 Header 테마를 따르고, 정하면 Popup 값만 바뀜(SWHeader 속성Bar 값은 `--hm-sw-header-input-color`) | `--hm-sw-popup-value-color` |
|
||||
| 값 상자 | 28px, Hover 1px `rgba(33,29,19,.4)`, 숫자 편집 On `#0F0D0A`(단위 흰색), Dropdown On `#211D13` | `--hm-sw-property-field-*` |
|
||||
| 동작 | Non-modal(Dim·Portal·Focus trap 없음). Escape는 Popup 안이면 그 Popup, 밖이면 최근 사용 Popup 하나. `autoFocus`면 열릴 때 Focus, 안에서 닫히면 원래 위치로 | — |
|
||||
|
||||
@@ -160,7 +162,8 @@
|
||||
|
||||
- **Model / Layout 탭 토큰:** 위 `--hm-sw-viewer-tabs-*`·`--hm-sw-viewer-tab-*`은 탭 컨테이너·버튼에만 연결되며 다른 Header 부품·SWHeader·Footer·패널에 쓰이지 않습니다. 탭 색만 바꾸려면 가장 바깥 SW 범위의 `style`로 덮습니다: 화면을 `<SWTheme style={...}>`로 감싸거나 단독 `SWViewerHeader`의 루트에 지정합니다(토큰이 그 범위에 선언되므로 일반 `div` 래퍼의 값은 가려짐). 예: `style={{ "--hm-sw-viewer-tab-active-border": "linear-gradient(#e07b00, #e07b00)", "--hm-sw-viewer-tab-inactive-border": "linear-gradient(#3d6bb3, #3d6bb3)" }}`. `var(...)` 기본값(`active-text`·`hover-*`·`focus-color`)은 SW 범위에서 계산되므로 공통 토큰(`--hm-sw-viewer-accent` 등)을 바꾸면 같은 범위에서 함께 따라가고, 탭 토큰을 직접 지정하면 공통 토큰과 독립합니다. 글자 외곽선(`text-shadow`)은 탭에서도 기존 그대로(이름 `--hm-sw-header-text-outline` 1.3px, 개수 `--hm-sw-header-label-outline` 1px)이며 탭 토큰이 아닙니다. 탭 class로 색을 덮지 않습니다.
|
||||
- **공유 토큰(모드 혼합 아님):** 뷰어 Header의 파일명·객체 수정·속성 면은 `--hm-sw-header-document-*`, `--hm-sw-header-object-*`, `--hm-sw-header-property-*`를, 글자 외곽선은 `--hm-sw-header-text-outline`·`--hm-sw-header-label-outline`을 그대로 읽습니다(§2·§3). 패널 제목 막대 추가 버튼(`ghost`)의 Hover·Focus 색은 `--hm-sw-header-selected-color`입니다.
|
||||
- **Viewer Header 전용 덮어쓰기(2026-11-10, 공유 유지):** 위 면 토큰 10개는 같은 이름의 `--hm-sw-viewer-*` alias(가장 바깥 SW 범위, 기본값 `var(--hm-sw-header-…)`)를 거쳐 읽습니다 — `--hm-sw-viewer-document-background`·`-document-border-gradient`·`-document-border-color`, `--hm-sw-viewer-object-background`·`-object-border-gradient`·`-object-radius`, `--hm-sw-viewer-property-background`·`-property-border-color`·`-property-radius`·`-property-shadow`. 적용 범위: 파일명 면, 객체 수정 면, 속성 입력 그룹(`data-surface="field"`), 오른쪽 상태·버튼 띠와 그 시작 조각. 기본 동작은 그대로 공유입니다(정하지 않으면 SWHeader 테마를 따름). Viewer Header만 바꾸려면 가장 바깥 SW 범위(`<SWTheme style>` 또는 단독 `SWViewerHeader`의 `style`)에 `--hm-sw-viewer-*`를 지정합니다(SWHeader·Step Header 영향 없음). 제약: alias는 가장 바깥 범위에서 계산되므로 안쪽 요소에서만 바꾼 `--hm-sw-header-*`는 이 10개에 전달되지 않습니다. 파일명 버튼 등 재사용 SWHeader 부품의 `sw.css` 규칙(로고가 있을 때 로고 칸 바탕 — SWHeader 홈과 같은 공용 규칙 `.hm-sw-header__home::before, .hm-sw-viewer-header__brand[data-logo]::before` — 포함), Dropdown·More·파일 목록, 패널(`selected-color`·`input-color`), 글자 외곽선은 계속 `--hm-sw-header-*`를 읽습니다.
|
||||
- **Viewer Header 전용 덮어쓰기(2026-11-10, 공유 유지):** 위 면 토큰 10개는 같은 이름의 `--hm-sw-viewer-*` alias(가장 바깥 SW 범위, 기본값 `var(--hm-sw-header-…)`)를 거쳐 읽습니다 — `--hm-sw-viewer-document-background`·`-document-border-gradient`·`-document-border-color`, `--hm-sw-viewer-object-background`·`-object-border-gradient`·`-object-radius`, `--hm-sw-viewer-property-background`·`-property-border-color`·`-property-radius`·`-property-shadow`. 적용 범위: 파일명 면, 객체 수정 면, 속성 입력 그룹(`data-surface="field"`), 오른쪽 상태·버튼 띠와 그 시작 조각. 기본 동작은 그대로 공유입니다(정하지 않으면 SWHeader 테마를 따름). Viewer Header만 바꾸려면 가장 바깥 SW 범위(`<SWTheme style>` 또는 단독 `SWViewerHeader`의 `style`)에 `--hm-sw-viewer-*`를 지정합니다(SWHeader·Step Header 영향 없음). 제약: alias는 가장 바깥 범위에서 계산되므로 안쪽 요소에서만 바꾼 `--hm-sw-header-*`는 이 10개에 전달되지 않습니다. 파일명 버튼 등 재사용 SWHeader 부품의 `sw.css` 규칙(로고가 있을 때 로고 칸 바탕 — SWHeader 홈과 같은 공용 규칙 `.hm-sw-header__home::before, .hm-sw-viewer-header__brand[data-logo]::before` — 포함), 파일 목록, 패널 `selected-color`, 글자 외곽선은 계속 `--hm-sw-header-*`를 읽습니다. Header 안 Dropdown·More·로고 메뉴는 아래 메뉴 alias, 패널 값 글자색은 `--hm-sw-popup-value-color`(§8)를 읽습니다.
|
||||
- **Viewer Header 메뉴 전용 덮어쓰기(2026-11-10, 공유 유지):** 로고 메뉴와 Header 안 Dropdown(메뉴 버튼·More·하위 메뉴)은 `--hm-sw-viewer-header-menu-background`·`-border-color`·`-shadow`·`-item-hover-background`(가장 바깥 SW 범위, 기본값 각각 `var(--hm-sw-header-more-menu-background)`·`var(--hm-sw-header-more-menu-border-color)`·`var(--hm-sw-header-more-menu-shadow)`·`var(--hm-sw-header-more-item-hover-background)`)를 읽습니다. Hover 토큰은 Focus·서브메뉴 열림에도 같습니다. 정하지 않으면 SWHeader `...` 메뉴와 같은 색이고, 정하면 Viewer Header 메뉴만 바뀝니다(SWHeader More 영향 없음). 글자 `--hm-sw-header-text-color`(보조 글자 80%)·구분선 `--hm-sw-header-document-menu-divider-color`·Disabled 0.5는 공유 그대로이고, 단독 `SWViewerMenu`는 위 `--hm-sw-viewer-menu-*`·`--hm-sw-viewer-more-*`를 씁니다. 제약은 면 alias와 같습니다(안쪽 요소에서만 바꾼 `--hm-sw-header-more-*`는 전달되지 않음).
|
||||
|
||||
| 상태 | Header 항목(button·toggle·menu·tabs·switch) | 패널 콘텐츠 |
|
||||
|---|---|---|
|
||||
@@ -216,7 +219,7 @@
|
||||
| Layout 탭 | 높이 22, 12px 자간 -5%(Model 포함, 2026-10-22; 이전 2%), 라벨 700(모든 상태, 2026-10-22; 이름 편집 입력은 400). Default 면 `#C3B79E` 글자 `#161410`. On: `linear-gradient(0deg, #7C6C4C, #7C6C4C 85%, #A6946F)` 흰 글자. 이름 편집: `#0F0D0A` 빨강 `#E60403` 주황 테두리. Hover·On·편집 글자 아래 70% 검정 1.3px 외곽선. 둥근 탭: 1px `rgba(0,0,0,.05)`(Hover·편집 `#DA6813`), 위 radius 4, 안쪽 그림자 `inset 0 -3px 4px rgba(56,48,31,.25)`, ×(16) Hover 노랑. 사선 탭(마지막): 97 × 22 자산(9-slice, 2026-10-22: 97px·라벨 57px가 최소, 라벨이 길면 사선 21px·모서리 4px는 고정하고 가운데만 늘어남, 라벨 최대 12em), On hover = 베이지 면 + 주황 글자, ×(28, 빨강 사선). Hover(선택 아님, 두 모양 모두, 2026-10-20) = Model과 같은 `gra-border-orange` 면(사선 탭은 자산 모양 안)만 바뀌고 글자는 Default 그대로(`#161410` 12px 700, 외곽선 없음, 위치·크기 불변) | `--hm-sw-footer-tab-*`, `--hm-sw-footer-gra-border-orange` |
|
||||
| `+` 버튼 | Layout 옆 `+`(24 × 22) Hover = `gra-border-orange` 면 + `+`(분류 행 `+` 자산). 분류 행 `+`(28 × 12) Hover = `+` 채움 `#FF5C00`(검정 외곽선 그대로) | `--hm-sw-footer-gra-border-orange`, `--hm-sw-footer-accent` |
|
||||
| Model 탭 | 73 × 22 자산(왼쪽 사선, 9-slice: 73px 최소, 긴 라벨이면 사선 21px 고정·가운데만 늘어남, 라벨 최대 12em, 2026-10-22), 글자 왼쪽 22. Hover·On = 자산 모양 안 `gra-border-orange` 면 + 검정 `#161410` 글자(12px, 외곽선 없음)(2026-10-20 사용자 확정, 이전 Figma On 갈색·흰 글자 대체). 라벨은 모든 상태 700(2026-10-22). 앞 사선 탭과 8px 겹침 | `--hm-sw-footer-gra-border-orange` |
|
||||
| 명령창 | 566 × 71 / 270, 위 radius 4, 위 71px `linear-gradient(180deg, #635537, #262218 5%, #281F16 10%, #282216)` + 아래 `#282216`. 12px 자간 10% 줄 14, 1px 외곽선. 입력 칸 23(좌우 4) `#16130D` + `0 -1px 2px rgba(255,255,255,.25)`, `inset 0 1px 4px #000`. 화살표 18 × 18 검정 radius 2(4, 4 오른쪽 위) | `--hm-sw-footer-command-*` |
|
||||
| 명령창 | 566 × 71 / 270, 위 radius 4, 위 71px `linear-gradient(180deg, #635537, #262218 5%, #281F16 10%, #282216)` + 아래 `#282216`. 12px 자간 10% 줄 14, 1px 외곽선. 입력 칸 23(좌우 4) `#16130D` + `0 -1px 2px rgba(255,255,255,.25)`, `inset 0 1px 4px #000`. 화살표 18 × 18 검정 radius 2(4, 4 오른쪽 위). 기록 스크롤바 `thin`, Thumb `#574B30` / Track `transparent`(2026-11-10 개별 토큰, 값 변경 없음) | `--hm-sw-footer-command-*`(스크롤바 `--hm-sw-footer-command-scrollbar-thumb`·`-track`) |
|
||||
| 바 안 명령창(실제 화면) | 보조기능 뒤 4px, 바 아래에 붙어 펼치면 위로 270. 남는 폭(바 − 보조기능 영역 − 명령어 전체보기)을 칸 영역 0.57 : 명령창 나머지(최소 280)로 나눔 — 칸 수는 바 폭으로만 계산, 분류의 명령 수와 무관(1920 → 12칸) | `--hm-sw-footer-command-min-width`, `--hm-sw-footer-slot-share` |
|
||||
| 좌표(실제 화면) | 바 위 왼쪽, 12px `#F6F4F2` 1px 외곽선, 아래 3px | — |
|
||||
| 좁은 폭(실제 화면) | 빈 칸이 먼저 줄고, 넘치는 명령은 마지막 칸 ▲(ico_arrow 18 × 18, 오른쪽 위 4) → 명령 칸 위 패널(같은 칸, 오른쪽 맞춤, 테두리 1px `#886D35`·아래 없음·위 radius 4, 명령 칸 면). 분류 탭 넘침 = 탭 영역 잘림 + ◀ ▶(13 × 16, 6 × 8 삼각형 `#F6F4F2`, Hover `#FF5C00`) + +. 바 1279px 이하 보조기능 아이콘만 45 × 33 | `--hm-sw-footer-aux-compact-width` |
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
- **반응형(2026-10-21, 실제 가용 폭 기준 3단계):** 고정 Breakpoint를 쓰지 않고 Header 행의 실제 남는 폭(로고·파일명·메뉴·Gap·Padding·More 버튼 포함)을 측정합니다. 파일명 면은 글자 수가 아니라 Header 폭으로 정한 기본 폭으로 계산하므로, 같은 폭에서는 짧은 파일명과 긴 파일명의 단계·파일 열기 버튼·파일명 면 너비가 같고 긴 이름은 `…`입니다(2026-10-21). ① 모두 들어가면 기본 형태(아이콘+텍스트), ② 부족하면 `icon`과 `iconOnlyBelow`가 있는 항목을 모두 아이콘만(라벨은 Tooltip·접근성 이름), ③ 그래도 부족하면 `collapseBelow` 항목을 하나씩 More(⋯, 베이지 260px Dropdown)로 옮깁니다(값이 큰 것부터, 같으면 오른쪽 먼저). ③의 판단(2026-10-26)은 tabs를 최소 폭(`--hm-sw-viewer-tabs-min-width`)으로 세므로, tabs가 `…`로 좁아져서 들어가면 More로 옮기지 않습니다. 파일명 면 기본 폭은 `clamp(80px, min(45cqw - 256px, 100cqw - 780px), 360px)`(1280px 이상은 이전과 같음), Header 560px 미만에서는 56px입니다. 넓어지면 More → 아이콘 → 기본 순서로 복귀하고, 상단과 More에 같은 항목이 동시에 나오지 않습니다. `collapseBelow`·`iconOnlyBelow`의 숫자는 px Breakpoint가 아니며 순서만 정합니다(`iconOnlyBelow`는 지정 여부만, `collapseBelow`는 값이 큰 것부터; Props 형식 동일). 로고 옆 파일 열기 버튼(`onDocumentOpen`)은 ② 단계에서 아이콘만으로 줄고 More로 옮기지 않으며, 샘플은 `documents`. 레이어·설정·속성은 `collapseBelow` 없이 아이콘까지만. 1180px 미만 간격 2px, 560px 미만 상태 글자 숨김(CSS container)은 그대로이고, 560px 미만에서는 tabs 개수 숨김·switch `shortLabel` 캡션·switch 좌우 여백 1px도 적용됩니다(2026-10-26). 가로 스크롤은 만들지 않습니다.
|
||||
- More는 그룹/항목 단위 구획으로 자동 구성됩니다: menu·tabs는 자기 구획(`moreLabel`), button·toggle·switch는 그룹 구획(`group.moreLabel ?? group.label`). toggle·switch는 체크 항목, tabs는 라디오 항목이 됩니다.
|
||||
- **Dropdown 색(2026-10-21):** Header 안의 Dropdown — 메뉴 버튼(`type: "menu"`), More(⋯), 로고 메뉴 — 은 크기·간격·글자는 `SWViewerMenu` 그대로, 색은 일반 `SWHeader`의 `...` 메뉴와 같은 More 토큰을 읽습니다(2026-11-10: 면 `--hm-sw-header-more-menu-background` #0e3d2f, Border `--hm-sw-header-more-menu-border-color` = `var(--hm-sw-header-object-border-color)`, 그림자 `--hm-sw-header-more-menu-shadow` 0 4px 12px rgba(0,0,0,.5), 글자 `--hm-sw-header-text-color`, 보조 글자는 같은 색 80%, Hover `--hm-sw-header-more-item-hover-background` = `var(--hm-sw-header-hover)`, 구분선 `--hm-sw-header-document-menu-divider-color`). 파일명 목록(`documents`)은 `SWHeader` 파일명 목록 모양을 유지하고, 단독 `SWViewerMenu`(우클릭 메뉴 등)는 기존 색 그대로입니다.
|
||||
- **Dropdown 색(2026-10-21):** Header 안의 Dropdown — 메뉴 버튼(`type: "menu"`), More(⋯), 로고 메뉴 — 은 크기·간격·글자는 `SWViewerMenu` 그대로, 색은 기본적으로 일반 `SWHeader`의 `...` 메뉴와 같습니다. 2026-11-10부터 면·Border·그림자·Hover는 Viewer 전용 alias를 거쳐 읽습니다 — 면 `--hm-sw-viewer-header-menu-background`(기본 `var(--hm-sw-header-more-menu-background)`, #0e3d2f), Border `--hm-sw-viewer-header-menu-border-color`(기본 `var(--hm-sw-header-more-menu-border-color)` = `var(--hm-sw-header-object-border-color)`), 그림자 `--hm-sw-viewer-header-menu-shadow`(기본 `var(--hm-sw-header-more-menu-shadow)`, 0 4px 12px rgba(0,0,0,.5)), Hover·Focus·서브메뉴 열림 `--hm-sw-viewer-header-menu-item-hover-background`(기본 `var(--hm-sw-header-more-item-hover-background)` = `var(--hm-sw-header-hover)`). 이 4개를 가장 바깥 SW 범위에 정하면 Viewer Header 메뉴만 바뀌고 SWHeader `...` 메뉴는 그대로입니다. 글자 `--hm-sw-header-text-color`, 보조 글자는 같은 색 80%, 구분선 `--hm-sw-header-document-menu-divider-color`는 공유 그대로입니다. 파일명 목록(`documents`)은 `SWHeader` 파일명 목록 모양을 유지하고, 단독 `SWViewerMenu`(우클릭 메뉴 등)는 기존 색 그대로입니다.
|
||||
- **상태 표현:** Hover·Active·Pressed·열림·선택 탭 = 글자·아이콘 #FFC600(면·선택 박스 없음, Hover 전후 크기·위치 동일 — 2026-10-21 Hover 면 #266651 제거), Disabled = 60% 불투명, Focus = 2px #FFC600 외곽선.
|
||||
- **글자·아이콘 외곽선(SWHeader와 동일):** 14px 글자 `--hm-sw-header-text-outline`(검정 1.3px), 11px 캡션·개수·상태 `--hm-sw-header-label-outline`(검정 1px), 아이콘 `SWViewerIcon`(SWHeader 아이콘 자산의 1px 검정 바깥 Stroke를 **SVG stroke**로 재현: 아이콘 뒤 복사본의 모든 도형을 검정, 원래 선 두께 + 화면 기준 양쪽 1px(`--hm-sw-viewer-icon-outline-width`)로 그림. 위 아이콘은 그대로라 색·모양·선 두께 유지. CSS `filter` 미사용, glow는 생략). Header 항목·`SWViewerHeaderButton`의 `icon`은 자동 적용, `custom` / `trailing`의 직접 그린 아이콘은 `<SWViewerIcon>`으로 감쌉니다. 색·크기·굵기·간격은 그대로이고 Hover/Active는 채움 색만 바뀝니다. Dropdown·패널 안에는 외곽선이 없습니다(SWHeader More 메뉴와 같음). 프로그램 아이콘은 `currentColor`로 그리면 상태 색과 외곽선이 자동 적용됩니다.
|
||||
- **면 색(SWHeader와 공유, 2026-11-10 Viewer 전용 덮어쓰기 추가):** 파일명·객체 수정·속성 입력 면과 오른쪽 상태·버튼 띠는 기본으로 SWHeader 토큰(`--hm-sw-header-document-*`·`-object-*`·`-property-*`)을 따릅니다. Viewer Header만 바꾸려면 같은 이름의 `--hm-sw-viewer-document-*`·`--hm-sw-viewer-object-*`·`--hm-sw-viewer-property-*`(10개)를 가장 바깥 SW 범위(`<SWTheme style>` 또는 단독 `SWViewerHeader`의 `style`)에 지정합니다. 안쪽 요소에서 바꾼 `--hm-sw-header-*`는 이 값에 전달되지 않습니다. 목록은 [DESIGN_RULES.md](./DESIGN_RULES.md) §11.
|
||||
|
||||
@@ -94,6 +94,9 @@
|
||||
--hm-sw-footer-command-input-shadow: 0 -1px 2px rgba(255, 255, 255, 0.25), inset 0 1px 4px #000;
|
||||
--hm-sw-footer-command-font-size: 12px;
|
||||
--hm-sw-footer-command-line-height: 14px;
|
||||
/* History scrollbar (thin; 2026-11-10 individual tokens, values as before: the former scrollbar-color literals). */
|
||||
--hm-sw-footer-command-scrollbar-thumb: #574b30;
|
||||
--hm-sw-footer-command-scrollbar-track: transparent;
|
||||
/* Not in Figma (reference screens 2026-10-20): the command line inside the bar keeps at least this width. */
|
||||
--hm-sw-footer-command-min-width: 280px;
|
||||
/* Not in Figma: with the command line inside the bar, the slot area takes this share of the free width (bar minus
|
||||
@@ -613,7 +616,7 @@
|
||||
overflow-y: auto;
|
||||
list-style: none;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #574b30 transparent;
|
||||
scrollbar-color: var(--hm-sw-footer-command-scrollbar-thumb) var(--hm-sw-footer-command-scrollbar-track);
|
||||
text-shadow: var(--hm-sw-header-label-outline);
|
||||
}
|
||||
.hm-sw-footer-command__history > li { white-space: pre; }
|
||||
|
||||
@@ -127,6 +127,17 @@
|
||||
--hm-sw-viewer-property-border-color: var(--hm-sw-header-property-border-color);
|
||||
--hm-sw-viewer-property-radius: var(--hm-sw-header-property-radius);
|
||||
--hm-sw-viewer-property-shadow: var(--hm-sw-header-property-shadow);
|
||||
|
||||
/* Viewer Header menus (2026-11-10): the logo menu and the header dropdowns (menu buttons, More, their submenus) read
|
||||
these copies of the SWHeader `...` More tokens. Each defaults to the More token, so the shared look stays; set
|
||||
--hm-sw-viewer-header-menu-* on the outermost SW scope to change the Viewer Header menus alone (SWHeader More
|
||||
unchanged). Resolved on the outermost scope like the face aliases above. Text color and the divider stay shared
|
||||
(--hm-sw-header-text-color, --hm-sw-header-document-menu-divider-color); a standalone SWViewerMenu keeps
|
||||
--hm-sw-viewer-menu-* / -more-*. */
|
||||
--hm-sw-viewer-header-menu-background: var(--hm-sw-header-more-menu-background);
|
||||
--hm-sw-viewer-header-menu-border-color: var(--hm-sw-header-more-menu-border-color);
|
||||
--hm-sw-viewer-header-menu-shadow: var(--hm-sw-header-more-menu-shadow);
|
||||
--hm-sw-viewer-header-menu-item-hover-background: var(--hm-sw-header-more-item-hover-background);
|
||||
}
|
||||
|
||||
.hm-sw-viewer-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
||||
@@ -225,7 +236,10 @@ button.hm-sw-viewer-header__brand { text-align: left; }
|
||||
min-width: 160px;
|
||||
padding: 4px;
|
||||
border-width: 1px;
|
||||
border-color: var(--hm-sw-viewer-header-menu-border-color);
|
||||
border-radius: var(--hm-sw-viewer-radius);
|
||||
background: var(--hm-sw-viewer-header-menu-background);
|
||||
box-shadow: var(--hm-sw-viewer-header-menu-shadow);
|
||||
color: var(--hm-sw-header-text-color);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
@@ -237,7 +251,7 @@ button.hm-sw-viewer-header__brand { text-align: left; }
|
||||
padding: 0 12px;
|
||||
color: inherit;
|
||||
}
|
||||
.hm-sw-viewer-header__logo-menu .hm-sw-header__more-item:is(:hover, :focus):not(:disabled) { background: var(--hm-sw-header-more-item-hover-background); outline: none; }
|
||||
.hm-sw-viewer-header__logo-menu .hm-sw-header__more-item:is(:hover, :focus):not(:disabled) { background: var(--hm-sw-viewer-header-menu-item-hover-background); outline: none; }
|
||||
.hm-sw-viewer-header__logo-menu .hm-sw-header__more-item:disabled { opacity: 0.5; }
|
||||
/* Title = SWViewerMenu label, directly above its items (one group, no divider); icons = SWViewerMenu icon slot. */
|
||||
.hm-sw-viewer-header__logo-menu .hm-sw-header__more-title { min-height: 0; padding: 6px 12px; font-size: 12px; font-weight: 500; line-height: 18px; }
|
||||
@@ -318,13 +332,13 @@ button.hm-sw-viewer-header__brand { text-align: left; }
|
||||
unchanged on hover. Size, spacing and type stay SWViewerMenu. A standalone SWViewerMenu (context menu) and the
|
||||
document list keep their own colors. */
|
||||
.hm-sw-viewer-header .hm-sw-viewer-menu[data-surface] {
|
||||
border-color: var(--hm-sw-header-more-menu-border-color);
|
||||
background: var(--hm-sw-header-more-menu-background);
|
||||
box-shadow: var(--hm-sw-header-more-menu-shadow);
|
||||
border-color: var(--hm-sw-viewer-header-menu-border-color);
|
||||
background: var(--hm-sw-viewer-header-menu-background);
|
||||
box-shadow: var(--hm-sw-viewer-header-menu-shadow);
|
||||
color: var(--hm-sw-header-text-color);
|
||||
}
|
||||
.hm-sw-viewer-header .hm-sw-viewer-menu .hm-sw-viewer-menu__item:is(:focus, :hover, [data-state="open"]):not([aria-disabled]) {
|
||||
background: var(--hm-sw-header-more-item-hover-background);
|
||||
background: var(--hm-sw-viewer-header-menu-item-hover-background);
|
||||
color: inherit;
|
||||
}
|
||||
.hm-sw-viewer-header .hm-sw-viewer-menu :is(.hm-sw-viewer-menu__label, .hm-sw-viewer-menu__shortcut, .hm-sw-viewer-menu__chevron) { color: inherit; opacity: 0.8; }
|
||||
@@ -880,8 +894,8 @@ button.hm-sw-viewer-header__brand { text-align: left; }
|
||||
border-color: transparent;
|
||||
border-radius: 2px;
|
||||
background: transparent;
|
||||
color: var(--hm-sw-header-input-color);
|
||||
caret-color: var(--hm-sw-header-input-color);
|
||||
color: var(--hm-sw-popup-value-color);
|
||||
caret-color: var(--hm-sw-popup-value-color);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
|
||||
@@ -112,12 +112,21 @@
|
||||
--hm-sw-popup-group-shadow: 0 2px 2px rgba(255, 255, 255, .6), inset 0 2px 4px #e1cfab, inset 0 -2px 2px rgba(104, 95, 76, .8);
|
||||
--hm-sw-popup-divider-color: rgba(0, 0, 0, .5);
|
||||
--hm-sw-popup-table-line-color: rgba(0, 0, 0, .4);
|
||||
/* Popup value (2026-11-10): the red editable value of the property window / SWPopupRow (input, select, SWSelect,
|
||||
dropdown trigger) and of SWViewerPanel inputs. Defaults to the header input color it used, so a header theme still
|
||||
reaches it; set it to change popup values alone (the SWHeader 속성Bar keeps --hm-sw-header-input-color). */
|
||||
--hm-sw-popup-value-color: var(--hm-sw-header-input-color);
|
||||
/* SWHeader `...` More menu (2026-11-10 individual tokens, values as before). The viewer header dropdowns and the
|
||||
viewer logo menu take the same face and shadow. Border and row hover default to the header tokens they used. */
|
||||
viewer logo menu default to the same face and shadow (--hm-sw-viewer-header-menu-*, sw-viewer.css). Border and row
|
||||
hover default to the header tokens they used. */
|
||||
--hm-sw-header-more-menu-background: #0e3d2f;
|
||||
--hm-sw-header-more-menu-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
|
||||
--hm-sw-header-more-menu-border-color: var(--hm-sw-header-object-border-color);
|
||||
--hm-sw-header-more-item-hover-background: var(--hm-sw-header-hover);
|
||||
/* SWHeader K guide preview panel (2026-11-10 individual tokens, values as before): its border defaulted to the
|
||||
property window border, its face was a literal. Text and outline stay the header tokens. */
|
||||
--hm-sw-header-k-guide-border-color: var(--hm-sw-property-border);
|
||||
--hm-sw-header-k-guide-background: #0e3c2e;
|
||||
/* Popup scrollbar (every SW popup body: SWPopup, SWViewerPanel, property window). Same as the DWG/DXF viewer
|
||||
panels (thin, HM warm-neutral-mid thumb on warm-neutral-light track). */
|
||||
--hm-sw-popup-scrollbar-thumb: #9a9284;
|
||||
@@ -1696,16 +1705,19 @@ div.hm-sw-header__menu-row { justify-content: space-between; gap: 8px; padding-l
|
||||
.hm-sw-property__row > label { flex: 0 0 72px; min-width: 0; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.hm-sw-property__value { display: flex; align-items: center; gap: 4px; flex: 1; min-width: 0; }
|
||||
.hm-sw-property__value > .hm-sw-header__input { width: auto; max-width: 100%; min-width: 0; flex: 1; text-shadow: none; letter-spacing: .02em; font-weight: 500; }
|
||||
.hm-sw-property__value > .hm-sw-header__input:focus { color: var(--hm-sw-header-input-color); }
|
||||
.hm-sw-property__value > select:not(.hm-sw-select) { flex: 1; min-width: 0; width: 0; height: 28px; padding: 0; appearance: none; border: 1px solid transparent; border-radius: 2px; background: transparent; color: var(--hm-sw-header-input-color); font: inherit; }
|
||||
.hm-sw-property__value > select:not(.hm-sw-select):disabled { opacity: 1; appearance: none; color: var(--hm-sw-header-input-color); }
|
||||
/* Popup value color (2026-11-10, --hm-sw-popup-value-color): the inputs of the value, gauge value and color value cells
|
||||
(before: the shared .hm-sw-header__input color); read-only values keep the label color (rule below, higher specificity). */
|
||||
:is(.hm-sw-property__value, .hm-sw-property__slider-value, .hm-sw-property__color-value) > .hm-sw-header__input,
|
||||
.hm-sw-property__value > .hm-sw-header__input:focus { color: var(--hm-sw-popup-value-color); caret-color: var(--hm-sw-popup-value-color); }
|
||||
.hm-sw-property__value > select:not(.hm-sw-select) { flex: 1; min-width: 0; width: 0; height: 28px; padding: 0; appearance: none; border: 1px solid transparent; border-radius: 2px; background: transparent; color: var(--hm-sw-popup-value-color); font: inherit; }
|
||||
.hm-sw-property__value > select:not(.hm-sw-select):disabled { opacity: 1; appearance: none; color: var(--hm-sw-popup-value-color); }
|
||||
.hm-sw-property__value > select:not(.hm-sw-select):focus { background: var(--hm-sw-property-content); border-color: var(--hm-sw-property-inner-border); }
|
||||
/* SWSelect: native select, panel field look; focus = the SW popup focus ring. */
|
||||
.hm-sw-select { flex: 0 0 auto; height: 28px; padding: 0 8px; border: 1px solid var(--hm-sw-select-border-color); border-radius: 4px; background: var(--hm-sw-select-background); color: var(--hm-sw-select-text); font: inherit; font-size: 12px; }
|
||||
.hm-sw-select:disabled { opacity: 0.4; cursor: default; }
|
||||
/* 2026-10-21: in an SW popup the chosen value of an enabled select is editable, so it is red like every editable value;
|
||||
a disabled select and the option list keep --hm-sw-select-text. The viewer panels keep their own text color (sw-viewer.css). */
|
||||
.hm-sw-property .hm-sw-select:not(:disabled) { color: var(--hm-sw-header-input-color); }
|
||||
.hm-sw-property .hm-sw-select:not(:disabled) { color: var(--hm-sw-popup-value-color); }
|
||||
.hm-sw-property .hm-sw-select option { color: var(--hm-sw-select-text); }
|
||||
.hm-sw-property__unit { flex: 0 0 auto; font-size: 12px; white-space: nowrap; }
|
||||
/* Color preview (26:12175): 70 x 16 black tile (1px padding, radius 2), 68 x 14 swatch with a white 1px border. */
|
||||
@@ -1915,7 +1927,7 @@ div.hm-sw-header__menu-row { justify-content: space-between; gap: 8px; padding-l
|
||||
align-self: stretch;
|
||||
min-width: 0;
|
||||
padding: 0 7px;
|
||||
color: var(--hm-sw-header-input-color);
|
||||
color: var(--hm-sw-popup-value-color);
|
||||
text-align: left;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
@@ -1948,7 +1960,7 @@ div.hm-sw-header__menu-row { justify-content: space-between; gap: 8px; padding-l
|
||||
|
||||
/* K guide 26:11698 / 26:11669: original image, fixed mask viewport and continuous pan/zoom. */
|
||||
.hm-sw-k-guide { position: relative; flex: 0 0 40px; height: 38px; }
|
||||
.hm-sw-k-guide__panel { z-index: 45; height: 149px; min-height: 149px; overflow: hidden; border: 1px solid var(--hm-sw-property-border); border-radius: 2px; background: #0e3c2e; animation: hm-sw-k-guide-enter 511ms ease-out; }
|
||||
.hm-sw-k-guide__panel { z-index: 45; height: 149px; min-height: 149px; overflow: hidden; border: 1px solid var(--hm-sw-header-k-guide-border-color); border-radius: 2px; background: var(--hm-sw-header-k-guide-background); animation: hm-sw-k-guide-enter 511ms ease-out; }
|
||||
.hm-sw-k-guide__viewport { position: absolute; top: 6px; left: 6px; width: 192px; height: 109px; mask-size: 192px 109px; mask-repeat: no-repeat; outline: 1px solid #000; }
|
||||
.hm-sw-k-guide__viewport > img { display: block; max-width: none; width: 456px; height: 256.5px; transform-origin: 0 0; transform: translate(0, 0) scale(1); transition-property: transform; transition-timing-function: ease-in-out; }
|
||||
.hm-sw-k-guide__panel[data-frame="2"] .hm-sw-k-guide__viewport > img { transform: translate(0, -40.738px) scale(1); }
|
||||
|
||||
@@ -274,6 +274,118 @@ async function dropdownColors(page, def, themed) {
|
||||
check('Viewer Header 토큰은 SWHeader·Step Header 화면에 영향 없음', untouched.every(item => item.includes(' same ')), untouched.join(', '))
|
||||
}
|
||||
|
||||
// ---------- 1d. Component tokens (2026-11-10): viewer header menus, K guide, popup value, footer command scrollbar ----------
|
||||
// Each defaults to the token / literal it replaced (same value), follows a shared token set on the outermost SW scope,
|
||||
// and when set itself changes its component alone.
|
||||
{
|
||||
const setOn = (page, root, values) => page.locator(root).first().evaluate((element, values) => {
|
||||
let outer = element
|
||||
for (let e = element; e; e = e.parentElement) if (e.matches?.('.hm-sw-header, .hm-sw-theme')) outer = e
|
||||
Object.entries(values).forEach(([token, value]) => value === null ? outer.style.removeProperty(token) : outer.style.setProperty(token, value))
|
||||
}, values)
|
||||
const C1 = 'rgb(44, 55, 66)', C2 = 'rgb(1, 2, 3)'
|
||||
const flow = async (label, page, root, read, shared, own, expect) => {
|
||||
const base = await read()
|
||||
await setOn(page, root, Object.fromEntries(shared.map(token => [token, C1])))
|
||||
const inherited = await read()
|
||||
await setOn(page, root, Object.fromEntries(own.map(token => [token, C2])))
|
||||
const mine = await read()
|
||||
await setOn(page, root, Object.fromEntries([...shared, ...own].map(token => [token, null])))
|
||||
const back = await read()
|
||||
check(`${label}: 기본값 = 기존 값`, JSON.stringify(base) === JSON.stringify(expect), JSON.stringify(base))
|
||||
check(`${label}: 바깥 범위의 공유 토큰을 따름`, Object.values(inherited).every(value => value.includes(C1)), JSON.stringify(inherited))
|
||||
check(`${label}: 전용 토큰 지정이 우선, 지우면 복귀`, Object.values(mine).every(value => value.includes(C2)) && JSON.stringify(back) === JSON.stringify(base), JSON.stringify(mine))
|
||||
}
|
||||
// Viewer Header menus: a header dropdown (도면) and the logo menu (no Story renders `logoMenu`: the same class
|
||||
// structure is appended to the header, so the real rules apply).
|
||||
const viewer = await newPage()
|
||||
await open(viewer, 'sw-viewer--dropdowns')
|
||||
await viewer.locator('.hm-sw-viewer-header').first().evaluate(header => {
|
||||
const wrap = document.createElement('div'); wrap.className = 'hm-sw-viewer-header__logo-menu hm-sw-header__more'
|
||||
wrap.innerHTML = '<div class="hm-sw-header__more-menu" data-probe=""><button type="button" class="hm-sw-header__more-item" data-visible="true">probe</button></div>'
|
||||
header.append(wrap)
|
||||
})
|
||||
await viewer.getByRole('button', { name: '도면' }).first().click()
|
||||
const menu = viewer.locator('.hm-sw-viewer-header .hm-sw-viewer-menu[data-surface]').first()
|
||||
await menu.locator('.hm-sw-viewer-menu__item:not([aria-disabled])').first().hover()
|
||||
const readMenu = () => viewer.evaluate(() => {
|
||||
const m = getComputedStyle(document.querySelector('.hm-sw-viewer-header .hm-sw-viewer-menu[data-surface]'))
|
||||
const hovered = [...document.querySelectorAll('.hm-sw-viewer-header .hm-sw-viewer-menu .hm-sw-viewer-menu__item')].find(item => item.matches(':hover, :focus'))
|
||||
const logo = getComputedStyle(document.querySelector('[data-probe]'))
|
||||
return { face: `${m.backgroundColor} | ${m.borderTopColor} | ${m.boxShadow}`, hover: hovered ? getComputedStyle(hovered).backgroundColor : 'none', logo: `${logo.backgroundColor} | ${logo.borderTopColor} | ${logo.boxShadow}` }
|
||||
})
|
||||
const header = await newPage()
|
||||
await open(header, 'sw-header--overview')
|
||||
// Expected defaults: the SWHeader More menu face (same tokens) — computed on a probe More menu inside SWHeader.
|
||||
const moreFace = await header.locator('.hm-sw-header').first().evaluate(root => {
|
||||
const wrap = document.createElement('div'); wrap.className = 'hm-sw-header__more'
|
||||
wrap.innerHTML = '<div class="hm-sw-header__more-menu"></div>'; root.append(wrap)
|
||||
const s = getComputedStyle(wrap.firstChild); const value = `${s.backgroundColor} | ${s.borderTopColor} | ${s.boxShadow}`; wrap.remove(); return value
|
||||
})
|
||||
const hoverDefault = 'rgba(255, 255, 255, 0.08)'
|
||||
await flow('Viewer Header 메뉴', viewer, '.hm-sw-viewer-header', readMenu,
|
||||
['--hm-sw-header-more-menu-background', '--hm-sw-header-more-menu-border-color', '--hm-sw-header-more-menu-shadow', '--hm-sw-header-more-item-hover-background'],
|
||||
['--hm-sw-viewer-header-menu-background', '--hm-sw-viewer-header-menu-border-color', '--hm-sw-viewer-header-menu-shadow', '--hm-sw-viewer-header-menu-item-hover-background'],
|
||||
{ face: moreFace, hover: hoverDefault, logo: moreFace })
|
||||
// The shadow token is a full shadow: the C1 / C2 color strings above are not valid shadows, so the shadow keeps its
|
||||
// value there; check it once with a real shadow.
|
||||
await setOn(viewer, '.hm-sw-viewer-header', { '--hm-sw-viewer-header-menu-shadow': `0 0 0 3px ${C2}` })
|
||||
const shadow = await readMenu()
|
||||
await setOn(viewer, '.hm-sw-viewer-header', { '--hm-sw-viewer-header-menu-shadow': null })
|
||||
check('Viewer Header 메뉴: 그림자 전용 토큰', shadow.face.endsWith(`${C2} 0px 0px 0px 3px`) && shadow.logo.endsWith(`${C2} 0px 0px 0px 3px`), JSON.stringify(shadow))
|
||||
await viewer.close()
|
||||
// K guide panel (hover opens it).
|
||||
const guide = await newPage()
|
||||
await open(guide, 'sw-header--k-guide')
|
||||
await guide.locator('.hm-sw-k-guide button').first().hover()
|
||||
await guide.locator('.hm-sw-k-guide__panel').first().waitFor()
|
||||
const readGuide = () => guide.evaluate(() => { const s = getComputedStyle(document.querySelector('.hm-sw-k-guide__panel')); return { border: s.borderTopColor, face: s.backgroundColor } })
|
||||
// Shared token: the border follows --hm-sw-property-border; the face was a literal (own token only).
|
||||
const guideBase = await readGuide()
|
||||
await setOn(guide, '.hm-sw-header', { '--hm-sw-property-border': C1 })
|
||||
const guideShared = await readGuide()
|
||||
await setOn(guide, '.hm-sw-header', { '--hm-sw-header-k-guide-border-color': C2, '--hm-sw-header-k-guide-background': C2 })
|
||||
const guideOwn = await readGuide()
|
||||
await setOn(guide, '.hm-sw-header', { '--hm-sw-property-border': null, '--hm-sw-header-k-guide-border-color': null, '--hm-sw-header-k-guide-background': null })
|
||||
check('K 가이드: 기본값 = 기존 값(#A67D03 / #0E3C2E)', guideBase.border === 'rgb(166, 125, 3)' && guideBase.face === 'rgb(14, 60, 46)', JSON.stringify(guideBase))
|
||||
check('K 가이드: 바깥 범위의 --hm-sw-property-border를 따름', guideShared.border === C1 && guideShared.face === guideBase.face, JSON.stringify(guideShared))
|
||||
check('K 가이드: 전용 토큰 지정이 우선, 지우면 복귀', guideOwn.border === C2 && guideOwn.face === C2 && JSON.stringify(await readGuide()) === JSON.stringify(guideBase), JSON.stringify(guideOwn))
|
||||
await guide.close()
|
||||
// Popup value color: property window inputs (value, gauge value), dropdown triggers, and the SWHeader 속성Bar untouched.
|
||||
const popup = await newPage()
|
||||
await open(popup, 'sw-header--property-panel-compare')
|
||||
const readValue = () => popup.evaluate(() => {
|
||||
const color = selector => [...new Set([...document.querySelectorAll(selector)].map(e => getComputedStyle(e).color))].join(',')
|
||||
return {
|
||||
input: color('.hm-sw-property__value:not([data-readonly]) > .hm-sw-header__input:not(:read-only)'),
|
||||
gauge: color('.hm-sw-property__slider-value:not([data-readonly]) > .hm-sw-header__input:not(:read-only)'),
|
||||
trigger: color('.hm-sw-property__value:not([data-readonly]) .hm-sw-property__trigger'),
|
||||
}
|
||||
})
|
||||
await flow('Popup 값 글자색', popup, '.hm-sw-property', readValue, ['--hm-sw-header-input-color'], ['--hm-sw-popup-value-color'],
|
||||
{ input: 'rgb(230, 4, 3)', gauge: 'rgb(230, 4, 3)', trigger: 'rgb(230, 4, 3)' })
|
||||
await popup.close()
|
||||
const bar = await newPage()
|
||||
await open(bar, 'sw-header--overview')
|
||||
const barColor = () => bar.evaluate(() => [...document.querySelectorAll('.hm-sw-property-bar .hm-sw-header__input, .hm-sw-property-bar__value')].map(e => getComputedStyle(e).color).join(','))
|
||||
const barBefore = await barColor()
|
||||
await setOn(bar, '.hm-sw-header', { '--hm-sw-popup-value-color': C2 })
|
||||
check('Popup 값 토큰은 SWHeader 속성Bar 값에 영향 없음', barBefore.length > 0 && barBefore === await barColor(), barBefore)
|
||||
await bar.close()
|
||||
// Footer command history scrollbar.
|
||||
const footer = await newPage()
|
||||
await open(footer, 'sw-footer--full-footer')
|
||||
const readBar = () => footer.evaluate(() => ({ scrollbar: getComputedStyle(document.querySelector('.hm-sw-footer-command__history')).scrollbarColor }))
|
||||
const footerBase = await readBar()
|
||||
await setOn(footer, '.hm-sw-footer-command', { '--hm-sw-footer-command-scrollbar-thumb': C2, '--hm-sw-footer-command-scrollbar-track': C1 })
|
||||
const footerOwn = await readBar()
|
||||
await setOn(footer, '.hm-sw-footer-command', { '--hm-sw-footer-command-scrollbar-thumb': null, '--hm-sw-footer-command-scrollbar-track': null })
|
||||
check('Footer 명령창 스크롤바: 기본값 = 기존 값(#574B30 / transparent)', footerBase.scrollbar === 'rgb(87, 75, 48) rgba(0, 0, 0, 0)', JSON.stringify(footerBase))
|
||||
check('Footer 명령창 스크롤바: 전용 토큰 지정이 우선, 지우면 복귀', footerOwn.scrollbar === `${C2} ${C1}` && JSON.stringify(await readBar()) === JSON.stringify(footerBase), JSON.stringify(footerOwn))
|
||||
await footer.close()
|
||||
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) *)'
|
||||
|
||||
Reference in new issue
Block a user