Scale stage 1: everything the UI sizes with is now rem

Groundwork for the continuous interface scale (a root font-size lever): every
size the UI derives from must be root-relative, or it freezes while the rest
grows. Gate for this commit: at the default 16px root the app renders
pixel-identical to before — every conversion is exact (11px = 0.6875rem …).

- The 8 panel-chrome tokens in styles.css: px -> rem, and the data-text
  compact/large tiers are DELETED — a continuous scale replaces discrete
  tiers. Same for --w7-fs (12px -> 0.75rem) and the win7 --radius (5px ->
  0.3125rem, still landing rounded-md exactly on --w7-el-bdr's 3px).
  --radius-sm/md subtrahends: px -> rem, or corners would sharpen as the UI
  grew.
- All 93 text-[11px]/text-[10px] arbitraries -> rem equivalents, plus the two
  inline padding:24px empty-state cells in the 7like file panel.
- 7like.css gains a scale bridge in skinfix: 7.css is 370 px values with zero
  rem, and --w7-font reaches only 15 rules' TYPOGRAPHY — under a root lever
  win7 text would grow inside boxes frozen at 23px/14px/15px. The bridge
  re-expresses 7.css's size vars and its hardcoded box metrics (buttons via
  .w7-btn only, inputs, table rows/headers, title-bar controls, menu items,
  tabs, the 10pt check glyph) in rem at identical scale-1 pixels. 1px
  hairlines and focus rects stay px deliberately — crisp at any scale, and
  the tr border exists precisely to stop selection shifting rows.
- Two skinfix traps found while writing it, one by reasoning and one by
  pixels: (a) NO blanket `.win7 button` restatement — skinfix outranks the
  skin-layer neutralizer that strips 7.css's 75x23 metrics off the ~84 icon
  buttons, so a blanket rule would re-inflate them all; (b) restating a base
  rule in skinfix also outranks 7.css's own VARIANTS of it by layer — the
  title-bar rule silently shrank the close button from 48px to 29px until the
  harness pixel diff caught it, and the active tab's padding-bottom needed
  the same treatment. Both variants are restated alongside their base rules.
- TS px constants that mirror rem CSS are the latent-bug half of this work:
  both context menus' flip/clamp math converts through a new @core/metrics
  remPx() helper reading the LIVE root font-size (the shadlike menu's
  MENU_W=224 diverged from its w-56 box the moment scale != 1; the 7like
  menu's [style.width.px] pinned the box at 200px while text grew — it now
  binds width in rem, so math and box cannot disagree). The editors'
  cachedCharWidth is now keyed on the computed font string, self-invalidating
  under any font change, and both || px fallbacks derive from the element's
  font size instead of assuming a 16px root.

Verified: tsc + build clean; modern and win7 live captures and the harness
all diff to zero against pre-change baselines in their opaque regions (the
harness to exactly zero end to end; modern's only delta is the native GTK
titlebar focus state; win7's is the translucent Aero corner over the
desktop).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-16 18:02:41 +02:00
co-authored by Claude
parent c8ccc6b918
commit 9f2a076e08
21 changed files with 330 additions and 181 deletions
+126 -23
View File
@@ -74,12 +74,13 @@
:root.win7 {
/* Real Segoe first (Windows), bundled substitute everywhere else. */
--w7-family: 'Segoe UI', 'SegoeUI', 'Noto Sans App', system-ui, sans-serif;
/* ~7.css's 9pt, but in px so it tracks the interface-size axis below.
Restating the `font` SHORTHAND (not just the family) is deliberate:
7.css applies `font: var(--w7-font)` on 12 component rules, and the
shorthand also resets weight and line-height — pinning 400/1.4 here stops
those rules from flattening the app's `font-medium` / leading. */
--w7-fs: 12px;
/* ~7.css's 9pt, in REM so it follows the interface scale (the root
font-size lever) like every other size in the app — 0.75rem = 12px at the
16px root. Restating the `font` SHORTHAND (not just the family) is
deliberate: 7.css applies `font: var(--w7-font)` on 15 component rules,
and the shorthand also resets weight and line-height — pinning 400/1.4
here stops those rules from flattening the app's `font-medium` / leading. */
--w7-fs: 0.75rem;
--w7-font: 400 var(--w7-fs) / 1.4 var(--w7-family);
/* Surface ramp, Win7 Explorer. This INVERTS the modern ramp's direction:
@@ -104,21 +105,15 @@
--destructive-foreground: #ffffff;
--success: #06b025; /* the Win7 progress-bar green */
/* 5px, NOT 3px: `rounded-md` is 74 of the 112 radius utilities in the
templates and compiles to calc(var(--radius) - 2px), so 5 lands it exactly
on --w7-el-bdr (3px). --radius itself feeds `rounded-lg` (12 uses) at 5px,
near the 6px window radius. Bare `rounded` (26 uses) is a Tailwind literal
0.25rem and is unaffected. */
--radius: 5px;
/* 0.3125rem = 5px at the 16px root — NOT 3px: `rounded-md` is 74 of the 112
radius utilities in the templates and compiles to
calc(var(--radius) - 0.125rem), so 5 lands it exactly on --w7-el-bdr (3px).
--radius itself feeds `rounded-lg` (12 uses) at 5px, near the 6px window
radius. Bare `rounded` (26 uses) is a Tailwind literal 0.25rem. Rem so the
corners follow the interface scale with everything else. */
--radius: 0.3125rem;
}
/* Interface size: Win7 shipped this exact axis ("Smaller / Medium / Larger"),
so scaling is more authentic than disabling it. One indirection covers all
three tiers because var substitution is lazy. The --row-h / --list-font
block further up scales the file list independently and is untouched. */
:root.win7[data-text='compact'] { --w7-fs: 11px; }
:root.win7[data-text='large'] { --w7-fs: 13px; }
/* Light values for every 7.css literal the skinfix rules below restate through
vars (7.css hardcodes these outside its var system). The values are 7.css's
own, verbatim — light must render exactly as stock. Dark overrides live in
@@ -420,12 +415,120 @@ html.win7 body {
/* Above `skin`, for rules that must state a value rather than defer to a
neighbouring layer. */
@layer skinfix {
/* ---- Scale bridge: re-express 7.css's px metrics in rem ----
7.css sizes everything in px (370 of them, zero rem) and its --w7-font
shorthand reaches only 15 rules' TYPOGRAPHY — the boxes stay frozen. Under
the interface scale (root font-size lever) win7 text would grow inside
23px buttons, 14px table rows and a 15px-line-height caption. Every value
below is 7.css's own, converted at the 16px root (23px -> 1.4375rem …),
so scale 1.0 renders pixel-identical to stock and every other scale keeps
the boxes proportional to the text.
Deliberately NOT converted: 1px hairlines and focus rects (the tr border
that stops selection shifting rows, .is-cursor's 1px dotted outline, menu
bevels, the frame's inset glass ring) — those stay crisp at any scale. */
/* 7.css's own size vars — defined on .win7 in px; rem lands on the same
pixels at scale 1. Covers checkbox/radio/slider/tooltip/treeview/menu
gutters and both window metrics in one shot. */
.win7 {
--w7-cb-size: 0.875rem; /* 14px */
--w7-rd-size: 0.875rem;
--w7-rdd-size: 0.5rem; /* 8px */
--w7-rdl-space: 0.375rem; /* 6px */
--w7-sdt-w: 0.625rem; /* 10px */
--w7-sdt-h: 1.125rem; /* 18px */
--w7-blt-size: 1.125rem;
--w7-tvb-size: 0.5rem;
--w7-tv-left: 1.25rem; /* 20px */
--w7-clc-size: 0.3125rem; /* 5px */
--w7-lvc-size: 0.3125rem;
--w7-mn-left: 1.75rem; /* 28px */
--w7-el-bdr: 0.1875rem; /* 3px */
--w7-w-space: 0.375rem; /* 6px */
--w7-w-bdr: 0.375rem;
}
/* The hardcoded box metrics the vars do not reach, restated in rem; skinfix
outranks skin by layer, so these win over 7.css.
NO blanket `.win7 button` rule here, deliberately: the `skin`-layer
neutralizer strips 7.css's 75x23 metrics off every button (most are icon
squares sized by utilities), and a skinfix restatement would outrank that
neutralizer by LAYER and re-inflate all of them. The only buttons that
legitimately wear Win7 push-button metrics are .w7-btn (opted in below)
and the title-bar controls (which beat the neutralizer on specificity
inside `skin` and are restated here). */
.win7 input[type='email'],
.win7 input[type='number'],
.win7 input[type='password'],
.win7 input[type='search'],
.win7 input[type='text'],
.win7 input[type='url'] {
height: 1.4375rem;
}
.win7 input[type='email'],
.win7 input[type='number'],
.win7 input[type='password'],
.win7 input[type='search'],
.win7 input[type='text'],
.win7 input[type='url'],
.win7 textarea {
padding: 0.1875rem 0.25rem 0.3125rem; /* 3px 4px 5px */
}
.win7 table > thead > tr > * {
height: 1.375rem; /* 22px */
padding: 0 0.5rem; /* 0 8px */
}
.win7 table > tbody > tr > * {
height: 0.875rem; /* 14px */
padding: 0.125rem 0.5rem; /* 2px 8px */
}
.win7 .title-bar-text {
line-height: 0.9375rem; /* 15px */
}
.win7 .title-bar-controls button {
min-height: 1.1875rem; /* 19px */
min-width: 1.8125rem; /* 29px */
padding: 0;
}
/* BOTH of 7.css's wide-close selectors, not just .is-close: the app's title
bars key the buttons on aria-label, and the base rule above outranks the
`skin`-layer variant BY LAYER — restating only one arm shrank the close
button from 48px to 29px (caught by the harness pixel diff). */
.win7 .title-bar-controls button.is-close,
.win7 .title-bar-controls button[aria-label='Close'] {
min-width: 3rem; /* 48px */
}
.win7 ul[role='menu'] {
min-width: 9.375rem; /* 150px */
padding: 0.125rem; /* 2px */
}
.win7 ul[role='menu'] > [role='menuitem'] > a,
.win7 ul[role='menu'] > [role='menuitem'] > button,
.win7 ul[role='menu'] > [role='menuitem'] > label {
padding: 0.25rem 0.625rem 0.25rem 2rem; /* 4px 10px 4px 32px */
}
.win7 menu[role='tablist'] button {
padding: 0.125rem 0.375rem; /* 2px 6px */
}
/* The raised active tab: 7.css gives it padding-bottom 4px at (0,2,2) inside
`skin`, but the shorthand above outranks that BY LAYER and would flatten
the raise — so the variant is restated here too. */
.win7 menu[role='tablist'] button[aria-selected='true'] {
padding-bottom: 0.25rem; /* 4px */
}
/* The 10pt checkmark glyph in menu checkables — one of five pt sizes in
7.css that bypass --w7-font entirely. 10pt = 13.333px ≈ 0.8333rem. */
.win7 ul [role='menuitem'] > input[type='checkbox']:checked + label:after {
font-size: 0.8333rem;
}
/* Opt back IN to real Win7 push-button metrics, for the dialog and options
buttons where they are right. */
buttons where they are right. Same rem values as the button rule above —
this class exists to undo the `skin`-layer neutralizer, not to differ. */
.win7 .w7-btn {
min-width: 75px;
min-height: 23px;
padding: 0 12px;
min-width: 4.6875rem;
min-height: 1.4375rem;
padding: 0 0.75rem;
}
/* ---- ListView (file panels) ----