29 KiB
Coverage: SteamVR's own pages (area steamvr)
Owner of theme/vr/ (DESIGN.md §9, LAB.md "SteamVR's own pages", inventory docs/inventory/steamvr.md). Done on 2026-10-06 against SteamVR web UI CLSTAMP 11065908. Fix pass for the review findings on 2026-10-07 (see "Fix pass" below); current after-shots are the vrf_* set.
Files
| File | What it styles |
|---|---|
theme/vr/10-systemui.css |
vr:systemui chrome around the window: shared scale variables, the grab bar, the resize corner, the frame controls (and their tooltips, the More Options popout and the gamepad-mode pill), the controller status card, and the on-demand systemui panels (app transition, IPD, progress ring, framerate and gamepad-unsupported pills, room setup, Steam loading, persistent notifications, guided tour) |
theme/vr/20-nowplaying.css |
Now Playing (#nowplayingpanel) |
theme/vr/30-settings.css |
The SteamVR settings page (#vrsettingspanel) plus every SettingsMain control SteamVR reuses: Now Playing's bindings and video-settings modals, dropdown popovers, the restart banner |
theme/vr/40-bindings.css |
vr:controllerbindingui (app select, binding list, view/edit binding, rodal modals, popovers, scrollbars). Scoped to body.ControllerMainBorderRadius |
theme/vr/50-keyboard.css |
vr:keyboard (SteamVR's own keyboard, keyboard.html) |
theme/vr/60-overlays.css |
messageoverlay.html, notificationtoast.html, bindingcallouts.html |
theme/vr/pre/*.js |
State scripts for shots and audits (not bundled: only theme/vr/*.css is). See "Reproduce" |
Every file in theme/vr/ is bundled into every SteamVR page. Tokens therefore have to resolve on every page:
- Shared modules: the 9 that every page loads (grabhandle, controllerstatus, legacydashboardframecontrols, frameresizehandle, framegamepadmodebutton, gamepaduibutton, guidedtour_steamframe, …) are written as
%{Token}. - Systemui-only modules: roomsetup, framerate, gamepadunsupportedmessage, loadingthrobber and persistentnotifications would be "unresolved" on the binding and keyboard pages. They are matched by their CSS-module prefix instead, e.g.
[class*="framerate_FramerateRoot_"]. - Status:
window.__LGS_VR.status()showsunresolved: [], ambiguous: []onsystemui,Controller Bindingandkeyboard.
Look, per piece
Laser hover on SteamVR's laser-only pages. SteamVR settings, the binding editor, the SteamVR keyboard, message overlays, room setup and Now Playing in laser-mouse mode draw no gamepad focus ("Use Laser Mouse to Interact"), so laser hover is the only targeting cue. Controls there use one strong hover, defined once in 10-systemui.css §0: --lgs-vr-hover-fill (= --lgs-focus-fill, .22) plus --lgs-vr-hover-rim (an inset 1.5px × page scale ring in --lgs-control-outline). This matches Steam's own pages, where the laser lights the pointed control with .gpfocus; ours is the same fill without the glow, so gamepad focus (fill + ring + glow) stays distinct where it exists. Pressed stays brighter (--lgs-pressed-fill, .30). Rows that rest transparent or at --lgs-fill-1 get the fill only.
| Piece | Treatment |
|---|---|
| Window grab bar | Translucent white Liquid Glass capsule (white .62 + top sheen) with a two-tone edge: an inset 1.5px black .55 ring and an outer 1px black .32 ring (the 60px hit padding leaves room), so the white body reads over a dark room and the dark edge over a bright one. Hover and drag light it from within: white .92 + glow, same edge. SteamVR's scaleX grow, inline width, 60px hit padding and the transform timing are untouched |
| Resize corner | Near-opaque white stroke (--lgs-text-1; a .72 stroke let its own halo show through and went grey over a grey room) at SteamVR's own stroke width, with a tight two-pass dark halo (drop-shadow(0 0 .8px rgba(0,0,0,.9)) ×2, cursor style) plus a soft drop shadow. Pure white (--lgs-knob) on hover and drag. SteamVR's scale(1.4) and stroke growth are kept |
| Frame controls | Each section is a panel-glass capsule, with pure white glyphs. Hover, focus and press draw a pill (our ::before, free on these buttons) inset 3px in the button, so it is concentric with the section capsule (25.5 − 3 = 22.5) and the focus ring clears every glyph (the 36-unit glyphs at 38px overflowed the old 45px circle); one-button sections stay near-circles. Hover --lgs-hover-fill, gamepad focus --lgs-focus-fill + inset white ring, pressed --lgs-pressed-fill. Idle: SteamVR fades the container to .6 exactly when it is neither hovered nor gamepad-focused; then the capsule tint goes opaque (--lgs-vr-solid-bg, the panel tint), so the faded capsule is a .6 tint like stock and the glyphs keep at least stock contrast over a bright room. Hover and focus bring back the .73 panel glass at full opacity (background transition in step with SteamVR's .4s fade). Margins and paddings stay, so the quad width is unchanged |
| Frame-control tooltips | Panel-glass capsule, same padding and font |
| More Options popout | Panel-glass menu at --lgs-r-menu. Rows get inset rounded highlights; the separators become hairlines |
| Gamepad-mode pill | "Use Laser Mouse to Interact" is panel glass. "Enter Gamepad Mode" is the tinted primary, brighter on hover |
| Controller status | Panel-glass card behind the content only: our ::before, anchored with CSS anchor positioning to SteamVR's icon and battery frames, so it hugs the hand, the gamepad glyph or the battery alone. No card when the status is empty. The hand PNG and the gamepad glyph are drawn white. Battery pips are whole-fill colour: the horizontal pip PNGs (controller_model_battery_1..4, controller_model_low; a soft glow band and black empty pips) are swapped with content: for an SVG of the same natural size (210×25, pip centres and radius measured from the PNG), so the img box stays 160×19: green #30d158, orange #ff9f0a for _battery_1 (under 30%), red #ff453a for _low, empty pips white .24 fills. Data URIs can't read CSS variables, so the token hex values are written out. The charging PNGs and SteamVR's vertical vert_ set (not seen on the Frame) keep the full-strength hue-rotated tint. In the Frame's large layout, 8px of the battery row's top padding moves to its bottom (same total height) so the pips sit inside the card |
| Now Playing | Window glass at Steam's window radius. The art is content at the card radius with a soft shadow. No art: SteamVR paints its placeholder portrait (appimage_default_portrait.png, opaque #282828) as an inline background-image on the fallback's .Icon; that placeholder is decoration, so it goes (!important, inline style) and the fallback card is a --lgs-fill-1 fill with an inner rim and no content shadow. Only that URL is matched; real art and icons are untouched. Resume Game is the one tinted primary: deep play-green at rest (play tint under the dimming scrim, like Steam's Play capsule in 50-appdetails.css); hover and focus light it to --lgs-vr-play-lit (the play tint with 18% black, so the white 36px label stays ≥ 3.5:1 over any room); hover adds the inner rim, focus the ring. %{Highlighted} (SteamVR: primary + laser-mouse mode + no controllers) keeps the rest look, so in that mode rest and hover still differ. The other actions are fill capsules with the strong laser hover. Exit Game / Exit Home turns --lgs-tint-danger only on hover or focus; it is the only non-primary action whose label is not wrapped in a <span>, hence :not(:has(> span)). Focus is SteamVR's own .gpfocus: focus fill + one ring (--lgs-vr-focus-ring, like Steam's DialogButton and Play focus); the Steam-owned ::after ring element keeps its geometry but its outline goes clear and its flash / grow / 20× blinker stop |
| SteamVR settings | Window glass. The sidebar is the same raised pane as Steam Settings (60-settings.css), its sibling page in this window: our ::before inset 8/6/8/8 (×1.45), --lgs-r-panel ×1.45 (concentric with the window), panel sheen + --lgs-fill-1 + glass rim, the sidebar isolation: isolate so it sits under the list. Section rows are capsules inset 8px inside the pane like Steam's; the current section is a white capsule painted on the button from 7 overlapping opaque gradient layers (--r 32px; a non-overlapping 3-layer capsule showed hairline seams at this page's fractional scale). Grouped subsections are --lgs-fill-1 cards. Buttons and dropdowns are fill capsules. Radios are round fills, the selected one white. Segmented controls are a sunken capsule well with the white selected segment. Sliders have a fill-3 track, a white fill up to the knob (drawn from SteamVR's own --slider-value) and a white knob with a dark value; detent ticks are --lgs-text-2 with mix-blend-mode: difference, so they turn dark grey on the white fill and dim light grey on the track. Advanced controls swap SteamVR's purple slabs for a purple glass tint (--lgs-purple mixed 26% / 40% on hover) so they stay recognisable. Popovers and modals are thick glass. The restart banner is a glass strip with a tinted Restart. All hovers are the strong laser hover |
| Controller bindings | Window glass at Steam's window radius (body clip radius scaled). The title bar is no longer a slab: the title floats, with a hairline under it, and Back and Options are capsules. App rows and binding entries are fill cards; buttons are fill capsules. Action-set tabs are fill segments with the current one white; warning and error tabs keep an orange or red whole fill. The unbound-actions count (absolutely positioned over the end of the tab label) is an opaque panel-tint chip with a white count, like stock's opaque chip, so the digit never draws over the label. Mode groups are fill cards (hover one step up, --lgs-fill-3). SteamVR's .5 dimming of the mirrored column (.Mirrored) and of unbound rows (.None .Label.BindingLabel) is raised to .64: still clearly dimmer, but above stock contrast on translucent glass over a bright room. Rodal dialogs, popovers and inline dropdown lists are thick glass with white selected rows. Scrollbars are thin .28 white thumbs with no track, widths unchanged. All hovers are the strong laser hover |
| SteamVR keyboard | Panel-glass slab at Steam's keyboard radius (--lgs-r-panel × 2.25 = 54px, like 35-hud.css). What sits in its top corners follows the curve: the preview well's top corners (5px in, 49px) and, in minimal mode without a preview, the first row's corner keys (7px in, 47px). The bottom keys sit 33px above the slab edge, where the slab curve already clears their normal corners, so they keep the key radius. Keys are fill-2 (special keys fill-3) with white labels; laser hover lifts letter keys to --lgs-focus-fill and special keys to --lgs-pressed-fill (the special-key rule carries the :has() so it outranks their rest fill), both with the inner rim. The touched or pressed key (SteamVR's inline background:#000, overridden with !important) is white with a dark label. Done is tinted primary. The preview is a sunken well with a white caret |
| Message overlay | Panel glass; buttons are fill capsules (strong laser hover), the first (affirmative) one tinted primary |
| Notification toast | Panel-glass rounded rect; title --lgs-text-1, body --lgs-text-2 |
| Binding callouts | Panel-glass action panel and bubbles. The pressed input is lit with focus fill + ring; the current action set is white |
| Transition, IPD, progress, framerate, gamepad-unsupported, room setup, loading, persistent notifications, tour | Panel glass. Pills are capsules; SteamVR's borders stay (geometry) but go clear, with the glass clipped to the padding box. Primary buttons are tinted; the progress ring is --lgs-green on a panel disc; the tour keeps its semantic blue guide line |
Scale. SteamVR draws the dashboard window's pages wider than Steam draws main (1280 CSS px) for the same window, so window-level radii and rings scale by a page factor and corners match Steam's window glass:
| Page | Width | Factor |
|---|---|---|
| Now Playing, SteamVR settings | 1858 px | 1.45 (--lgs-vr-scale) |
| Binding page | 2400 px | 1.875 |
| Keyboard | 1920 px | 2.25 against Steam's 854 px keyboard |
Screens, shots and audits
Shots are in shots/ (1x; transparent areas show the room in the headset). before = theme off, after = theme on.
Audit columns:
- Audit is
glass.py auditor the in-page audit (pre/audit_inpage.js, see below), with the theme verifiably applied (themeApplied=true). - Panel sizes compares every
vsg-node[vsg-type=panel]box with the theme off and on; they must be identical.
Fix-pass shots (vrf_*, 2026-10-07) replace the earlier after-shots; the earlier vr_*_before stock shots stay valid where the state is the same. New vrf_*_before stock shots were taken where the live state had changed (two battery-only controller cards instead of the gamepad card; settings opening on the Dashboard section). They are captured with html.lgs-on removed for the capture (every theme rule is scoped under it), so the lgs-vr watcher is never stopped.
| Screen / state | Before | After | Audit (GONE/HIDDEN/SHRUNK/UNCLICKABLE/CONTRAST) |
|---|---|---|---|
| systemui idle: grab bar, frame controls, resize corner, controller cards | vrf_sys_before |
vrf_sys_after |
4 controls, 4 text runs, 0 issues |
| systemui laser hover (grab bar, resize, frame controls, 1st button) | vrf_sys_hover_before |
vrf_sys_hover_after |
0 issues. (The emulation can't lift SteamVR's :not(:hover) idle fade, so the capsules show their idle tint there) |
systemui drag + gamepad focus (ForceActive, HasGamepadFocus, GamepadFocused on pop-out) |
vrf_sys_focus_active_before |
vrf_sys_focus_active_after |
0 issues |
Frame-control tooltip (real legacy-tooltip panel) |
vr_sys_tooltip_before |
vr_sys_tooltip_after |
0 issues (unchanged by the fix pass) |
| On-demand systemui panels (static mocks: More Options with a focused and a hovered row, gamepad pills ×3, tooltip, transition, IPD, framerate, gamepad-unsupported, progress ring, room setup with hover, loading, persistent notification, tour card) | vr_sys_zoo_before, vr_sys_moreoptions_mock_before |
vrf_sys_zoo_after |
mocks only (not auditable as real panels) |
| Message overlay, notification toast, binding callouts (static mocks with hover and pressed states) | vr_overlays_zoo_before |
vrf_overlays_zoo_after |
mocks only |
Now Playing at rest (laser-mouse mode: Resume carries %{Highlighted}; no-art fallback) |
vr_np_before, vr_np_rest_before |
vrf_np_after |
0 issues. Panel sizes 819×132, 1858×1045, 334×51, 152×152, 256×256 ×3, 20×34, identical with the theme off and on (pre/panel_sizes.js) |
| Now Playing: Resume focused | vr_np_resumefocus_before |
vrf_np_resumefocus_after |
4 controls, 10 text runs, 0 issues |
| Now Playing: VR Controller Bindings focused, Exit hovered | vr_np_states_before |
vrf_np_states_after |
0 issues |
| Now Playing: Exit focused, Resume hovered | vr_np_exitfocus_before |
vrf_np_exitfocus_after |
0 issues |
| Now Playing: no focus, VR Video Settings hovered (laser mode) | vr_np_before |
vrf_np_hover_after |
0 issues |
| SteamVR settings (opens on its last section, Dashboard) | vrf_settings_before |
vrf_settings_after |
6 controls, 40 text runs, 0 issues |
| SteamVR settings, hover on a sidebar section, a segmented control, the slider and a dropdown | vrf_settings_states_before |
vrf_settings_states_after |
0 issues |
SteamVR settings mocks inside the real page (pre/settings_mock.js): a radio row with 80 hovered and 90 selected, two slider detents (on the fill and on the track) |
vr_settings_states_before (real General radios) |
vrf_settings_mock_after |
mocks; computed styles: rest .10, hovered .22 + rim, selected .94 |
| Bindings: app select | vr_bind_apps_before |
vrf_bind_apps_after |
3 controls, 4 text runs, 0 issues |
| Bindings: laser hover on an app row, Back and Show More | vrf_bind_hover_before |
vrf_bind_hover_after |
0 issues |
| Bindings: Show More Applications (107 apps) | vr_bind_apps_more_before |
vrf_bind_apps_more_after |
2 controls, 109 text runs, 0 issues (audited 1.5 s after the list opens; earlier, app images still loading reflow the names) |
| Bindings: binding list (VR Dashboard) | vr_bind_list_before |
vrf_bind_list_after |
24 controls, 23 text runs, 0 issues |
| Bindings: read-only binding view (unbound-actions chip on "Keyboard Entry") | vrf_bind_view_before |
vrf_bind_view_after |
51 controls, 141 text runs, 0 issues |
SteamVR keyboard, full mode ("r" pressed, "g" and backspace laser-hovered, pre/kb_states.js) |
vrf_kb_before |
vrf_kb_after |
0 controls (keys are plain divs), 2 text runs, 0 issues |
| SteamVR keyboard, minimal mode (no preview row: first-row corner keys follow the slab) | (none) | vrf_kb_minimal_after |
display check |
Other checks:
- Performance budget:
- At rest,
backdrop-filteris used on 0 elements in systemui and keyboard, and on 1 in the binding page: a hidden, 0×0 rodal dialog. - Blur only appears on opened modals and popovers.
- There are no infinite or filter animations, and
document.getAnimations()returns 0 at rest (re-checked after the fix pass on systemui, the binding page and the keyboard: 0 visible blurs, 0 blend modes, 0 animations). - The only blend mode is the settings slider detent (
differenceon a 3px tick, static). The frame-control capsules transitionbackground-coloronly on hover / idle changes, in step with SteamVR's own opacity fade. - None of these pages has a scroller under blur, so
perfwas not needed.
- At rest,
- Row 0 of the atlas: untouched.
html,body,#root,vsg-app,.AppSceneGraphandvsg-*elements are never painted.
Fix pass (review findings, 2026-10-07)
Measured from screenshots composited over a dark (24), grey (128), 210 and bright (235) room. "Stock" = theme off.
| Finding | Outcome | Evidence after the fix |
|---|---|---|
Keyboard icon keys lost laser hover (:has() rest rule outranked :hover) |
Fixed: .VRKBKey:has(> .VRKBKeyFace > img):hover |
Computed: special keys .16 → .30 + rim (was .16 → .16); letters .10 → .22 + rim. Hover-vs-rest over a grey room: letters 1.46, specials 1.52 (stock 1.32) |
| Laser hover too weak on laser-only pages | Fixed: strong SteamVR hover (§ "Laser hover") on settings, bindings, keyboard, message overlay, room setup, binding callouts, Now Playing | Radio rest vs hover (fill alone, rim not counted) 1.51 / 1.43 / 1.33 (was 1.13 / 1.10). Binding app row 1.74 / 1.65 / 1.49 (stock 1.54), Back 1.49 / 1.41 / 1.32, Show More 1.53 / 1.46 / 1.35 (were 1.10–1.15), plus the rim |
| Grab bar and resize corner vanish over a bright room | Fixed: two-tone bar edge; resize stroke near-opaque white with a tight dark halo, stock stroke width | Bar vs room, dark / grey / 210 / 235: 11.4 / 3.12 / 3.50 / 4.16 (was 14.9 / 3.59 / 2.18 / 2.23; stock 1.80 / 2.48 / 6.49 / 8.20). Resize: 16.3 / 3.82 / 7.01 / 7.90 (was 9.6 / 2.79 / 1.39 / 1.40) |
| Frame-control icons wash out at SteamVR's idle .6 | Fixed: capsule tint opaque only while idle, pure white glyphs | Glyph vs capsule, keyboard button: 6.15 / 5.16 / 4.28 / 4.06 (stock 5.13 / 4.60 / 3.88 / 3.69); More: 6.52 / 5.56 / 4.59 / 4.34 (stock 6.43 / 5.57 / 4.60 / 4.35, equal within 0.01) |
| Resume lit state below 3:1 | Fixed: lit fill = play tint + 18% black; %{Highlighted} keeps the rest look |
Label vs capsule, lit (hover or focus): 3.81 / 3.66 / 3.54 / 3.51 (was 2.64 / 2.52 / 2.43 / 2.41; stock 2.75). Rest 5.63–5.26 |
| Binding view dim rows over a bright room | Fixed: SteamVR's .5 mirrored / unbound dimming raised to .64 (labels were already --lgs-text-1, so no vibrancy step was left) |
At 235: mirrored header and Click 3.87 (was 2.97; stock 4.91 / 3.63; bold 24px is large text, threshold 3), Touch / None 3.72 (was 2.95; stock 3.64), mirrored Touch 2.51 (stock 1.93) |
| Unbound-actions count drew over the tab label | Fixed: opaque panel-tint chip, white count | vrf_bind_view_after: "3" clean on its chip |
| Slider detents vanish on the white fill | Fixed: --lgs-text-2 tick with mix-blend-mode: difference |
vrf_settings_mock_after: dark tick on the fill, dim light tick on the track |
| Now Playing double focus ring | Fixed: Steam-owned ::after outline clear, button's own ring kept |
vrf_np_resumefocus_after, vrf_np_states_after: one ring |
| Now Playing no-art fallback was an opaque slab | Fixed: placeholder background-image removed (URL-matched), fill-1 card with rim, no content shadow |
vrf_np_after; computed .Icon background-image none, ArtworkColumn shadow none |
| Frame-control circle cut through the glyphs | Fixed: concentric inset pill | vrf_sys_focus_active_after: the focus ring clears the pop-out glyph |
| SteamVR settings sidebar didn't match Steam Settings | Fixed: inset raised pane + capsules + white capsule | vrf_settings_after, vrf_settings_states_after |
| Battery pips: hue-rotate smear, black empty pips | Fixed: SVG swap for the horizontal pip set | vrf_sys_after: clean orange 1/4 and green 2/4, empty pips white .24 |
| Keyboard slab squarer than Steam's keyboard | Fixed for the slab (54px), the preview well and minimal-mode first-row corner keys. Not applied to the bottom corner keys: they sit 33px above the slab edge (last row ends at y 1047 on a 1080 slab), so a 47–49px corner would not be concentric (a concentric key corner there is about 21px, close to the 18px key radius) and the slab curve already clears their normal corners (checked: key arc reaches 47.2px from the slab corner centre, inside its 54px) | vrf_kb_after, vrf_kb_minimal_after |
Reproduce
All from glass-shell/. Always pass --theme on on after-shots (see "Caveat" below).
python glass.py sync
python glass.py shot vr:systemui vrf_sys_after --theme on
python glass.py shot vr:systemui vrf_sys_hover_after --theme on --pre "$(cat theme/vr/pre/sys_hover.js)"
python glass.py shot vr:systemui vrf_sys_focus_active_after --theme on --pre "$(cat docs/inventory/steamvr-pre/sys_focus_active.js)"
python glass.py shot vr:systemui vr_sys_tooltip_after --theme on --pre "$(cat docs/inventory/steamvr-pre/sys_tooltip.js)"
python glass.py shot vr:systemui vrf_sys_zoo_after --theme on --pre "$(cat theme/vr/pre/sys_zoo.js)"
python glass.py shot vr:systemui vrf_overlays_zoo_after --theme on --pre "window.__LGS_ZOO_PART = 2, $(cat theme/vr/pre/sys_zoo.js)"
python glass.py shot vr:controllerbindingui vrf_bind_view_after --theme on --pre "$(cat docs/inventory/steamvr-pre/bind_view.js)" --settle 1.5
python glass.py shot vr:keyboard vrf_kb_after --theme on --pre "$(cat theme/vr/pre/kb_states.js)"
python glass.py shot vr:keyboard vrf_kb_minimal_after --theme on --pre "window.__LGS_KB_MINIMAL = 1, $(cat theme/vr/pre/kb_states.js)"
python glass.py shot vr:controllerbindingui vrf_bind_hover_after --theme on --pre "$(cat theme/vr/pre/bind_hover.js)"
-
Stock before-shots without stopping the watcher: add
(()=>{const h=document.documentElement; h.classList.remove('lgs-on'); setTimeout(()=>h.classList.add('lgs-on'), 5000)})()after the state script in the--preof a--theme onshot. -
Panel sizes:
--pre "$(cat theme/vr/pre/panel_sizes.js)"(afternp_open.jsfor Now Playing). -
Now Playing (showcase only):
- Check with
../.claude/skills/run-liquid-glass-frame/driver.py statusthat it is inactive, then rundriver.py start. - Run each step as
--pre "(async()=>{ await $(cat docs/inventory/steamvr-pre/np_open.js); window.__LGS_NP = {focus: 3, hover: 0}; return await $(cat theme/vr/pre/np_states.js) })()". The Steam frame can come back between steps, so reopen Now Playing in every step. - Run
driver.py stop.
- Check with
-
SteamVR settings:
- Hold the Steam lab lock first:
python glass.py js "(async()=>{window.__LGS_VR_HOLD=Date.now(); await L.sleep(50000); window.__LGS_VR_HOLD=0})()" &. Keep it under the 60 s eval timeout. - Use
theme/vr/pre/settings_open.js. It stays 9 s, so an audit fits, and it returns to Steam main even if a previous run is still showing settings. Prefix it withwindow.__LGS_SET_HOVER = 1,for the hover marks; follow it withtheme/vr/pre/settings_mock.jsfor the radio and detent mocks (the page opens on the user's last section, which may have neither). - Leave ≥ 9.5 s between runs.
- Hold the Steam lab lock first:
-
Audits: use
theme/vr/pre/audit_inpage.jsas the--preof ashot --theme on, after the state script:--pre "(async()=>{ await <state>; return await <audit_inpage.js> })()". It toggleshtml.lgs-oninside the page with the lab's ownL.snap/L.diff, so it cannot be fooled by thelgs-vrwatcher, and it printsthemeApplied.python glass.py audit vr:PAGEgives the same numbers when nothing strips the theme mid-step.
Caveats found
- Other agents' Steam theme toggles strip the SteamVR pages. The
lgs-vrwatcher strips SteamVR pages whenever the Steam theme goes off. Consequences:--theme keepshots can come out stock: two did, and were re-shot.- A
glass.py audit vr:…can compare stock against stock and report a false 0. - The in-page audit above avoids both.
- The audit cannot see pseudo-element backgrounds, and it ignores
color(srgb …)gradient stops. Dark labels on white selections therefore need the white painted on the element itself:- Segmented controls: the selected option paints the white too, appearing when SteamVR's sliding
::afterpill arrives (transition delay = SteamVR's own switch time), so the slide animation still shows. - Sidebar: the current section's white rounded row is painted on the button from 7 gradient layers in opaque
--lgs-knobwhite (overlapping layers, no seams).
- Segmented controls: the selected option paints the white too, appearing when SteamVR's sliding
- SteamVR marks the selected radio
Disabled. The disabled dimming therefore only applies to.Disabled:not(.Selected). - White on the play tint (.86) is 2.4:1 at Now Playing's 36px. Resume rests deep (under
--lgs-scrim) like Steam's Play capsule, and its lit state (hover, focus) is the play tint with 18% black (≥ 3.5:1), not the full tint. - Deviations from DESIGN.md §9, for legibility (design owner to confirm): the resize corner's stroke is
--lgs-text-1, not--lgs-text-2(a translucent stroke over its own dark halo turns grey; see the fix pass), and the grab bar carries a dark two-tone edge besides its white rim, because without a blurrable backdrop a white-only bar disappears over a bright room. - Hover emulation can't undo
:not(:hover). The frame controls' idle fade and opaque idle tint are written as:not(:hover); the[data-lgs-hover]clone can't lift them, so hover shots show the idle capsule tint (a real hover shows the .73 panel glass).
Gaps (not verified live)
- More Options popout: opening it moves input focus (inventory §2.5.2). Verified on a static mock with SteamVR's classes only.
- Now Playing's VR Controller Bindings / VR Video Settings modals: styled from source (
.SettingsMain .Modal, thick glass). Opening them means pressing those buttons, which is not allowed. - SteamVR settings, other sections: only General was rendered. Not rendered: other sidebar sections, dropdown popovers, section modals, the restart banner, colour sliders (hue/alpha tracks are deliberately left alone), and password fields. All are styled from source.
- Binding editor edit mode: not opened (Edit/Create change bindings). This covers chord, pose and input-settings modals,
SourceModeGroup.Edit, simulated actions, the tracker editor and the debugger. They get generic fills from source. - SteamVR keyboard: DOM emulation only (
kb_show.js), so it is not the real overlay. The "flat" head-locked presentation was not seen. The shift planes' active-key inline style is handled by the same[style*="background"]rule. - Mock-only panels: message overlay, notification toast, binding callouts, app transition, IPD, progress ring, framerate, gamepad unsupported, room setup, Steam loading and the guided tour have no side-effect-free trigger. They were verified on static mocks only. The toast mock borrows
notificationtoast.css; its icon path did not load in the mock. - Controller status: seen with no role (empty, so no card), with battery only, with a right hand, and in gamepad mode. App-icon gamepad mode (
ControllerGamepadIconWithApp), the low / charging PNGs and the verticalvert_battery set were not seen (the low and charging PNGs 404 on this build anyway).controller_model_lowgets the red-pip SVG; charging andvert_keep the hue-rotated PNG. - SteamVR settings radios: the page now opens on the user's last section (Dashboard), which has none; the radio hover was verified on a mock row built from SteamVR's classes inside the real page, not by switching the user's section.
- Theater/dark mode: SteamVR multiplies the grab and resize panels by 0.3 there. Not checked in the headset.
- Not styled at all: the legacy SteamVR dashboard (unused on the Frame), dev UIs (
inputfocusdevui,framedevui,vrlink-info,testing-stuff),ErrorBoundary,WakingBaseStationsPanel(Lighthouse only), and desktop-only pages (settings_desktop,systemreportviewer,debugcommands).