feat(sw): bold 9-slice footer tabs and viewer panel bottom edge

User-approved 2026-10-22 SW changes (CSS only, public API unchanged).
- Footer Model / Layout tab labels 700 in every state, letter-spacing -5%;
  the name-edit input keeps 400.
- Slanted Layout tab and Model tab draw their SVG assets as 9-slice
  border-images: 97px / 73px stay the minimum, the slant and corners keep
  their size and only the flat middle grows with a longer label (max 12em,
  then ellipsis, like the rounded tabs). Hover surfaces use a union mask of
  the same asset (no mask-border), so Chromium and Firefox match. The
  default width renders pixel-identical to the previous drawing; the 8px
  slant / Model joint is unchanged.
- Viewer panel body: the 7px bottom padding (inside the scrollport) becomes
  a 2px transparent bottom border, so scrolled rows stop 2px above the
  frame border instead of running into it. SWPopup keeps its 7px.
- AI docs: DESIGN_RULES, AI_USAGE, FOOTER, VIEWER updated.
- Tests: text-weight and footer-design-review expect the 700 labels and read
  the Model surface from the 9-slice ::before.
This commit is contained in:
shkim4 committed 2026-10-22 15:32:07 +09:00
1 parent 449398e3de
commit fc65b2e836
8 files changed
+88 -36

No files matched your search

+68 -26
View File
@@ -424,10 +424,13 @@
height: 22px;
min-width: 69px;
padding: 0 4px 0 8px;
/* -5% (2026-10-22 user-set, was the row's 2%): the Bold labels fit the fixed 57px slanted label box. */
letter-spacing: -0.05em;
white-space: nowrap;
line-height: 14px;
}
.hm-sw-footer-tab__label { display: block; }
/* Model / Layout labels are Bold in every state (2026-10-22 user request); the name-edit input keeps 400. */
.hm-sw-footer-tab__label { display: block; font-weight: 700; }
.hm-sw-footer-tab__input {
width: 57px;
height: 16px;
@@ -469,33 +472,55 @@
/* Hover, not selected (2026-10-20, user request): the Model orange surface; text as on Model (below). */
.hm-sw-footer-tab[data-shape="rounded"][data-state="off"]:hover { background: var(--hm-sw-footer-gra-border-orange); }
/* Slanted tab (layout01, next to Model): 97 × 22 mirrored bg asset + the 28px red close piece. */
.hm-sw-footer-tab[data-shape="slant"] { width: 97px; }
.hm-sw-footer-tab[data-shape="slant"] .hm-sw-footer-tab__label { width: 57px; overflow: hidden; text-overflow: ellipsis; }
/* Slanted tab (layout01, next to Model): 97 × 22 mirrored bg asset + the 28px red close piece.
9-slice (2026-10-22): 97px / 57px label stay the minimum and the tab grows with a longer label. Each state asset is
drawn as a border-image: the 21px slant and the 4px corner keep their size, only the flat middle stretches (the
asset gradients are vertical, so stretching changes nothing else). Border-image works in every target browser;
mask-border (Firefox: none) is not used. */
.hm-sw-footer-tab[data-shape="slant"] { min-width: 97px; }
.hm-sw-footer-tab[data-shape="slant"] .hm-sw-footer-tab__label { min-width: 57px; max-width: 12em; overflow: hidden; text-overflow: ellipsis; }
.hm-sw-footer-tab[data-shape="slant"]::before,
.hm-sw-footer-tab[data-shape="slant"]::after {
content: "";
position: absolute;
inset: 0;
box-sizing: border-box;
transform: scaleX(-1);
background: url("../components/sw-footer/assets/tab-slant.svg") 0 0 / 97px 22px no-repeat;
border: solid transparent;
border-width: 0 4px 0 21px;
border-image: url("../components/sw-footer/assets/tab-slant.svg") 0 4 0 21 fill / 0 4px 0 21px stretch;
pointer-events: none;
}
.hm-sw-footer-tab[data-shape="slant"]::after { z-index: 2; background-image: none; }
.hm-sw-footer-tab[data-shape="slant"]::after { z-index: 2; border-image-source: none; }
.hm-sw-footer-tab[data-shape="slant"] .hm-sw-footer-tab__remove { width: 28px; background: url("../components/sw-footer/assets/tab-close.svg") 0 0 / 28px 22px no-repeat; }
.hm-sw-footer-tab[data-shape="slant"] .hm-sw-footer-tab__remove:hover { background-image: url("../components/sw-footer/assets/tab-close-hover.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="on"]::before { background-image: url("../components/sw-footer/assets/tab-slant-on.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="editing"]::before { background-image: url("../components/sw-footer/assets/tab-slant-editing.svg"); }
/* Hover, not selected (2026-10-20, user request): the Model orange surface in the slanted shape. */
.hm-sw-footer-tab[data-shape="slant"][data-state="on"]::before { border-image-source: url("../components/sw-footer/assets/tab-slant-on.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="editing"]::before { border-image-source: url("../components/sw-footer/assets/tab-slant-editing.svg"); }
/* Hover, not selected (2026-10-20, user request): the Model orange surface in the slanted shape. The shape mask is the
union of the asset at both ends (slant end, corner end) and a plain middle, so it follows any width without
mask-border. */
.hm-sw-footer-tab[data-shape="slant"][data-state="off"]:hover::before {
background: var(--hm-sw-footer-gra-border-orange);
-webkit-mask: url("../components/sw-footer/assets/tab-slant.svg") 0 0 / 97px 22px no-repeat;
mask: url("../components/sw-footer/assets/tab-slant.svg") 0 0 / 97px 22px no-repeat;
border-image-source: none;
/* Longhands: a var() shorthand followed by background-origin does not survive CSSOM re-serialization. */
background-image: var(--hm-sw-footer-gra-border-orange);
background-origin: border-box;
-webkit-mask:
url("../components/sw-footer/assets/tab-slant.svg") 0 0 / 97px 22px no-repeat,
url("../components/sw-footer/assets/tab-slant.svg") 100% 0 / 97px 22px no-repeat,
linear-gradient(#000 0 0) 21px 0 / calc(100% - 25px) 22px no-repeat;
mask:
url("../components/sw-footer/assets/tab-slant.svg") 0 0 / 97px 22px no-repeat,
url("../components/sw-footer/assets/tab-slant.svg") 100% 0 / 97px 22px no-repeat,
linear-gradient(#000 0 0) 21px 0 / calc(100% - 25px) 22px no-repeat;
-webkit-mask-origin: border-box;
mask-origin: border-box;
-webkit-mask-clip: border-box;
mask-clip: border-box;
}
.hm-sw-footer-tab[data-shape="slant"][data-state="off"]:hover::after { background-image: url("../components/sw-footer/assets/tab-slant-ring.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="off"]:hover::after { border-image-source: url("../components/sw-footer/assets/tab-slant-ring.svg"); }
/* layout01 Variant6 (editing + hover): the fill without its own ring and the orange ring on top. */
.hm-sw-footer-tab[data-shape="slant"][data-state="editing"]:hover::before { background-image: url("../components/sw-footer/assets/tab-slant-editing-fill.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="editing"]:hover::after { background-image: url("../components/sw-footer/assets/tab-slant-ring.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="editing"]:hover::before { border-image-source: url("../components/sw-footer/assets/tab-slant-editing-fill.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="editing"]:hover::after { border-image-source: url("../components/sw-footer/assets/tab-slant-ring.svg"); }
.hm-sw-footer-tab[data-shape="slant"] + .hm-sw-footer-tab--model { margin-left: -9px; }
/* Text per state (Figma layout01 / layout02 variants). */
@@ -518,26 +543,43 @@
.hm-sw-footer-tab:is([data-shape="rounded"], [data-shape="slant"])[data-state="off"]:hover,
.hm-sw-footer-tab--model:is([data-state="on"], :hover) { color: var(--hm-sw-footer-tab-text); }
/* layout01 "on hover" draws the default beige surface under the orange text. */
.hm-sw-footer-tab[data-shape="slant"][data-state="on"]:hover::before { background-image: url("../components/sw-footer/assets/tab-slant.svg"); }
.hm-sw-footer-tab[data-shape="slant"][data-state="on"]:hover::before { border-image-source: url("../components/sw-footer/assets/tab-slant.svg"); }
/* The × appears on hover (all shapes) and in no other state (Figma opacity 0). */
.hm-sw-footer-tab:hover .hm-sw-footer-tab__remove { opacity: 1; }
.hm-sw-footer-tab .hm-sw-footer-tab__remove:disabled { opacity: 0; cursor: default; }
/* Model (26:9914): 73 × 22 asset, label 22px from the left. */
.hm-sw-footer-tab--model { width: 73px; background: url("../components/sw-footer/assets/tab-model.svg") 0 0 / 73px 22px no-repeat; }
/* Selected and hovered (2026-10-20, user request; was the brown model On 26:9925): the gra-border-orange surface in the
Model shape. */
.hm-sw-footer-tab--model:is([data-state="on"], :hover) { background-image: none; }
.hm-sw-footer-tab--model:is([data-state="on"], :hover)::before {
/* Model (26:9914): 73 × 22 asset, label 22px from the left. 9-slice (2026-10-22, see the slanted tab): 73px stays the
minimum; the 21px slant and the 1px right edge keep their size, the flat middle stretches with a longer label. */
.hm-sw-footer-tab--model { min-width: 73px; }
.hm-sw-footer-tab--model::before {
content: "";
position: absolute;
inset: 0;
background: var(--hm-sw-footer-gra-border-orange);
-webkit-mask: url("../components/sw-footer/assets/tab-model.svg") 0 0 / 73px 22px no-repeat;
mask: url("../components/sw-footer/assets/tab-model.svg") 0 0 / 73px 22px no-repeat;
box-sizing: border-box;
border: solid transparent;
border-width: 0 1px 0 21px;
border-image: url("../components/sw-footer/assets/tab-model.svg") 0 1 0 21 fill / 0 1px 0 21px stretch;
pointer-events: none;
}
.hm-sw-footer-tab--model .hm-sw-footer-tab__select { min-width: 0; width: 100%; padding: 0 16px 0 22px; }
/* Selected and hovered (2026-10-20, user request; was the brown model On 26:9925): the gra-border-orange surface in the
Model shape (union mask: the asset at the slant end + a plain rest, square right end). */
.hm-sw-footer-tab--model:is([data-state="on"], :hover)::before {
border-image-source: none;
background-image: var(--hm-sw-footer-gra-border-orange);
background-origin: border-box;
-webkit-mask:
url("../components/sw-footer/assets/tab-model.svg") 0 0 / 73px 22px no-repeat,
linear-gradient(#000 0 0) 21px 0 / calc(100% - 21px) 22px no-repeat;
mask:
url("../components/sw-footer/assets/tab-model.svg") 0 0 / 73px 22px no-repeat,
linear-gradient(#000 0 0) 21px 0 / calc(100% - 21px) 22px no-repeat;
-webkit-mask-origin: border-box;
mask-origin: border-box;
-webkit-mask-clip: border-box;
mask-clip: border-box;
}
.hm-sw-footer-tab--model .hm-sw-footer-tab__select { flex: 1 0 auto; min-width: 0; padding: 0 16px 0 22px; }
.hm-sw-footer-tab--model .hm-sw-footer-tab__label { max-width: 12em; overflow: hidden; text-overflow: ellipsis; }
/* ---------- Command line ---------- */
.hm-sw-footer-command {
+4
View File
@@ -552,6 +552,10 @@ button.hm-sw-viewer-header__brand { text-align: left; }
/* ---------- Panel (SWPopup frame + viewer window behavior) ---------- */
.hm-sw-viewer-panel-backdrop { inset: 0; background: var(--hm-sw-viewer-panel-overlay); }
.hm-sw-viewer-panel[data-collapsed] .hm-sw-property__body { display: none; }
/* Bottom gutter outside the scrollport (2026-10-22, user-set 2px): the SWPopup body's 7px bottom padding is inside the
scroll area, so scrolled rows ran into it up to the frame border. A transparent border instead clips the list
2px above the frame border. */
.hm-sw-viewer-panel .hm-sw-property__body { padding-bottom: 0; border-bottom: 2px solid transparent; }
.hm-sw-viewer-panel[data-draggable] .hm-sw-property__titlebar { cursor: grab; touch-action: none; user-select: none; }
.hm-sw-viewer-panel[data-dragging] .hm-sw-property__titlebar { cursor: grabbing; }
/* Any width: the Figma title art keeps its 308px curve at the close button (right-aligned; at 314px this is exactly