42 KiB
P4 CSS foundation: evidence log
Status
Maintenance session (2026-10-07 11:40, platform maintainer): REQ Coordinator->P4 (PLAN §1.18 A14, round R2) done in docs/phase2/DESIGN2.md: §3.2 (quiet legend's dim band on window routes, R2-1; the ornament hugs its members and the laser Sort & Filter pill is the toolbar's trailing group, R2-2, also §7.3), §3.7 (menu box y 108–616, Cancel 60 with its own E-MENU line, count table 6 / 7–10 / 11–14, value menus follow it, E-MENU ≥ 320 / ≥ 280, R2-5; alert and sheet placement on the glass, outside click and default focus as recorded deviations, R2-6), §3.8 (source card 0 with a glow, R2-3; dimming from SG-5's measurement, R2-4); §0.3 register now A1–A14. FD-7 re-run below. No token or CSS change (R2-7 needed none). Open REQs to P4: none.
Complete, R1 review fixed (2026-10-07 10:08 local, session 3). Every review finding is fixed and re-tested live, or answered (table "R1 review fixes" below); every acceptance test FD-1 to FD-7 still passes; all REQs to P4 answered (P9's DESIGN2 one this session). M1 and M3 were reached in sessions 1 and 2.
- M1 (G-PERF) fixed: the hook rules' subjects are generated flat host lists (
:where(<host>)::after|::before, one entry per host, per family and per kind) instead of*::after/*::before. Scroll A/B on/library/tab/AllGames: listed hooks −1.5 % fps against no hooks, the old universal subject −27 %. The generator ispython docs/phase2/fontkit.py --hooks(any package may run it after adding a hook selector;--hooks-checkexits 1 when stale); the gate probe ispython docs/phase2/fontkit.py --perf-js(contract §2). The whole theme is still ~25 % under stock on that route, which is not P4's share (noted for the coordinator, C2c, P10). - M2 fixed: the FocusRing plate paints nothing when the focused element is an
--lgs-illhost (generated:has(.gpfocus:where(<hosts' whole selectors>))); live on Settings › Notifications: no plate on C4a's segments, label#0d0e12. A dark crescent at the selected segment's ends is C4a's fill clip (REQ P4->C4a). - Minors: m1 lobe arc on row/nav (ratio 0.89 → 0.00), m2 lens contract corrected, m3 SteamVR ring tokens ring-free + documented, m4
--lgs-t-*= P5 tokens (G-MOTION 0 findings), m5 mode-keyed in-transitions, m6 HC dial +--lgs-mat-window-tint(REQ C1a), m7 P-03 measured and tuned (--lgs-row-hover-add.06,--lgs-nav-hover-spot.16; all +12.8..+23.1 L), m8 window rim dims under a scrim only, m9 DESIGN2, m10 kit, m11, m12 (REQ V1), m13 scratch deleted. Plus: nav hover was invisible in High Contrast (now +19.6 L). - Open, outside P4: REQ P4->C1a (HC window tint), P4->C4a (segment fill shape), P4->C5b and P4->C4b (SteamVR hover-rim restatements), P4->P10 (check-theme warns on stale hook lists), P4->V1 (input-mode default). A next P4 session: answer new REQs; rerun
fontkit.py --hooks-checkand--perf-jsif areas add many hook selectors.
Session 2 summary (still true unless the R1 table says otherwise):
Complete (2026-10-07 06:13 local, session 2). M1 reached in session 1; M3 at 05:40; every acceptance test FD-1 to FD-7 now PASS on the live device. P4 is CSS-only (T1, no runtime module), so M2/M3 mean tokens, font, materials and states complete and verified live in both input modes (pad: G-FOCUS; laser: a stale .gpfocus and Steam's FocusRing paint nothing).
- Tokens
theme/00-tokens.nowrap.css: D2 §16 + PLAN §1, focus add .32 final (P4-D8), selection recipe, edge lobe, T1 materials--lgs-mat-*,--lgs-white-glow22/8/.55 with parts,--lgs-text-on-color,--lgs-fill-danger,--lgs-shadow-5mm; state numbers and hook switches registered (--lgs-edge-z); Phase 1 aliases kept (motion aliases follow P5). - Font
theme/01-font.nowrap.css(generated bydocs/phase2/fontkit.py), live in every Steam window and, through P8's binary FontFace load, in the SteamVR pages. - Materials
theme/03-material.css:--lgs-edgehook (conic + lobe, rim ×1.5 on focus,--lgs-edge-z, the window's rim above its header),.lgs-glass[data-lgs-mat], plate, media dim, scroll edges, High Contrast stroke. - States
theme/04-states.css: input-mode keyed state numbers,--lgs-illhook (raised, recessed, row, nav, white, card), FocusRing light plate (beats Phase 1's ring; glow 22/6/.44;opacity: 0in laser mode), disabled focus, Reduce Motion, High Contrast (outer stroke on white fills). - SteamVR
theme/vr/00-vr-tokens.css; kit parity tool (fontkit.py --parity) and kit/theme parity; DESIGN2 A1–A13 (§0.3).
Tests: FD-1, FD-2 (Steam windows and SteamVR pages), FD-3, FD-4 (window, CONFIRM alert and fixture, with P4's material applied in a test step: C1a and C1c have not adopted the hooks yet), FD-5 (C4a's live primitives on /zoo/buttons and /zoo/toggles), FD-6, FD-7: all PASS. All REQs to P4 answered (C1a ×2, C1c, C3a, C4a, P3, P5).
Open at the end of session 2 (since resolved): C1a removed vr/10-systemui.css §0, C4a removed Phase 1's §10 ring, and C1a/C1c set --lgs-edge (FD-4 integrated passes, review R1). Still open: the check-circle bloom needs P3's .lgs-ring-check tag (only for check circles without a hook). Areas' mockups that use the kit's is-focus / lgk-glass / lgk-menu / liquid glass render differently since this session (contract §8): re-render before cmp. A next session only answers new REQs.
Decisions taken without the user (PLAN §1.17 rule: safe default, noted)
| # | Decision | Why |
|---|---|---|
| P4-D1 | Phase 1 names that Phase 2 redefines with the same meaning (radii, colours, text levels, tints, selected fill, scrim, well shadow, separator .08) take the Phase 2 value; every other Phase 1 name keeps its Phase 1 value | D2 §16 says "values replace the Phase 1 tokens of the same name"; these changes only move looks toward the target |
| P4-D2 | --lgs-glass-bg, --lgs-thick-bg, --lgs-glass-blur, --lgs-thick-blur keep Phase 1 values; Phase 2 material values live under new names --lgs-mat-<m>-bg/-blur/-shade (D2 §16 amended) |
Phase 1 files use them inside linear-gradient(x, x) and background-color, where D2's new gradient values would be invalid and make glass transparent |
| P4-D3 | T1 window tint keeps the Phase 1 dial formula .60 + dial × .24 (.72 at the default dial, range .60–.84); PLAN §1.2's ".74" is inside the range |
.72 is the value Phase 1's CONTRAST audits passed with; a dial of .58 gives .74 |
| P4-D4 | Panel tint .67 + dial × .22 (= .78 at the default dial, PLAN §1.6) |
Matches the plan's default exactly |
| P4-D5 | The illumination layer uses normal alpha compositing, not plus-lighter |
VP D-2's "+48 L for .28" is normal compositing; a blend mode on every hooked control would force isolated groups (GPU cost) |
| P4-D6 | Hooks (--lgs-ill, --lgs-edge, --lgs-scroll-band) are CSS style queries on the host's ::before / ::after. R1: the subject is a generated host list, not * (see the R1 table: the universal subject doubled the style-recalc cost of the poster grid) |
Areas cannot add classes to Steam nodes in T1; CSS has no mixins, and a custom-property "recipe" cannot carry per-element state (a var() inside a custom property is substituted where it is declared). Proven on CEF 126 (SQ-1); cost measured in R1 |
| P4-D7 | The kit/theme token parity check (FD-3) is a mode of P4's own tool: python docs/phase2/fontkit.py --parity |
PLAN §2.6 gives P4 no tools/ path; fontkit.py is P4's tool file. It compares every kit --lg-X with its theme twin --lgs-X (00-tokens + P5's 02-motion) after normalising number spelling |
| P4-D8 | Final --lgs-focus-add = .32 (top of .28–.32). First set at .28 (every criterion passed on P4's fixture, min P-14 +45.7 L on L 85 glass), then raised after REQ C1a: on the tab bar over glass at L ~95 (inside D2's 55–110 band) .28 gives P-14 +35.2 and P-15 +7.6 (FAIL), .30 +39.1 / +11.6 (FAIL), .32 +42.5 / +15.1 (PASS) |
The focus look must pass G-FOCUS on every glass in the band, and focus vs the .18 selection only has the headroom above the glass; .32 is the plan's ceiling. Glare is bounded: the add is uniform over controls, never over large areas |
| P4-D9 | --lgs-white-glow = 22/8/.55 (C3a's measured value) instead of PLAN §1.4's 18/2/.30 |
PLAN's literal measures +4 to +12 L in the P-16 band (C1c, C3a); 22/8/.55 measures +25 to +49 L live (fixture) |
| P4-D10 | The kit's illumination layer switched from plus-lighter to normal compositing and the theme's values; kit edges use the E3 lobe |
Parity with the theme (FD-3) and DESIGN2 A4/A5. Mockups that use is-focus render brighter (.32 instead of .14) and need a re-render before cmp (changelog in contracts/tokens.md §8) |
| P4-D11 | The FocusRing plate is written at html.lgs-on.lgs-on and its glow is the white-glow token at 3/4 spread and 4/5 alpha (22/6/.44), not CTL §4.5's 18/2/.22 |
Phase 1's §10 ring (C4a's file, later in the bundle, same specificity) still drew the ring; CTL's glow measured +8 L in P-16's band on a white radio segment, 22/6/.44 measures +34 L |
| P4-D12 | --lgs-edge-z (registered, non-inherited) controls the edge layer's stacking; window defaults to 20000 |
The window's content (header, art) covered a ::before edge (FD-4 first try); Phase 1's window rim used z 20000 for the same reason; non-inherited so a window's value never reaches the controls inside |
| P4-D13 | --lgs-focus-arc stays .55 (PLAN §1.4's "arc ×1.5" of the .22 rest arc would be .33); on row and nav kinds the arc is drawn as the E3 lobe (26 % of the width, fading both ways) instead of a uniform inset line |
The review's finding (m1) is the uniform full-width line on wide rows; the lobe removes it. On buttons and circles (≤ ~300 px) the .55 arc reads as the control's lit lip, and five area files use the token directly in looks their reviews accepted (31-cc, 32-launcher, 41-home, vr/10-systemui); changing the value would move their looks without a measured reason |
| P4-D14 | Any package may run python docs/phase2/fontkit.py --hooks (contract §2): it rewrites only the generated lgs-hosts blocks of 03-material.css / 04-states.css, deterministically from theme/ |
A new hook selector paints nothing until it is listed; waiting for a P4 session would block areas. The blocks are machine output (no hand edits), so a run by another package cannot conflict with P4's work. --hooks-check (REQ P4->P10 for check-theme) reports staleness |
Evidence
Steam build 11094443 (Chrome/126.0.6478.183 in the popups).
| Test | Command | Result | Date |
|---|---|---|---|
| SQ-1 style queries on pseudo-elements in CEF 126 | python glass.py js "<scratch probe: a test <style> + 3 off-screen divs in main, removed in finally>" |
p4a:"Q"/0.5 | p4c:none/1 | p4f:"Q"/1 | p4b:none/1: ::after queries its originating element; a non-inherited registered property reaches the pseudo through inherit; children are not matched. linear() and plus-lighter supported. Probe nodes removed (verified) |
2026-10-07 |
| M1 load | python glass.py check-theme; python glass.py sync; python glass.py reload |
check-theme … PASS; unresolved: [], ambiguous: [], no skipped files |
2026-10-07 |
| M1 CSSOM | python glass.py js "<count CSSContainerRule / CSSPropertyRule in the lgs sheet of main; read --lgs-* on .gpfocus>" |
16 container rules, 9 @property rules parsed; .gpfocus element has --lgs-focus: 1, --lgs-lift: 1; --lgs-focus-add: .28 |
2026-10-07 |
| REQ glow + Reduce Motion aliases | python glass.py check-theme; python glass.py sync; python glass.py reload |
check-theme … PASS; unresolved: [], ambiguous: [] |
2026-10-07 05:10 |
| FD-2 Steam windows | python glass.py js "$(cat fd2.js)" (scratch probe: per window document.fonts.load/check('500 24px "LGS Inter"'), FontFace status, canvas widths, computed font-family of the first visible label and of a form control, size of the 01-font.nowrap.css section in the live <style>) |
PASS on main, bar, barpopup, keyboard, notifications, plus tooltip, frame.menu, floatingfooter, volumelevel: face loaded, check true, no load error (so no CSP block); Latin width 276.4 (Inter) vs 289.7 (Motiva): Inter is used; labels and INPUT compute "LGS Inter", "Motiva Sans", Arial, sans-serif; CJK 日本語のテキスト中文한국어 measures 306.2 with our stack = 306.2 with Steam's stack (falls through to Steam's CJK font; tofu would be 152.6); font section in the bundle 163,530 bytes (main bundle 481,450) |
2026-10-07 05:12 |
| FD-2 perf | python glass.py js "$(cat fd2perf.js)": on /library/home, L.perf('main', 4000) scrolling the largest scroller, alternating with a temporary override style that sets Steam's stack on everything (removed in finally, verified) |
font 87.5 / 89.0 / 90.0 fps, no font 89.0 / 86.8 / 87.4 fps; median frame 11.1 ms in all six; mean +1.3 % with the font: PASS (within 5 %) | 2026-10-07 05:14 |
| FD-2 SteamVR pages | the same probe --in vr:systemui, --in vr:controllerbindingui; CSP read with fetch(location.href) |
BLOCKED (P8): face status error (NetworkError): the pages' CSP default-src 'self' 'unsafe-eval' blocks font-src data:. Text falls back to Steam's stack (nothing breaks). A binary new FontFace(…, bytes) of the same data loads (loaded, width 276.4; probe face deleted again). REQ P4->P8 |
2026-10-07 05:13 |
| G-FOCUS (PLAN §1.4) on the P4 state fixture | python glass.py shot main p2_p4_states_<backing> --route /library/home --back --mode pad --settle 1.5 --pre "(window.__P4FX = {base: 'rgb(B B B)'}, <fixture>)" for B = 134 (bright lounge), 51 (dim room), 250 (glass at L 85); the fixture is a .lgs-glass[data-lgs-mat=window] slab with 20 hooked controls (--lgs-ill kinds, real .gpfocus class, inline --lgs-hover/--lgs-press), removed by its own 20 s timer; luma pairs with P10's tools/p2/edge_profile.py (lum, region_mean, 601 weights, pills inset 6 shot px) |
PASS on all three at focus add .28: P-14 raised +54.5 / +60.8 / +45.7, recessed +71.7 / +76.8 / +64.0, row pill +66.5 / +74.1 / +55.6; P-15 focused vs selected nav +30.7 / +33.7 / +25.8, selected+focused vs selected +56.3 / +62.6 / +47.2; selected vs hovered +22.3 / +25.4 / +18.6; disabled+focus +19.2 / +21.5 / +16.0; P-16 glow band 8–16 CSS px: white +29.6 / +33.0 / +24.8, green +29.9 / +33.3 / +25.0. Shots p2_p4_states_bright.png, _dim.png, _l85.png |
2026-10-07 05:21 |
| G-FOCUS first frame | the same with {pause0: true} (every lgs-focus-in paused at t = 0), p2_p4_states_bright_t0.png |
focus − rest at t0 = +33.4 L vs +54.5 final: 61.3 % ≥ 60 % PASS | 2026-10-07 05:25 |
| FD-1 | python glass.py status |
PASS: unresolved: [], ambiguous: [] on the Steam bundle (481,450 bytes) and both SteamVR pages (277,901 bytes) |
2026-10-07 05:26 |
| FD-3 kit parity | python docs/phase2/fontkit.py --parity |
PASS: kit 163 tokens, theme 312; 160 shared, 160 equal, 0 different; kit-only --lg-fill-focus, --lg-fill-hover, --lg-fill-selected (derived from parity tokens). Kit re-rendered: python tools/mockshot.py docs/phase2/mockups/_example.html shots/p2_example.png (A1 values: glass 656, ornament 628, title x 100, search 520, no mic; menu +10 mm, 40 px header; laser hover only) viewed |
2026-10-07 05:24 |
| FD-7 DESIGN2 review | grep -o "\[A<n>[],]" docs/phase2/DESIGN2.md per n; read §0.3 |
PASS: §0.3 register lists A1–A13 with sections and sources; in-place tags A1 ×9, A2 ×4, A3 ×13, A4 ×6, A5 ×11, A6 ×3, A7 ×2, A8 ×3, A9 ×1 (new §10.5), A10 ×1, A11 ×1, A12 ×3, A13 ×3; §16 annotated with the as-built token differences (P4-D2) | 2026-10-07 05:22 |
| FD-7 (R2, A14) | grep -o "\[A<n>[],]" docs/phase2/DESIGN2.md | wc -l for n = 1..14; read §0.3, §3.2, §3.7, §3.8, §7.3 against PLAN §1.19 R2-1 to R2-6 |
PASS: §0.3 lists A1–A14 with sections and sources; in-place tags A1 11, A2 4, A3 14, A4 6, A5 13, A6 3, A7 2, A8 3, A9 1, A10 1, A11 1, A12 3, A13 3, A14 11; no stale text left (grep for "56 px quiet", "6–7", "≥ 8 (single", "hidden real panel", "520 px box", "+15 if it was the focused": 0 hits) |
2026-10-07 11:40 |
| FD-4 window | python glass.py shot main p2_p4_edge_window --route /library/home --back --settle 1.2 --pre "<edgewin.js: a 9 s test <style> in main: %{BasicUiRoot} { --lgs-edge: window; box-shadow: var(--lgs-mat-window-shade) } and its Phase 1 ::after rim off>"; python glass.py edge shots/p2_p4_edge_window.png 0 120 1800 |
PASS with P4's window edge: segments 38 → 75 (peak left of centre: the key arc + lobe) → −37, ratio 0.00 (≤ 0.35). Stock today (p2_p4_edge_window_stock.png, C1a's Phase 1 rim): ratio 0.87 FAIL (a uniform top line). First try showed the edge hidden under the header: fixed by --lgs-edge-z (window default 20000, above the header's 6000; contract §2) |
2026-10-07 05:30 |
| FD-4 CONFIRM alert | python glass.py shot main p2_p4_edge_alert --route /library/home --back --settle 1.0 --pre "<inventory shell §0.2 CONFIRM (no-action lab dialog, closes itself after 4.5 s) + a 6 s test style: .GenericConfirmDialog { --lgs-edge: thick; background: var(--lgs-mat-thick-bg); backdrop-filter: var(--lgs-mat-thick-blur); box-shadow: var(--lgs-mat-thick-shade), var(--lgs-shadow-10mm) } !important>"; python glass.py edge shots/p2_p4_edge_alert.png 405 535 1385 |
PASS with P4's thick material: segments 44 → 97 → −2, ratio 0.00. With Phase 1's rim still winning (first try): ratio 0.98 FAIL, so C1c's alert rewrite must drop --lgs-glass-rim. Viewed next to shots/p2_window-nav_alert.png: same arc-and-lobe edge, no outline; the OK button still shows Phase 1's focus ring (C4a/C1c) |
2026-10-07 05:32 |
| FD-4 fixture slab | python glass.py edge shots/p2_p4_states_bright.png 84 200 1700 |
PASS ratio 0.02 (.lgs-glass[data-lgs-mat=window]) |
2026-10-07 05:21 |
| FD-6 Reduce Motion | python glass.py js "<fixture + fd6 probe, fixture removed at the end>" --mode pad --media reduce |
PASS: matchMedia(reduce) true; --lgs-press-grow 0, --lgs-press-max 1, --lgs-card-lift / --lgs-disc-lift 1; light transitions 150 ms linear (press out 90 ms); lgs-focus-in 150 ms (P5: opacity only); Phase 1 aliases --lgs-t-fast/-t/-t-slow 150 / 150 / 180 ms (was 1 ms): fades only |
2026-10-07 05:27 |
| FD-6 High Contrast | the same with --media contrast; python glass.py shot main p2_p4_fd6_contrast --route /library/home --back --mode pad --media contrast --pre "<fixture>" |
PASS: slab rgba(11,13,16,.94), no backdrop-filter, no sheen; edge ::before = 2 px solid white .70 (the one allowed stroke) on the slab and on hooked controls; focus = white .30 + the 2 px stroke; no spot. First shot showed focus invisible on white and coloured fills (an inner white stroke on white): fixed with an outer stroke behind a 3 px dark gap (04-states §4), re-shot and viewed |
2026-10-07 05:30 |
FD-5 pre-check on Steam's /zoo/buttons |
python glass.py shot main p2_p4_zoo_buttons_focus --route /zoo/buttons --back --mode pad --settle 0.8 --pre "<zoo.js: 9 s test style .DialogButton { --lgs-ill: raised; raised fill }, real gamepad focus with L.gpTake>" (and _disfocus on the disabled button) |
Hook works under Steam's own focus: the focused button has .gpfocus, --lgs-focus: 1, lit ::after; focus vs rest +97.0 L, disabled+focus +68.3 L (both inflated by Phase 1's focus ring, which still wins: REQ P4->C4a). The real FD-5 waits for C4a's primitives |
2026-10-07 05:28 |
| Laser mode keying (VP P-01, P-02) | python glass.py js "<fixture; read --lgs-focus/--lgs-lift/--lgs-hover and ::after on 'focus', 'selfoc', 'hover', 'rest'; fixture removed>" --mode laser |
PASS: html lgs-input-laser; elements carrying a stale .gpfocus get --lgs-focus: 0, --lgs-lift: 0, ::after uniform 0 and no glow; --lgs-hover: 1 lights the .08 layer. In pad mode the same .gpfocus gives --lgs-focus: 1 (G-FOCUS rows above) |
2026-10-07 05:34 |
| FocusRing plate live (CTL §4.5, P-17, P-16) | python glass.py shot main p2_p4_focusring_settings --route /settings/notifications --back --mode pad --settle 0.5 --pre "(window.__P4RING = {idx: 0}, <ring2.js: L.gpTake on the first radio segment, read the visible %{FocusRing}>)" and the same with idx: 1 (p2_p4_focusring_rest.png: the ring on the neighbouring segment, segment 0 at rest); band above and below segment 0 with tools/p2/edge_profile.py |
First run: the plate's fill and radius applied but Phase 1's outline: 2px solid … !important (10-primitives §10, same specificity, later file) still drew a ring, and CTL's 18/2/.22 glow gave only +8.3 L in the P-16 band on the white "Always" segment. Fixed: plate rules at html.lgs-on.lgs-on, glow 22/6/.44 from the white-glow token. Now PASS: outline transparent, animations 0, radius min(24px, 50%), fill .12 + .16 spot; P-16 band +34.3 L (8–16 CSS px), +53.2 (8–16 shot px); no ring (viewed). A game page and /zoo/* had no visible ring (p2_p4_focusring_pad.png) |
2026-10-07 05:44 |
| G-FOCUS at the final .32 (after REQ C1a) | the same fixture shots p2_p4_states32_bright.png, _dim.png, _l85.png, _bright_t0.png (theme synced and reloaded with --lgs-focus-add: .32) |
PASS on all three: P-14 raised +61.2 / +68.2 / +51.3, recessed +80.1 / +86.0 / +71.7, row pill +74.2 / +82.6 / +62.0; P-15 focused vs selected nav +38.6 / +42.4 / +32.3, selected+focused vs selected +62.7 / +69.6 / +52.5; selected vs hovered +22.3 / +25.4 / +18.6; disabled+focus +19.2 / +21.5 / +16.0; P-16 band (8–16 CSS px) white +29.6 / +33.0 / +24.8, green +29.9 / +33.3 / +25.0; first frame 37.4 / 61.2 = 61 % | 2026-10-07 05:37 |
| FocusRing in laser mode | python glass.py js "(window.__P4RING = {idx: 0}, <ring2.js>)" --mode laser |
PASS: the ring Steam still draws for its focus has computed opacity: 0 under html.lgs-input-laser (CTL §4.5, VP P-02) |
2026-10-07 05:44 |
| FD-2 SteamVR pages (after P8's binary FontFace load) | python glass.py js "$(cat fd2.js)" --in vr:systemui and --in vr:controllerbindingui |
PASS: face loaded, check true, no load error; Latin 276.4 (Inter, = Steam windows); CJK 306.2 = Steam's stack (falls through, not tofu). The page's <style> is now 114,154 bytes: P8's core moves the 161,948-byte base64 out of the CSS into a binary FontFace (font section 1,197 bytes) |
2026-10-07 06:11 |
FD-5 /zoo/buttons with C4a's live primitives (since 05:48) |
python glass.py shot main p2_p4_fd5_buttons_b<t> --route /zoo/buttons --back --mode pad --settle 0.8 --pre "(window.__P4ZOO = {sel: 'button.DialogButton', target: t}, <zoo2.js: L.gpTake, no test style>)" for t = 0 and 3; same-element pairs (pill, inset 6) |
PASS: P-14 button 0 +85.3 L, button 3 +85.8 L (focus at --lgs-focus-add .32, --lgs-ill: raised, no ring, outline none); the single-control row carries the focus (+105–110 L, CTL C-D14). A first try caught the page mid-repaint (black background) and was re-shot |
2026-10-07 06:08 |
FD-5 /zoo/toggles |
the same with sel: '%{*GamepadDialogContent>Toggle}', t = 0 (on switch) and 2 (disabled) |
PASS: the on switch is a coloured fill, so its cue is the outer glow: band +97 L (8–16 CSS px, row lit too); disabled + focus +26.4 L (≥ 10). P-14 on the green fill itself +9 L, as designed (P-16 applies to white and coloured fills) | 2026-10-07 06:10 |
R1 review fixes (session 3, 2026-10-07 08:26–)
Review: wp/P4-review-R1.md. Steam build 11094443. Every live step through python glass.py … (lab lock); the device was shared with ~20 agents (load average 5.3–5.5, another package's native session on part of the time), so absolute frame rates are lower and noisier than the review's; every perf number below is a paired, interleaved A/B inside one locked step, never stock-then-themed minutes apart.
| Finding | Fix (files) | Test (command) | Result | Time |
|---|---|---|---|---|
| M1 universal hook subjects cost the grid ~20 % | Every hook rule's subject is a generated flat host list: one :where(<host>)::before/::after entry per host, per family and per kind, written by python docs/phase2/fontkit.py --hooks (03-material.css §1/§3, 04-states.css §2; the in-transitions keep no list, they match only hovered/focused/pressed elements). Hosts = the rightmost compound of every theme selector that sets --lgs-ill/-edge/-scroll-band to a kind (pseudo-classes dropped, a superset), the whole selector when that compound has no class/id/attribute/token or only .Panel/.Focusable. :where() keeps the old (0,1,2) specificity |
1. Diagnosis, recalc microbenchmark (forced style recalc of the 800-node grid subtree, CSSOM delete/insert of P4's rules, sheet digest equal after): python glass.py js "<perfab3.js>" |
before the fix: universal 122–158 ms, rules removed 73–77, a class-bucketed subject that matches nothing 71–76 (so the cost is the universal subject, not the queries) | 08:45 |
| 2. Which list form: same probe, variants | nested & under one :where(<list>) +29 % (Blink files a nested & rule in the universal bucket), nested plain list +32 %, flat entries +8 %, flat without the 9 %{*A>B} token entries ≈ 0 % (a multi-class :is() is unbucketed); 300 :where(.x)::after entries cost +1 % against 300 plain ones and :is(.a,.b) +25 % (bucketing of single-argument :where() confirmed) |
09:35 | ||
3. G-PERF gate, scroll A/B (L.perf-style rAF scroll of the grid, 2.5 s per run, rounds alternate order): python glass.py js "(window.__P4AB = {route: '/library/tab/AllGames', mode: 'scroll', ms: 2500, rounds: 2}, $(python docs/phase2/fontkit.py --perf-js))" ×2 |
shipped hooks 51.2 / 47.4 fps, hooks removed 52.0 / 48.1, old universal subject 37.2 / 35.3: listed −1.5 %, universal −27 % → P4's share PASS (≤ 5 %); digest equal | 09:43 | ||
4. Whole theme vs stock: python glass.py perf main --route /library/tab/AllGames --seconds 4 |
stock 83.4 fps / p95 22.1 / 4 long; themed 61.1 / 23.6 / 20 long (native session of another package running). The remaining gap is not P4's (step 3); noted for the coordinator / C2c (library) and P10 | 09:26 | ||
5. Hooks still paint: python glass.py focus main --route /zoo/buttons --mode pad --pairs <btn0 focus vs btn3 focus> |
P-14 button 0 +89.0 L (flat lists; +85.2 with the interim nested form; review +85.3) | 09:53 | ||
5b. The FocusRing rule's :has() (new for M2), same A/B with only that rule removed |
with 50.5 fps, without 47.8 (noise): no measurable cost | 09:58 | ||
| 5c. Size | P4's hook rules in the live sheet: 23.9 KB (flat lists) + 11.0 KB (FocusRing host test), per window | 09:45 | ||
6. Lists stay current: python docs/phase2/fontkit.py --hooks-check |
exit 0; it caught two staleness events during the session (areas added edge hosts: 28 → 35 → 38) and --hooks fixed them; REQ P4->P10 asks check-theme to warn |
09:45 | ||
| M2 FocusRing plate framed C4a's capsule segments | 04-states.css §3: generated html.lgs-on.lgs-on:has(.gpfocus:where(<--lgs-ill hosts' whole selectors>)) %{FocusRing} { background: none; box-shadow: none } (0,4,1, also beats the High Contrast plate); radius var(--lgs-ring-r, min(var(--lgs-r-row), 50%)); contract §3.4 amended |
python glass.py shot main p2_p4r1_ring_<i> --route /settings/notifications --mode pad --settle 0.8 --pre "(window.__P4RI = i, <ringpre.js: scroll, L.gpTakeEl(segment i), read the ring and the segment>)" for i = 0 ("Always", selected) and 1 |
ring: background none, box-shadow none on both; the segment keeps only C4a's own glow (22/8/.55, one layer); label rgb(13,14,18) (was rgb(65,66,69) darkest); no grey frame, no 3 px dark band from the plate. Viewed next to p2_controls_states.png "On + focus": the white capsule with one soft glow. A dark crescent remains at each end inside the halo: C4a's fill is background-clip: content-box inset 20 px while its light is inset 2 px (REQ P4->C4a) |
09:30 |
| m1 full-width focus arc on wide rows | row and nav kinds draw the arc as the E3 lobe (radial-gradient(50% 3px at 26% 0, …), alpha --lgs-focus-arc), not the uniform inset line (P4-D13); kit mirrors it |
python glass.py shot main p2_p4r1_zoo_tog1_pad --route /zoo/toggles --mode pad --pre "<togpre.js: L.gpTakeEl(toggle 1)>"; python glass.py edge shots/p2_p4r1_zoo_tog1_pad.png 282 460 1850 |
segments 15.5 → 33.9 → 29.6 → … → −13.7, ratio 0.00 PASS (review 0.89 FAIL on p2_p4rev_zoo_tog1_pad.png); viewed: a key-light glint at the left third instead of a lip along the row |
09:28 |
m2 contract promised .lgs-lens |
contract §6 corrected: the class interface is not built; how to ask for it | — | — | 09:12 |
| m3 SteamVR pages: no hooks, ring tokens | vr/00-vr-tokens.css: --lgs-vr-focus-ring = focus add as an inset layer + lit top arc + 32 px glow; --lgs-vr-hover-rim = lit top arc only (no closed ring); contract §5 documents that the hooks are not bundled into SteamVR pages; REQ P4->C5b, P4->C4b for their local restatements |
python glass.py js "<read --lgs-vr-focus-ring/-hover-rim>" --in vr:systemui |
both resolve on vr:systemui (html.lgs-on, scale 1.45, rgb(255 255 255 / .32) inset layer, arc .55); status: unresolved: [] on the SteamVR pages |
09:58 |
| m4 Phase 1 durations not tokens | --lgs-t-fast/-t/-t-slow = --lgs-d-interactive / -hover-in / -fade (210/294/441 ms; Reduce Motion follows P5) |
python glass.py gates main --route /settings/notifications --only motion --mode pad --pre "<focus radio 1>"; python glass.py js "<read the aliases>" |
G-MOTION PASS, 0 findings (review: one non-token 140 ms); aliases 210ms / 294ms / 441ms | 09:26 |
| m5 unscoped in-transition rule | :hover only when not pad, .gpfocus/.gpfocuswithin only when not laser (&:not(.lgs-input-pad) :hover::after, …) |
read in the live sheet | scoped | 10:00 |
| m6 High Contrast window .84; dial order | html.lgs-on.lgs-on { --lgs-dial: 1 } in the HC block (beats the bundler's remembered dial); new --lgs-mat-window-tint (.94 in HC); REQ P4->C1a to use it |
python glass.py js "<read dial, tints>" --media contrast |
--lgs-dial 1, --lgs-mat-window-tint rgb(11 13 16 / .94); C1a's --lgs-c1a-tint still .60 + 1 × .24 = .84 until REQ C1a |
09:26 |
| m7 P-03 laser hover never measured | Measured on a P4 fixture (a window slab over a flat room of L 134 or 51, data-lgs-ill raised / row / nav controls at rest and with --lgs-hover: 1, laser mode, self-removing). First reading: button +20.5 / +22.8, row +24.5 / +27.4 (over 25 in the dim room), nav +9.8 (under 10 in the bright room) / +10.9. Tuned: new tokens --lgs-row-hover-add .06 and --lgs-nav-hover-spot .16 (kit twins; parity PASS) |
`python glass.py shot main p2_p4r1_hover_<bright | dim> --mode laser --pre "(window.__P4HFX = {base: 'rgb(B B B)'}, <hoverfx.js>)"; python glass.py focus --png … --pair btn=pill:660,210,990,300:pill:240,210,570,300:10 --pair row=… --pair nav=…` |
after tuning: bright room: button +20.5, row +20.8, nav +12.8; dim room: button +22.8, row +23.1, nav +14.2: all inside +10..+25 PASS (p2_p4r1_hover_bright.png, _dim.png, viewed: the spot reads as light at the pointer, no rim). The review's +25.7 / +35.6 were over the transparent window with alpha dropped (black), outside the room band |
| m8 window rim above the scrim | window edge takes --lgs-edge-dim: .35 while a scrim shows: &:is(:has(.ModalOverlayContent.inactive), :has(.ModalOverlayContent.active):not(:has(.ModalOverlayContent.active .BasicUIContextMenu))) :where(<window hosts>)::before (Steam keeps an empty, invisible .ModalOverlayBackground at all times, so a plain :has() of it was wrong: caught live, rim dimmed with no modal) |
python glass.py js "<modalprobe.js: rim before / during the lab CONFIRM / after>"; shots p2_p4r1_edge_window, p2_p4r1_edge_alert on /library/tab/AllGames |
--lgs-edge-dim none → .35 during → none after; rim (max of the top 4 rows, x 300–1600) − interior (rows 14–19) under the CONFIRM scrim: 47.6 L now against 70.3 L in the review's alert shot (p2_p4rev_edge_alert.png, same interior 25.8) and 67.1 L on the unscrimmed window (p2_p4rev_edge_window.png): the rim now dims with the window (×0.68) instead of standing out more. p2_p4r1_edge_alert.png viewed (my own window shot caught a transparent frame, alpha 0, and is not used) |
09:50 |
| m9 DESIGN2 contradicts A5 | §7.6 (nav rows: spot-only hover, focus .32 + lobe), §7.8 (sheen: normal compositing), §10.1 (host lists, plate off) | grep |
done | 09:15 |
| m10 kit recipes | kit E3 bottom stop .20 → .16; focus rim = theme E3 ×1.5 (conic + lobe, same stops); "+ .28" → "+ .32"; row/tab focus arc as the lobe; P-03 hover twins | python docs/phase2/fontkit.py --parity; python tools/mockshot.py docs/phase2/mockups/_example.html shots/p2_example.png (viewed) |
parity PASS (168 shared, 0 different) | 10:00 |
| m11 contract disabled selector | §3.1 → %{*GamepadDialogContent>Disabled} |
— | — | 09:12 |
| m12 shipped default without input-mode classes | contract §3.1 "Runtime off" paragraph; REQ P4->V1 for defaults.json |
— | recorded | 09:20 |
| m13 session-1 scratch on the Frame | rm -rf /tmp/lgs/p4 (frame_ssh) |
ls /tmp/lgs/p4 |
gone | 08:46 |
| REQ P9->P4 | DESIGN2 §11: menu slab materializes in place, CSS clip-path morph, no glassd morph / overshoot / neck; "morph k" dropped |
— | answered in wp/P9.md |
09:15 |
| High Contrast after the restructure (FD-6) | the HC variants now live in the flat lists | python glass.py shot main p2_p4r1_fd6_contrast --mode pad --media contrast --pre "<hoverfx.js over L 51>" |
slab rgb(11 13 16 / .94) with the 2 px white .70 stroke, no spot (viewed); a focused /zoo/buttons button in High Contrast (python glass.py js "<hcfocus.js>" --mode pad --media contrast): ::after box-shadow is the inset 0 0 0 2px white stroke form, its edge ::before and the window's are the 2 px solid stroke. First shot showed no laser hover on nav rows in High Contrast (spot-only hover, spots are 0 there; also true before R1): fixed, nav takes the uniform add under prefers-contrast: more. Re-shot: hover button +16.9, row +14.3, nav +19.6 (was 0) |
10:05 |
| Device left | probe globals __P4* deleted in SharedJSContext, fixture removed (timer + check), no modal open, route back to /library/home, theme on (enabled: true, unresolved: []); no flags set; native mode was another package's session (lgs-native on <html> during part of the session), never entered by P4; Frame scratch none |
python glass.py js "<cleanup + check>"; python glass.py status |
clean | 10:10 |
Requests
-
REQ P4->C4a: selected radio segment (
%{Group>Button}%{Group>Active},theme/10-primitives.css§7): the white fill isbackground-clip: content-boxinsidepadding: 2px 20px, so it is inset 20 px at the ends with ~10 px corners, while the light layer and its--lgs-ill: whiteglow follow the element (--lgs-ill-inset: 2px, radius 28). Result on Settings › Notifications with focus on "Always" (P4 R1 shotshots/p2_p4r1_ring_0.png, crop at x 430–520, y 742: track 29 L for 27 px between the white 242 and the glow): a dark crescent at each end inside the halo, and a white pill with square-ish ends instead of the mockup's capsule ("On + focus",p2_controls_states.png). P4's FocusRing plate no longer paints there (R1 M2). Please make the fill and the light the same shape: e.g. fill the padding box inset 2 px all round (border: 2px solid transparent; background-clip: padding-box; padding: 0 18px) so the white capsule meets its glow, or give--lgs-ill-insetthe fill's inset. -
REQ P4->C1a: High Contrast window tint (review R1 m6).
theme/20-shell.csspaints%{BasicUiRoot}with--lgs-c1a-tint: rgb(20 22 30 / calc(.60 + var(--lgs-dial) * .24)), which is .84 in High Contrast (live probe in the R1 review), while FD-6 asks for opaque glass (.94). P4 added--lgs-mat-window-tint(same formula;rgb(11 13 16 / .94)underprefers-contrast: more). Please set--lgs-c1a-tint: var(--lgs-mat-window-tint)(or restate .94 in your own High Contrast block). P4's High Contrast--lgs-dial: 1now beats the bundler's remembered dial (html.lgs-on.lgs-on).- C1a answer (2026-10-07, R2 fix pass): done:
--lgs-c1a-tint: var(--lgs-mat-window-tint)(High Contrast .94).
- C1a answer (2026-10-07, R2 fix pass): done:
-
REQ P4->C5b:
theme/vr/40-bindings.cssrestates--lgs-vr-hover-rimat the binding page's scale as the Phase 1 closed inner ring (inset 0 0 0 calc(1.5px * 1.875) var(--lgs-control-outline)). Since R1 (m3) P4's token is ring-free: the lit top arc only,inset 0 calc(1.5px * S) calc(1.5px * S) calc(-1px * S) rgb(255 255 255 / .45)with S your scale (1.875). Please restate it in that form (or drop your restatement and set--lgs-vr-scale).--lgs-vr-focus-ring(used invr/20-nowplaying.css) is now the focus add as an inset layer + arc + glow, no ring; re-shoot Now Playing focus against your mockup. -
REQ P4->C4b:
theme/vr/50-keyboard.cssrestates--lgs-vr-hover-rimas the Phase 1 closed ring (inset 0 0 0 calc(1.5px * var(--lgs-vr-kb-scale)) var(--lgs-control-outline)). P4's token is ring-free since R1 (m3): please restate asinset 0 calc(1.5px * var(--lgs-vr-kb-scale)) calc(1.5px * var(--lgs-vr-kb-scale)) calc(-1px * var(--lgs-vr-kb-scale)) rgb(255 255 255 / .45). -
REQ P4->P10:
check-theme: also runpython docs/phase2/fontkit.py --hooks-check(exit 1 = a theme rule sets--lgs-ill,--lgs-edgeor--lgs-scroll-bandon a selector the generated host lists in03-material.css/04-states.cssdo not cover yet) and print its message as a warning (not a failure). Why: since R1 M1 the hooks list their hosts instead of*::after(G-PERF), so a new hook selector paints nothing untilpython docs/phase2/fontkit.py --hooksis run; contract tokens.md §2 lets any package run it.- P10 (2026-10-07 session 4): done:
check-themerunsfontkit.py --hooks-check --json(60 s cap) and prints aWARN hook host lists are stale (<lists>) … run python docs/phase2/fontkit.py --hooksline (also with--quiet; JSONwarnings), never a failure; exit codes other than 0/1 become a note. Tested: the tree today → PASS, no warning; a scratch copy with an extra--lgs-edge: panelrule → WARN (edge ::before, edge:panel ::before), still PASS, exit 0. contracts/lab.md §3.
- P10 (2026-10-07 session 4): done:
-
REQ P4->V1: defaults (review R1 m12). With
wp.p3off (today's shipped default) no window haslgs-input-*, so P4's states run in "runtime off" mode: a stale.gpfocuslights under the laser and a parked:hoverlights in pad mode (VP P-02, P-09). Laser/pad keying needs P3's input mode on; please weigh this when you setdevice/defaults.json(contract tokens.md §3.1 "Runtime off"). -
REQ P4->P8: the UI font does not load in SteamVR pages. Their CSP is
default-src 'self' 'unsafe-eval'(nofont-src, measured onvr:systemuiwithfetch(location.href)), so thedata:@font-faceintheme/01-font.nowrap.cssends inNetworkError(document.fontsstatuserror) onvr:systemuiandvr:controllerbindingui. The same bytes load as a binary FontFace (no fetch, CSP does not apply):new FontFace('LGS Inter', bytes.buffer, {weight: '100 900'})→loaded, width 276.4 = Steam windows. Please makedevice/lgs_vr_core.jsdo this inapply()for every@font-faceinpayload.csswhosesrcisurl(data:font/woff2;base64,…): parsefont-family,font-weight,font-style,unicode-range(→unicodeRange) andfont-display(→display),atobthe base64 into aUint8Array,document.fonts.add(ff), keep the list, anddocument.fonts.delete(ff)each one indisable()(G-REMOVE). Once per payload version, not on every re-check. This closes FD-2 onvr:systemui.- P8 (2026-10-07 06:20): done in
device/lgs_vr_core.js: every@font-facewith adata:font/woff2src leaves the page CSS and becomes a binaryFontFace(family, weight, style, stretch, unicode-range, display), built once per payload version, kept indocument.fontsbyapply(), deleted bydisable();__LGS_VR.status()listsfonts/fontErrors. Live onvr:systemui,vr:controllerbindingui,vr:keyboard:LGS Inter 100 900 loaded,document.fonts.checktrue, body family"LGS Inter", …, canvas width of a 24 px test string 296.8 = the Steam main window's 296.8; the page CSS shrank 272 KB → 114 KB (nodata:font left).
- P8 (2026-10-07 06:20): done in
-
REQ P4->C1a:
theme/vr/00-vr-tokens.cssnow holds the SteamVR shared values oftheme/vr/10-systemui.css§0 (same names, same values). Please delete §0 fromvr/10-systemui.css(PLAN §6 two-step move). C1a answer (2026-10-07): accepted, not done yet: this session is M0 (documents and mockups only, by the coordinator's instruction). C1a deletes §0 at the start of its first M2 session, after avr:systemuicheck thatvr/00-vr-tokens.cssloads beforevr/10-systemui.cssand every--lgs-vr-*still resolves. Done (C1a, 2026-10-07 session 3): §0 deleted fromvr/10-systemui.cssafter avr:systemuicheck: one copy of the block in the live bundle (yours, before 10-systemui), every--lgs-vr-*resolves (--lgs-vr-scale1.45,-r-window,-ring-w,-focus-ring,-grabber*,-hit-inset,-hover-*,-solid-bg). -
REQ P4->C4a:
theme/04-states.css§3 restyles%{FocusRing}as CTL §4.5's light plate (input-mode keyed). When you rewritetheme/10-primitives.css, drop its section 10 (FocusRing ring + glow). (Update 05:44: P4's plate now wins by specificity,html.lgs-on.lgs-on, verified on Settings › Notifications, so §10 is dead code until you remove it.) Also measured on/zoo/buttons(2026-10-07, pad mode): a focusedDialogButtonstill gets Phase 1's--lgs-focus-ringbox-shadow (inset 2 px white .90 + 1 px ring, P-17) with!importantstrong enough to beat a test rule; with C4a's rewrite please set--lgs-ill: raised(orwhitewhen on) on Steam's buttons and drop the ring, so FD-5 can measure the hook alone. P4's hook already lights the real.gpfocusbutton (--lgs-focus: 1,::afterlit;shots/p2_p4_zoo_buttons_focus.png).- C4a (2026-10-07, M0): accepted. Section 10 is dropped when C4a rewrites
theme/10-primitives.cssat M2 (the M0 session builds no theme code);controls.md§4.5 now names P4's plate as the implementation. - C4a (2026-10-07 06:33): done in the Phase 2
10-primitives.css(live since 05:48): Phase 1's FocusRing section is gone; Steam's buttons are--lgs-ill: raised(whiteon tinted fills), switches light their rail's::after, rows are--lgs-ill: row; Steam's focus fill and Phase 1's--lgs-focus-ringare replaced by the rest look (!important), so the hook alone carries focus. Live G-FOCUS on/zoo/buttons(pad, single-control row): +167 L; disabled + focus +98.7 L (wp/C4a.mdM2-F1).
- C4a (2026-10-07, M0): accepted. Section 10 is dropped when C4a rewrites