feat(sw): refine headers, status label, popups and AI guide rules
Deploy Storybook / deploy (push) Successful in 1m10s
Deploy Storybook / deploy (push) Successful in 1m10s
- SW Header: the five field previews hide and return together in one measured step; from 760 to 1920px the groups from 레이어 to 치수스타일 share the room left before 속성보기, above 1920px they keep the 1920 widths (controls bars and < 760px unchanged) - SW Viewer Header Model / Layout: long names ellipsize in equal buttons (minmax(0, 1fr), name counted at --hm-sw-viewer-tab-name-basis) so they never change the box or the measured steps; green outer outline and its tokens removed; 378px / 42px, text-shadow and state unchanged - SWViewerStatus: the label keeps the width of its widest real state text (idle keeps its icon slot), so a state change never moves the header; with too little room it ellipsizes after the file name gives way - Popups: body inset shadow removed, title SVG drop shadow removed, viewer panel title icons use --hm-sw-viewer-panel-icon-color (#c7bda9) - Storybook: SW/Viewer › Model / Layout story removed - AI guide (docs/ai + package copies): design-only changes for every component, content limits for Submodule consumers (Header excluded), Submodule usage rules; VIEWER / DESIGN_RULES synced - viewer.browser.mjs: explicit 1920 / 1440 labels and approved 1280 icon-only expectations
This commit is contained in:
1 parent
325ec25e37
commit
db03e155dd
19 files changed
+190
-119
No files matched your search
@@ -34,10 +34,7 @@
|
||||
--hm-sw-viewer-tabs-height: calc(var(--hm-sw-viewer-control-height) + 2px);
|
||||
--hm-sw-viewer-tabs-radius: 6px;
|
||||
--hm-sw-viewer-tabs-background: rgba(0, 0, 0, 0.8);
|
||||
/* Outline: a 1px line just outside the frame, blurred (Figma Rectangle 9333: stroke outside + layer blur 2). */
|
||||
--hm-sw-viewer-tabs-outline-color: #349f7f;
|
||||
--hm-sw-viewer-tabs-outline-blur: 1px;
|
||||
/* Extra glow around the frame: none in Figma (the blurred outline is the glow); set a color to add one. */
|
||||
/* Extra glow around the frame: none by default (the green outline was removed on 2026-10-23); set a color to add one. */
|
||||
--hm-sw-viewer-tabs-glow-color: transparent;
|
||||
--hm-sw-viewer-tabs-glow: 0 0 4px var(--hm-sw-viewer-tabs-glow-color);
|
||||
--hm-sw-viewer-tabs-shadow: inset 0 2.857px 2.857px rgba(0, 0, 0, 0.25);
|
||||
@@ -45,6 +42,9 @@
|
||||
--hm-sw-viewer-tab-radius: 5px;
|
||||
--hm-sw-viewer-tab-border-width: 1.428px;
|
||||
--hm-sw-viewer-tab-gap: 14px;
|
||||
/* Width a tab name counts for in the space calculation (2026-10-23): a longer name measures as this (about the
|
||||
default "Model"), so it never claims more room, takes free room when there is some and ellipsizes otherwise. */
|
||||
--hm-sw-viewer-tab-name-basis: 3em;
|
||||
--hm-sw-viewer-tab-background: #0e3d2f;
|
||||
--hm-sw-viewer-tab-gradient: linear-gradient(180deg, #081813 0%, #09271e 8.6884%, #0b3227 25.428%, #0e3d2f 51.252%, #0b3227 80.027%, #0a2b21 89.759%, #081813 100%);
|
||||
/* Selected (Model in Figma) / not selected (Layout in Figma). Borders are images (Figma gradient strokes, top to
|
||||
@@ -105,6 +105,9 @@
|
||||
--hm-sw-viewer-panel-badge-background: var(--hm-sw-property-table);
|
||||
--hm-sw-viewer-panel-overlay: rgba(0, 0, 0, 0.1);
|
||||
--hm-sw-viewer-panel-spacing: 12px;
|
||||
/* Title-bar icon buttons of viewer panels (collapse, `ghost` header actions; 2026-10-23). Popup-only: the header
|
||||
status text and switch captions keep --hm-sw-viewer-muted. SWPopup's own title icons are images. */
|
||||
--hm-sw-viewer-panel-icon-color: #c7bda9;
|
||||
}
|
||||
|
||||
.hm-sw-viewer-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
||||
@@ -160,7 +163,9 @@ button.hm-sw-viewer-header__brand { text-align: left; }
|
||||
.hm-sw-viewer-header__document {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 0 1 clamp(140px, 25cqw, 360px);
|
||||
/* Very large shrink weight (2026-10-23): the file name gives way first, as before; the object (shrink 1) only once
|
||||
the file name is at its minimum (see .hm-sw-viewer-header__object). */
|
||||
flex: 0 1000000 clamp(140px, 25cqw, 360px);
|
||||
width: clamp(220px, 25cqw, 360px);
|
||||
min-width: 56px;
|
||||
height: calc(var(--hm-sw-viewer-height) - 2px);
|
||||
@@ -318,7 +323,10 @@ button.hm-sw-viewer-header__brand { text-align: left; }
|
||||
.hm-sw-viewer-header__object {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1 0 auto;
|
||||
/* Shrinkable (2026-10-23): the file name still gives way first (its huge shrink weight, down to its minimum); only
|
||||
when nothing is left after the measured steps does the object give way, so the status label ellipsizes instead of
|
||||
the header overflowing. The measurement (data-measuring) keeps flex: 0 0 auto. */
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
height: calc(var(--hm-sw-viewer-height) - 2px);
|
||||
gap: var(--hm-sw-viewer-gap);
|
||||
@@ -399,34 +407,25 @@ button.hm-sw-viewer-header__brand { text-align: left; }
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: 1fr;
|
||||
/* minmax(0, 1fr) (2026-10-23): equal columns that may shrink below their content, so a long name never pushes the
|
||||
box past its group (max 378px) or makes the buttons unequal; the name ellipsizes instead (below). */
|
||||
grid-auto-columns: minmax(0, 1fr);
|
||||
min-width: 0;
|
||||
align-items: stretch;
|
||||
gap: 1px;
|
||||
box-sizing: border-box;
|
||||
height: var(--hm-sw-viewer-tabs-height);
|
||||
/* The transparent 1px edge is the room of the blurred outline (it stays inside the box height); the padding is the
|
||||
black margin around the buttons, which fill the rest. 1.5px at the bottom (2026-10-23 user value): the outline
|
||||
blur covers part of a 1px bottom margin, while the inner shadow darkens the top, so 1px read as no room below. */
|
||||
/* The transparent 1px edge was the room of the outline (removed 2026-10-23, user request); it stays so the box and
|
||||
button sizes do not change. The padding is the black margin around the buttons, which fill the rest; 1.5px at the
|
||||
bottom (2026-10-23 user value). */
|
||||
padding: 1px 1px 1.5px 1px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: calc(var(--hm-sw-viewer-tabs-radius) + 1px);
|
||||
background: var(--hm-sw-viewer-tabs-background) padding-box;
|
||||
box-shadow: var(--hm-sw-viewer-tabs-glow);
|
||||
}
|
||||
/* The outline (blurred, on the frame edge) and the inner shadow lie over the buttons, as the last Figma layers do. */
|
||||
.hm-sw-viewer-header__tabs::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
/* The outer edge of the box (its border box, over the transparent 1px edge row): same size as the tabs box. */
|
||||
inset: -1px;
|
||||
border: 1px solid var(--hm-sw-viewer-tabs-outline-color);
|
||||
border-radius: inherit;
|
||||
filter: blur(var(--hm-sw-viewer-tabs-outline-blur));
|
||||
/* The blur may soften 1px past that edge, no further (2026-10-23 user decision). */
|
||||
clip-path: inset(-1px round calc(var(--hm-sw-viewer-tabs-radius) + 1px));
|
||||
pointer-events: none;
|
||||
}
|
||||
/* The inner shadow lies over the buttons, as the last Figma layer does. (The blurred green outline, ::before, was
|
||||
removed on 2026-10-23 at the user's request, with its outline color / blur tokens.) */
|
||||
.hm-sw-viewer-header__tabs::after { content: ""; position: absolute; z-index: 1; inset: 0; border-radius: var(--hm-sw-viewer-tabs-radius); box-shadow: var(--hm-sw-viewer-tabs-shadow); pointer-events: none; }
|
||||
/* A group holding only the tabs: the container is its face (no field face around it, as in Figma: the tabs sit on
|
||||
the header object surface). */
|
||||
@@ -474,7 +473,23 @@ button.hm-sw-viewer-header__brand { text-align: left; }
|
||||
.hm-sw-viewer-header .hm-sw-viewer-header__tab[data-state="active"] { --hm-sw-viewer-tab-border-face: var(--hm-sw-viewer-tab-active-border); color: var(--hm-sw-viewer-tab-active-text); }
|
||||
.hm-sw-viewer-header__count { font-size: var(--hm-sw-viewer-label-size); font-weight: 400; font-variant-numeric: tabular-nums; }
|
||||
/* In a tab the count has the name's size (Figma: 14px Regular) and color. */
|
||||
.hm-sw-viewer-header__tab .hm-sw-viewer-header__count { font-size: inherit; }
|
||||
.hm-sw-viewer-header__tab .hm-sw-viewer-header__count { flex: 0 0 auto; font-size: inherit; }
|
||||
/* Long names (2026-10-23 user decision): the name gives way first with an ellipsis, the count keeps its width; the
|
||||
outline may paint 2px outside the clipped name (as the file name). The name counts at most
|
||||
--hm-sw-viewer-tab-name-basis in the tabs' natural width (width + max-width: max-content: shorter names keep their own
|
||||
width), so a long name changes neither the tabs' width nor the measured steps of the other items; it grows (flex-grow,
|
||||
up to its text) only into room the box has anyway. */
|
||||
.hm-sw-viewer-header .hm-sw-viewer-header__tab { min-width: 0; }
|
||||
.hm-sw-viewer-header__tab-name {
|
||||
flex: 1 1 auto;
|
||||
width: var(--hm-sw-viewer-tab-name-basis);
|
||||
max-width: max-content;
|
||||
min-width: 0;
|
||||
overflow: clip;
|
||||
overflow-clip-margin: var(--hm-sw-header-text-outline-room);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Switch fields (2.5D, 어두운 배경): 11px muted caption above the switch. */
|
||||
.hm-sw-viewer-header__switch-field {
|
||||
@@ -533,7 +548,22 @@ button.hm-sw-viewer-header__brand { text-align: left; }
|
||||
font-weight: 400;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.hm-sw-viewer-status__icon { display: inline-flex; width: var(--hm-sw-viewer-icon-size); height: var(--hm-sw-viewer-icon-size); }
|
||||
.hm-sw-viewer-status__icon { display: inline-flex; flex: 0 0 auto; width: var(--hm-sw-viewer-icon-size); height: var(--hm-sw-viewer-icon-size); }
|
||||
/* Stable status width (2026-10-23): the empty idle icon slot keeps its place; the label is one grid cell holding the
|
||||
shown text and, hidden, every other text it can show (SWViewerStatus), so its width is the widest real candidate in
|
||||
the real font. With too little room (after the measured steps) it shrinks and the text ellipsizes; with room again
|
||||
the full text returns. */
|
||||
.hm-sw-viewer-status__icon[data-empty] { visibility: hidden; }
|
||||
.hm-sw-viewer-status__label { display: inline-grid; grid-template-columns: minmax(0, auto); flex: 0 1 auto; min-width: 0; }
|
||||
.hm-sw-viewer-status__label > * {
|
||||
grid-area: 1 / 1;
|
||||
min-width: 0;
|
||||
overflow: clip;
|
||||
overflow-clip-margin: var(--hm-sw-header-text-outline-room);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.hm-sw-viewer-status__sizer { visibility: hidden; }
|
||||
.hm-sw-viewer-status[data-state="error"] { color: var(--hm-sw-viewer-accent); }
|
||||
.hm-sw-viewer-status__spin { animation: hm-sw-viewer-spin 1s linear infinite; }
|
||||
@keyframes hm-sw-viewer-spin { to { transform: rotate(360deg); } }
|
||||
@@ -753,7 +783,7 @@ button.hm-sw-viewer-header__brand { text-align: left; }
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
color: var(--hm-sw-viewer-muted);
|
||||
color: var(--hm-sw-viewer-panel-icon-color);
|
||||
}
|
||||
.hm-sw-viewer-panel :is(.hm-sw-viewer-panel__icon-button, .hm-sw-viewer-button[data-variant="ghost"]):hover:not(:disabled) { background: transparent; color: var(--hm-sw-header-selected-color); }
|
||||
.hm-sw-viewer-panel :is(.hm-sw-viewer-panel__icon-button, .hm-sw-viewer-button[data-variant="ghost"]):focus-visible { outline: 2px solid var(--hm-sw-header-selected-color); outline-offset: -2px; }
|
||||
|
||||
@@ -1637,7 +1637,7 @@ div.hm-sw-header__menu-row { justify-content: space-between; gap: 8px; padding-l
|
||||
.hm-sw-property__close-hover { visibility: hidden; }
|
||||
.hm-sw-property__close:hover > .hm-sw-property__close-default { visibility: hidden; }
|
||||
.hm-sw-property__close:hover > .hm-sw-property__close-hover { visibility: visible; }
|
||||
.hm-sw-property__body { display: flex; flex-direction: column; gap: 8px; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 7px 7px 7px; box-shadow: inset 0 2px 2px rgba(255,255,255,.6); }
|
||||
.hm-sw-property__body { display: flex; flex-direction: column; gap: 8px; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 7px 7px 7px; }
|
||||
/* Popup scrollbar: the body and any scrolling area inside it (zero-specificity so content can still override). */
|
||||
.hm-sw-property__body, .hm-sw-property__body :where(*) { scrollbar-width: thin; scrollbar-color: var(--hm-sw-popup-scrollbar-thumb) var(--hm-sw-popup-scrollbar-track); }
|
||||
/* Popup toggle switch (Figma toggle_btn_new 633:75188 / 633:75902): one face for every switch inside an SW popup —
|
||||
@@ -1948,7 +1948,7 @@ div.hm-sw-header__menu-row { justify-content: space-between; gap: 8px; padding-l
|
||||
< 1800 the 그리기모드 text hides; fields take their content width and shrink with an ellipsis, each by its own width.
|
||||
< 1080 window move area 88 -> 24 (window 240 -> 176), field insets 6px.
|
||||
Measured (2026-10-22 user request, SWHeader data-compact): from 760px to 1800px, while the object area lacks
|
||||
room the field pictures hide one at a time — 치수 아이콘, 선 그림, 글꼴명, 색상 칩, 레이어 상태 아이콘 — then the file
|
||||
room the field pictures hide together — 레이어 상태 아이콘, 색상 칩, 선 그림, 글꼴명, 치수 아이콘 — then the file
|
||||
name gives way (310 -> 96px), then the values ellipsize. With room to spare nothing hides.
|
||||
<= 900 the 선스타일 field shows 선스타일 only (가중치 / 스케일 hide).
|
||||
Below 760px (phones, e.g. 390) the former tiers are unchanged: parts move into More (선/문자/치수 스타일, 그리기모드,
|
||||
@@ -1994,6 +1994,16 @@ div.hm-sw-header__menu-row { justify-content: space-between; gap: 8px; padding-l
|
||||
/* 1610 - 1301 + the tighter object area with titles, without 그리기모드 (891, measured) = 1200. */
|
||||
.hm-sw-header__document { flex-basis: clamp(96px, calc(100cqw - 1200px - var(--hm-sw-header-leading-width, 0px)), 310px); }
|
||||
}
|
||||
/* Free room (2026-10-23 user request): from 760px (속성보기 shown) up to a 1920px header the groups from 레이어 to
|
||||
치수스타일 share the room left before 속성보기 (each grows by the same amount from its own width, its field box with
|
||||
it), so no gap stays there. Wider headers keep the 1920 widths (234 / 192 / 284 / 164 / 164) and the extra room stays
|
||||
before 속성보기 (its auto margin). The standard bar only: a `controls` bar ([data-configured]) keeps its own layout.
|
||||
The measured steps read the natural widths (data-measuring sets flex-grow 0), so hiding is unchanged. */
|
||||
@container hm-sw-header (min-width: 760px) and (max-width: 1920px) {
|
||||
.hm-sw-header__fields:not([data-configured]) { flex-grow: 1; }
|
||||
.hm-sw-header__fields:not([data-configured]) > :is(.hm-sw-header__layer, .hm-sw-header__menu, .hm-sw-header__field),
|
||||
.hm-sw-header__fields:not([data-configured]) :is(.hm-sw-header__layer, .hm-sw-header__menu) > .hm-sw-header__field { flex-grow: 1; }
|
||||
}
|
||||
@container hm-sw-header (min-width: 760px) and (max-width: 1079.98px) {
|
||||
/* Values need the room (1024px, 2026-10-21 user request): insets 6px, every value part may shrink (ellipsis); the
|
||||
pictures hide by measurement (data-compact). */
|
||||
@@ -2151,13 +2161,11 @@ div.hm-sw-header__menu-row { justify-content: space-between; gap: 8px; padding-l
|
||||
/* ...and the file name gives way only down to the default design's smallest width (96px, see the clamps above). */
|
||||
.hm-sw-header[data-leading="open"] .hm-sw-header__fields[data-configured] > .hm-sw-header__control[data-control="document"] { min-width: 96px; }
|
||||
|
||||
/* Measured pictures (SWHeader data-compact, 2026-10-22 user request): from 760px to 1800px the field pictures hide one at a
|
||||
time while the object area lacks room — 1 치수 아이콘, 2 선 그림, 3 글꼴명, 4 색상 칩, 5 레이어 상태 아이콘. */
|
||||
.hm-sw-header__fields:is([data-compact="1"], [data-compact="2"], [data-compact="3"], [data-compact="4"], [data-compact="5"]) .hm-sw-header__dimension-icon,
|
||||
.hm-sw-header__fields:is([data-compact="2"], [data-compact="3"], [data-compact="4"], [data-compact="5"]) .hm-sw-header__linetype,
|
||||
.hm-sw-header__fields:is([data-compact="3"], [data-compact="4"], [data-compact="5"]) .hm-sw-header__font-name,
|
||||
.hm-sw-header__fields:is([data-compact="4"], [data-compact="5"]) .hm-sw-header__chip,
|
||||
.hm-sw-header__fields[data-compact="5"] .hm-sw-header__field--layer .hm-sw-header__layer-icons { display: none; }
|
||||
/* Measured pictures (SWHeader data-compact): from 760px to 1800px, while the object area lacks room, the field pictures
|
||||
hide together in one step (2026-10-23 user request; formerly one at a time): 레이어 상태 아이콘, 색상 칩, 선 그림,
|
||||
글꼴명, 치수 아이콘. They come back together once all of them fit. */
|
||||
.hm-sw-header__fields[data-compact="1"] :is(.hm-sw-header__dimension-icon, .hm-sw-header__linetype, .hm-sw-header__font-name, .hm-sw-header__chip),
|
||||
.hm-sw-header__fields[data-compact="1"] .hm-sw-header__field--layer .hm-sw-header__layer-icons { display: none; }
|
||||
|
||||
/* Measured More (SWHeader, 2026-10-19): on top of the breakpoints, parts move into More while the object area has no
|
||||
room for them and return when it has. data-measuring is set for one synchronous layout read and removed before
|
||||
|
||||
Reference in new issue
Block a user