mirror of
https://github.com/MichaelScottsman/LiquidAss-Frame.git
synced 2026-10-17 17:01:09 +02:00
A denser tint (.46 + blur -> .6) on the small chip over the art. Route changes ~51 -> ~56 fps.
1400 lines
53 KiB
CSS
1400 lines
53 KiB
CSS
/* Glass Shell, area "library" (package C2c): the library catalogue.
|
|
Routes: /library/tab/* and /library/collection/* (glass mode `window`),
|
|
the laser Sort & Filter pill (the toolbar row's trailing group), the
|
|
library's ornament members (and the five fixed slots behind
|
|
libFixedSlots), the grid's secondary chrome, the letter scrubber and
|
|
Library Filters' content.
|
|
Concept: docs/phase2/concepts/home-apps.md (HA) §7-§9; PLAN §1.3, §1.4,
|
|
§1.10, §1.12; WN §3.4.6. Home (/library/home) is C2a's (41-home.css).
|
|
|
|
Wrapped as html.lgs-on { ... } by the injector. Steam's geometry is kept
|
|
(poster size, columns, virtualized rows, the tab row's horizontal
|
|
handlers, the VR sub-filter's place); we move the header rows below the
|
|
toolbar row (C1a's --lgs-hdr-pad), restyle, and put Steam's laser Sort &
|
|
Filter pill in the toolbar row's trailing side (D-C2c-12).
|
|
|
|
Hooks this file reads:
|
|
#Footer[data-lgs-orn-fixed="library"] C1a T2: fixed slots registered
|
|
(flag libFixedSlots, off)
|
|
[data-lgs-lib-sort] on the Y label C2c T2: the current sort name
|
|
%{Collection}[data-lgs-empty] C2c T2: an empty collection
|
|
%{Collection}[data-lgs-dwell] C2c T2: a dwell host (P3)
|
|
%{GridWithControls}[data-lgs-scrub] C2c T3: the scrubber is mounted
|
|
.lgs-lib-scrub (+ -track, -list, -l, C2c T3: the scrubber's nodes
|
|
-dot, -cur) (flag libScrubber)
|
|
%{FilterBucket}.lgs-lib-chips C2c T3: a Filters section drawn
|
|
as toggle capsules (flag
|
|
libFilterChips) */
|
|
|
|
& {
|
|
/* the library header: the tab row and the sub-filter, 80 px boxes each */
|
|
--lgs-lib-row: 80px;
|
|
--lgs-lib-hdr-top: var(--lgs-hdr-pad, 68px);
|
|
/* Steam's gamepad focus grows a poster by its own transform (x1.053:
|
|
7 px at each end of a 258 px poster, 4 px on a 186 px collection
|
|
tile), and Steam's own focus scroll lands up to 9 px past the scroll
|
|
padding on collection pages (R2F-P23); the scroll guard keeps the grown
|
|
box inside y 124-612 with that margin */
|
|
--lgs-lib-lift-room: 16px;
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* 1. Header rows below the toolbar row; the grid's scroll guard */
|
|
/* ==================================================================== */
|
|
|
|
/* Steam pins its header (tab row + sub-filter) at the top of the tabbed
|
|
page (y 40). The toolbar row's fallback (C1a, lgs-hdr-40) puts the
|
|
search capsule at y 22-86, so the header moves down by --lgs-hdr-pad
|
|
(68; 0 with lgs-hdr-108) and the grid starts below it (y 270). */
|
|
%{GamepadLibrary} %{TabHeaderRowWrapper} {
|
|
top: var(--lgs-lib-hdr-top);
|
|
}
|
|
/* Steam's pinned band (shown once the grid scrolls under the header)
|
|
becomes the scroll edge, reaching up under the toolbar row */
|
|
%{GamepadLibrary} %{TabHeaderRowWrapper}::before {
|
|
top: calc(-40px - var(--lgs-lib-hdr-top));
|
|
background: var(--lgs-scroll-band-top);
|
|
backdrop-filter: blur(12px);
|
|
-webkit-mask-image: linear-gradient(180deg, #000 72%, transparent 100%);
|
|
mask-image: linear-gradient(180deg, #000 72%, transparent 100%);
|
|
box-shadow: none;
|
|
}
|
|
/* the grid starts under the two 80 px rows (Steam: 118 for 58 + 46) */
|
|
%{GamepadLibrary} %{TabContentsScroll} {
|
|
padding-top: calc(var(--lgs-lib-hdr-top) + 154px) !important;
|
|
/* room after the last row, so it can scroll above the ornament guard
|
|
like every other row (Steam ends the grid 40 px above the window
|
|
bottom; the Collections tab could not scroll at all): the scroller
|
|
ends at y 720, so 720 - 612 + the focus growth (VP P-23, PLAN R2-11) */
|
|
padding-bottom: calc(720px - var(--lgs-guard-bottom, 612px) + var(--lgs-lib-lift-room)) !important;
|
|
/* focused posters stay below the header rows (window y 270) and above
|
|
the ornament guard (612) with Steam's focus growth counted: VP P-23,
|
|
C1a's guard variables */
|
|
scroll-padding-top: calc(var(--lgs-lib-hdr-top) + 162px + var(--lgs-lib-lift-room));
|
|
scroll-padding-bottom: calc(720px - var(--lgs-guard-bottom, 612px) + var(--lgs-lib-lift-room));
|
|
}
|
|
/* Tabs without the sub-filter row (Collections, Non-Steam, Soundtracks,
|
|
collection pages) keep the same grid top: rows land where they do on All
|
|
Games, so the ornament never covers part of a row's 80 px hit band
|
|
without covering a quarter of it (P-08's obscured rule). Review R2 m7
|
|
asked for the grid under the tab row; measured in the R2 fix pass, the
|
|
second row then sat with 19 % of its hit band under the ornament and
|
|
failed P-08 on the VR collection (gates 19:33), so the band stays. */
|
|
|
|
/* ==================================================================== */
|
|
/* 2. Tabs: Steam's tab row as a segmented control (CTL §8.3, E-SEG) */
|
|
/* ==================================================================== */
|
|
|
|
/* A 64 px recessed track; segments contiguous, 60 visible and >= 140 wide
|
|
inside 80 px boxes (10 px transparent borders, so the hit is 80); 22 px
|
|
Semibold in Steam's own case; selected = white .94 with a dark label. */
|
|
%{GamepadLibrary} %{TabRow} {
|
|
height: var(--lgs-lib-row);
|
|
min-height: var(--lgs-lib-row);
|
|
align-items: center;
|
|
}
|
|
%{GamepadLibrary} %{FixCenterAlignScroll} {
|
|
height: var(--lgs-lib-row);
|
|
align-items: center;
|
|
/* contiguous segments (Steam leaves 2 px between tabs) */
|
|
gap: 0;
|
|
anchor-name: --lgs-lib-tabscroll;
|
|
}
|
|
%{GamepadLibrary} %{FixCenterAlignScroll} > :nth-child(1 of %{GamepadTabbedPage>Tab}) {
|
|
anchor-name: --lgs-lib-tab-first;
|
|
}
|
|
%{GamepadLibrary} %{FixCenterAlignScroll} > :nth-last-child(1 of %{GamepadTabbedPage>Tab}) {
|
|
anchor-name: --lgs-lib-tab-last;
|
|
}
|
|
%{GamepadLibrary} %{FixCenterAlignScroll} > :nth-child(1 of %{GamepadTabbedPage>Tab}):nth-last-child(1 of %{GamepadTabbedPage>Tab}) {
|
|
anchor-name: --lgs-lib-tab-first, --lgs-lib-tab-last;
|
|
}
|
|
/* the track: our ::before on Steam's tab row (Steam draws none there),
|
|
held inside the tab scroller's box when the row overflows. (Not the
|
|
wrapper's ::after: Phase 1's legacy layer rule "tabs" pops that pseudo
|
|
forward in native mode, and this track is recessed, PLAN §1.7.) */
|
|
%{GamepadLibrary} %{TabHeaderRowWrapper}::after {
|
|
content: none;
|
|
}
|
|
%{GamepadLibrary} %{TabRow} {
|
|
position: relative;
|
|
isolation: isolate;
|
|
}
|
|
%{GamepadLibrary} %{TabRow}::before {
|
|
content: "";
|
|
position: absolute;
|
|
z-index: -1;
|
|
top: calc(anchor(--lgs-lib-tab-first top, 0px) + 8px);
|
|
height: var(--lgs-seg);
|
|
left: max(calc(anchor(--lgs-lib-tab-first left, 100%) - 2px), anchor(--lgs-lib-tabscroll left, 0px));
|
|
right: max(calc(anchor(--lgs-lib-tab-last right, 0px) - 2px), anchor(--lgs-lib-tabscroll right, 0px));
|
|
border-radius: var(--lgs-r-capsule);
|
|
background: var(--lgs-fill-thick);
|
|
box-shadow: var(--lgs-recessed-shadow);
|
|
pointer-events: none;
|
|
}
|
|
%{GamepadLibrary} %{GamepadTabbedPage>Tab} {
|
|
position: relative;
|
|
isolation: isolate;
|
|
box-sizing: border-box;
|
|
height: var(--lgs-lib-row);
|
|
max-height: none;
|
|
min-width: var(--lgs-seg-min-w);
|
|
width: max-content !important;
|
|
flex: 0 0 auto !important;
|
|
gap: 6px;
|
|
margin: 0;
|
|
padding: 0 16px;
|
|
border: 10px solid transparent;
|
|
border-inline-width: 0;
|
|
background-clip: padding-box;
|
|
justify-content: center;
|
|
border-radius: var(--lgs-r-capsule);
|
|
background-color: transparent;
|
|
color: var(--lgs-text-1);
|
|
box-shadow: none !important;
|
|
--lgs-ill: raised;
|
|
--lgs-ill-inset: 10px 0;
|
|
--lgs-ill-z: -1;
|
|
transition: background-color var(--lgs-motion-snappy), color var(--lgs-motion-fade);
|
|
}
|
|
%{GamepadLibrary} %{GamepadTabbedPage>Tab} %{GamepadTabbedPage>TabTitle} {
|
|
font: 600 22px/28px var(--lgs-font);
|
|
text-transform: none;
|
|
letter-spacing: 0;
|
|
}
|
|
%{GamepadLibrary} %{GamepadTabbedPage>Tab}:hover {
|
|
background-color: transparent;
|
|
color: var(--lgs-text-1);
|
|
}
|
|
%{GamepadLibrary} %{GamepadTabbedPage>Tab} %{TabCount} {
|
|
font: 500 22px/28px var(--lgs-font);
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
margin: 0;
|
|
color: var(--lgs-text-2);
|
|
}
|
|
%{GamepadLibrary} %{GamepadTabbedPage>Tab}%{GamepadTabbedPage>Selected},
|
|
%{GamepadLibrary} %{GamepadTabbedPage>Tab}%{GamepadTabbedPage>Selected}:hover {
|
|
background-color: var(--lgs-selected-fill) !important;
|
|
color: var(--lgs-text-on-selected) !important;
|
|
box-shadow: none !important;
|
|
--lgs-ill: white;
|
|
}
|
|
%{GamepadLibrary} %{GamepadTabbedPage>Tab}%{GamepadTabbedPage>Selected} %{TabCount} {
|
|
color: var(--lgs-text-on-selected-2) !important;
|
|
opacity: 1;
|
|
}
|
|
/* Gamepad focus on a segment (tabs, sub-filter; D-C2c-16). A white .94
|
|
selection leaves no room for an additive focus light, so focus is the
|
|
tvOS look: the focused segment is the brightest thing in the control
|
|
(opaque white, dark label) with a strong outer glow (P-16), and while
|
|
focus is inside a control its selected-but-unfocused segment recedes to
|
|
the navigation-selected look (white .18 + arc), so focus and selection
|
|
are never the same look (P-15). Steam selects a tab when it takes focus,
|
|
so a focused tab is also the selected one. */
|
|
&:not(.lgs-input-laser) %{GamepadLibrary} %{GamepadTabbedPage>Tab}.gpfocus {
|
|
background-color: rgb(255 255 255) !important;
|
|
background-clip: padding-box !important;
|
|
color: var(--lgs-text-on-selected) !important;
|
|
box-shadow: none !important;
|
|
--lgs-ill: white;
|
|
--lgs-white-glow-spread: 14px;
|
|
--lgs-white-glow-a: .70;
|
|
}
|
|
&:not(.lgs-input-laser) %{GamepadLibrary} %{GamepadTabbedPage>Tab}.gpfocus %{TabCount} {
|
|
color: var(--lgs-text-on-selected-2) !important;
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Steam's previous / next tab arrows: 60 px plain circles in 80 px boxes */
|
|
%{GamepadLibrary} %{TabHeaderRowWrapper} %{Arrows} {
|
|
position: relative;
|
|
isolation: isolate;
|
|
box-sizing: border-box;
|
|
width: 80px;
|
|
height: 80px;
|
|
min-width: 80px;
|
|
padding: 16px;
|
|
border: 10px solid transparent;
|
|
border-radius: 50%;
|
|
background: var(--lgs-raised-bg);
|
|
background-clip: padding-box;
|
|
box-shadow: none;
|
|
color: var(--lgs-text-1);
|
|
--lgs-ill: raised;
|
|
--lgs-ill-inset: 10px;
|
|
--lgs-ill-z: -1;
|
|
transition: background var(--lgs-motion-fade);
|
|
}
|
|
%{GamepadLibrary} %{TabHeaderRowWrapper} %{Arrows} svg {
|
|
width: 28px;
|
|
height: 28px;
|
|
}
|
|
&:not(.lgs-input-pad) %{GamepadLibrary} %{TabHeaderRowWrapper} %{Arrows}:is(:hover, :active),
|
|
&:not(.lgs-input-laser) %{GamepadLibrary} %{TabHeaderRowWrapper} %{Arrows}.gpfocus {
|
|
background: var(--lgs-raised-bg) !important;
|
|
background-clip: padding-box !important;
|
|
box-shadow: none !important;
|
|
color: var(--lgs-text-1) !important;
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* 3. The VR sub-filter (All · VR · Non-VR), in Steam's place */
|
|
/* ==================================================================== */
|
|
|
|
/* The same segmented control, 3 x 140. The radio group is wider than its
|
|
segments (Steam centres them), so the track is the group's ::after
|
|
anchored to the first and last segment. (C4a's %{Group} primitive skips
|
|
centred pills; this one is the library's.) */
|
|
%{GamepadLibrary} %{VRSubTabFilterContainer} {
|
|
height: var(--lgs-lib-row);
|
|
min-height: var(--lgs-lib-row);
|
|
transform-style: preserve-3d;
|
|
}
|
|
%{GamepadLibrary} %{VRSubTabFilterContainer} > %{VRLibraryFilterGroup} {
|
|
transform-style: preserve-3d;
|
|
}
|
|
%{VRLibraryFilterGroup} > :nth-child(1 of %{Group>Button}) {
|
|
anchor-name: --lgs-vrsub-first;
|
|
}
|
|
%{VRLibraryFilterGroup} > :nth-last-child(1 of %{Group>Button}) {
|
|
anchor-name: --lgs-vrsub-last;
|
|
}
|
|
%{VRLibraryFilterGroup} > :nth-child(1 of %{Group>Button}):nth-last-child(1 of %{Group>Button}) {
|
|
anchor-name: --lgs-vrsub-first, --lgs-vrsub-last;
|
|
}
|
|
%{VRLibraryFilterGroup}%{CenteredPill} {
|
|
background: none;
|
|
box-shadow: none;
|
|
border: 0;
|
|
gap: 0;
|
|
}
|
|
%{VRLibraryFilterGroup}%{CenteredPill}::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: calc(anchor(--lgs-vrsub-first top, 0px) + 8px);
|
|
height: var(--lgs-seg);
|
|
left: calc(anchor(--lgs-vrsub-first left, 100%) - 2px);
|
|
right: calc(anchor(--lgs-vrsub-last right, 0px) - 2px);
|
|
z-index: -1;
|
|
border-radius: var(--lgs-r-capsule);
|
|
background: var(--lgs-fill-thick);
|
|
box-shadow: var(--lgs-recessed-shadow);
|
|
pointer-events: none;
|
|
}
|
|
%{VRLibraryFilterGroup}%{CenteredPill} > %{Group>Button} {
|
|
position: relative;
|
|
isolation: isolate;
|
|
box-sizing: border-box;
|
|
height: var(--lgs-lib-row);
|
|
min-height: var(--lgs-lib-row);
|
|
min-width: var(--lgs-seg-min-w);
|
|
margin: 0;
|
|
padding: 0 20px;
|
|
border: 10px solid transparent;
|
|
border-inline-width: 0;
|
|
background-clip: padding-box;
|
|
border-radius: var(--lgs-r-capsule);
|
|
background-color: transparent;
|
|
font: 600 22px/28px var(--lgs-font);
|
|
text-transform: none;
|
|
letter-spacing: 0;
|
|
text-shadow: none;
|
|
color: var(--lgs-text-1);
|
|
--lgs-ill: raised;
|
|
--lgs-ill-inset: 10px 0;
|
|
--lgs-ill-z: -1;
|
|
transition: background-color var(--lgs-motion-snappy), color var(--lgs-motion-fade);
|
|
/* Steam's 1 px dividers */
|
|
&::before { background: transparent !important; }
|
|
}
|
|
/* the focused segment's glow paints over its neighbours */
|
|
&:not(.lgs-input-laser) %{VRLibraryFilterGroup}%{CenteredPill} > %{Group>Button}.gpfocus,
|
|
&:not(.lgs-input-laser) %{GamepadLibrary} %{GamepadTabbedPage>Tab}.gpfocus {
|
|
z-index: 2;
|
|
}
|
|
/* Steam's own hover and focus fills go back to rest (the light hook and
|
|
the focus rule below carry those states) */
|
|
%{VRLibraryFilterGroup}%{CenteredPill} > %{Group>Button}:not(%{Group>Active}, [aria-checked="true"]):hover,
|
|
&.lgs-input-laser %{VRLibraryFilterGroup}%{CenteredPill} > %{Group>Button}:not(%{Group>Active}, [aria-checked="true"]).gpfocus {
|
|
background-color: transparent !important;
|
|
color: var(--lgs-text-1);
|
|
}
|
|
%{VRLibraryFilterGroup}%{CenteredPill} > %{Group>Button}:is(%{Group>Active}, [aria-checked="true"]),
|
|
%{VRLibraryFilterGroup}%{CenteredPill} > %{Group>Button}:is(%{Group>Active}, [aria-checked="true"]):hover {
|
|
background-color: var(--lgs-selected-fill) !important;
|
|
background-clip: padding-box !important;
|
|
color: var(--lgs-text-on-selected) !important;
|
|
box-shadow: none !important;
|
|
--lgs-ill: white;
|
|
}
|
|
/* focus inside the sub-filter on another segment: the selection recedes
|
|
to the navigation-selected look (D-C2c-16; P-15) */
|
|
&:not(.lgs-input-laser) %{VRLibraryFilterGroup}%{CenteredPill}:has(> .gpfocus) > %{Group>Button}:is(%{Group>Active}, [aria-checked="true"]):not(.gpfocus) {
|
|
background-color: var(--lgs-fill-nav) !important;
|
|
color: var(--lgs-text-1) !important;
|
|
box-shadow: var(--lgs-sel-arc) !important;
|
|
--lgs-ill: raised;
|
|
}
|
|
/* gamepad focus on a sub-filter segment: as on the tabs above (D-C2c-16) */
|
|
&:not(.lgs-input-laser) %{VRLibraryFilterGroup}%{CenteredPill} > %{Group>Button}.gpfocus {
|
|
background-color: rgb(255 255 255) !important;
|
|
background-clip: padding-box !important;
|
|
color: var(--lgs-text-on-selected) !important;
|
|
box-shadow: none !important;
|
|
--lgs-ill: white;
|
|
--lgs-white-glow-spread: 14px;
|
|
--lgs-white-glow-a: .70;
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* 4. Posters (Steam's geometry: 172 x 258, 6 per row) */
|
|
/* ==================================================================== */
|
|
|
|
/* Content: opaque art, radius 20, no ring. Steam's focus outline (grow,
|
|
fade, 20 pulses) and its shine sweep go, so nothing runs at rest
|
|
(PLAN §1.5, P-42, P-52). The attention target lifts: x1.05 on the
|
|
independent `scale` (Steam's transform untouched), the +15 mm shadow
|
|
and a soft glow; gamepad focus at once, laser after the 80 ms dwell. */
|
|
%{GamepadLibrary} %{LibraryItemBox} {
|
|
border-radius: var(--lgs-r-card);
|
|
outline: none !important;
|
|
animation: none !important;
|
|
/* gamepad focus already grows by Steam's own focus transform (about
|
|
x1.07, kept); the laser's dwell lifts by x1.05 here */
|
|
scale: 1;
|
|
/* Steam's 300 ms transform transition is replaced by MO tokens */
|
|
transition-property: filter, box-shadow, transform, scale !important;
|
|
transition-duration: 0s, var(--lgs-d-hover-in), var(--lgs-d-fade), var(--lgs-d-fade) !important;
|
|
transition-timing-function: linear, var(--lgs-ease-b0), var(--lgs-ease-b0), var(--lgs-ease-b0) !important;
|
|
}
|
|
%{GamepadLibrary} %{LibraryItemBox} > %{PortraitImage>Container},
|
|
%{GamepadLibrary} %{LibraryItemBox} > %{PortraitImage>Container} > img {
|
|
border-radius: var(--lgs-r-card);
|
|
}
|
|
/* a poster without art (Steam's title card) is content, so it is opaque:
|
|
a neutral gradient tile, not a tint over the window (in native mode the
|
|
window's CSS tint is gone and a translucent tile let the room through
|
|
its title: 1.4 : 1, gates 19:50 with another agent's native session) */
|
|
%{GamepadLibrary} %{LibraryItemBox} > %{PortraitImage>Container}%{GreyBackground} {
|
|
background: linear-gradient(160deg, rgb(64 68 82), rgb(34 36 46));
|
|
}
|
|
%{GamepadLibrary} %{LibraryItemBox}::after {
|
|
border-radius: inherit;
|
|
}
|
|
%{GamepadLibrary} %{LibraryItemBoxShine} {
|
|
animation: none !important;
|
|
opacity: 0 !important;
|
|
}
|
|
/* at rest and on a plain laser hover (before the dwell): no ring; also on
|
|
a stale .gpfocus that Steam leaves under the laser (both input modes are
|
|
named, so the rule reads as deliberate, VP P-01) */
|
|
%{GamepadLibrary} %{LibraryItemBox}:is(.Focusable:focus, :hover),
|
|
&.lgs-input-laser %{GamepadLibrary} %{LibraryItemBox}.gpfocus,
|
|
&:not(.lgs-input-laser) %{GamepadLibrary} %{LibraryItemBox}.gpfocus {
|
|
box-shadow: none !important;
|
|
}
|
|
/* lifted (gamepad: focus; laser: after the dwell; no input runtime: hover) */
|
|
&:not(.lgs-input-laser) %{GamepadLibrary} %{LibraryItemBox}.gpfocus.gpfocus,
|
|
&.lgs-input-laser %{GamepadLibrary} %{LibraryItemBox}:is(.lgs-dwell:hover, .lgs-attend-80),
|
|
&:not(.lgs-input-laser):not(.lgs-input-pad) %{GamepadLibrary} %{PortraitImage>HoversEnabled} %{LibraryItemBox}:hover {
|
|
box-shadow: var(--lgs-shadow-15mm), 0 0 30px 2px rgb(255 255 255 / .16) !important;
|
|
--lgs-ill: card;
|
|
}
|
|
&.lgs-input-laser %{GamepadLibrary} %{LibraryItemBox}:is(.lgs-dwell:hover, .lgs-attend-80):not(.gpfocus) {
|
|
scale: var(--lgs-card-lift);
|
|
}
|
|
/* Steam's compatibility badge (shown on hover and focus): a dark chip */
|
|
/* (a denser tint, no backdrop blur: on 30 capsules at once the blur was 30
|
|
backdrop layers in every library frame, ~2 fps of route changes) */
|
|
%{GamepadLibrary} %{LibraryItemBox} %{SteamDeckCompatInfo} {
|
|
background: rgb(0 0 0 / .6);
|
|
box-shadow: none;
|
|
border: 0;
|
|
border-radius: var(--lgs-r-capsule);
|
|
}
|
|
/* missing art: Steam's title card, title 22 Bold */
|
|
%{GamepadLibrary} %{LibraryItemBox} %{PortraitImage>Title} {
|
|
font: 700 22px/1.25 var(--lgs-font);
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
color: var(--lgs-text-1);
|
|
text-shadow: var(--lgs-text-shadow);
|
|
}
|
|
/* uninstalled / download bar: capsule track, white fill */
|
|
%{GamepadLibrary} %{PortraitImage>UninstalledBar} {
|
|
background-color: var(--lgs-fill-thick);
|
|
border-radius: var(--lgs-r-capsule);
|
|
}
|
|
%{GamepadLibrary} %{PortraitImage>UninstalledBar} %{UninstalledProgressBar} {
|
|
background-color: var(--lgs-text-1);
|
|
border-radius: var(--lgs-r-capsule);
|
|
}
|
|
%{GamepadLibrary} %{FriendsBar} %{SummaryCount} {
|
|
color: var(--lgs-text-1);
|
|
text-shadow: var(--lgs-text-shadow);
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* 5. Grid chrome: the filter notice, section headers, fast scroll */
|
|
/* ==================================================================== */
|
|
|
|
/* "N apps hidden due to filter": a 60 px quiet capsule (white .08, Callout
|
|
Medium white .70); Steam's two 1 px rules go (P-43). The button variant
|
|
(clears the filter) is a 60 px text button with an 80 px hit. */
|
|
%{GamepadLibrary} %{AppGridFilterHeader} {
|
|
box-sizing: border-box;
|
|
min-height: 80px;
|
|
border: 10px solid transparent;
|
|
background-clip: padding-box;
|
|
border-radius: var(--lgs-r-capsule);
|
|
background-color: rgb(255 255 255 / .08);
|
|
box-shadow: none !important;
|
|
}
|
|
%{GamepadLibrary} %{AppGridFilterHeader}::before,
|
|
%{GamepadLibrary} %{AppGridFilterHeader}::after {
|
|
display: none;
|
|
}
|
|
%{GamepadLibrary} %{AppGridFilterHeader} %{AppGridFilterText} {
|
|
font: 500 22px/1.3 var(--lgs-font);
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
color: var(--lgs-text-2);
|
|
text-shadow: none;
|
|
}
|
|
%{GamepadLibrary} %{AppGridFilterHeader}%{AppGridFilterHeaderAsButton} {
|
|
background-color: var(--lgs-raised-bg, rgb(255 255 255 / .14));
|
|
isolation: isolate;
|
|
--lgs-ill: raised;
|
|
--lgs-ill-inset: 10px 0;
|
|
--lgs-ill-z: -1;
|
|
}
|
|
%{GamepadLibrary} %{AppGridFilterHeader}%{AppGridFilterHeaderAsButton} %{AppGridFilterText} {
|
|
font: 600 24px/1.3 var(--lgs-font);
|
|
color: var(--lgs-text-1);
|
|
}
|
|
|
|
/* sort sections ("Over 10 hours" ...): Title 3 in Steam's own case; the
|
|
rule goes; the sticky band becomes the scroll edge */
|
|
%{GamepadLibrary} %{AppGridSectionHeader} %{AppGridSectionLabel} {
|
|
font: var(--lgs-type-title3);
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
color: var(--lgs-text-1);
|
|
text-shadow: none;
|
|
}
|
|
%{GamepadLibrary} %{AppGridSectionHeader} %{LibraryImageBackgroundGlow>Rule} {
|
|
display: none;
|
|
}
|
|
%{GamepadLibrary} %{AppGridSectionHeader}::after {
|
|
background: var(--lgs-scroll-band-top);
|
|
box-shadow: none;
|
|
}
|
|
|
|
/* Steam's fast-scroll letter (gamepad): a thick glass circle */
|
|
%{FastScrollOverlay}%{FastScrollOverlay>Visible} {
|
|
background: var(--lgs-mat-thick-bg);
|
|
backdrop-filter: var(--lgs-mat-thick-blur);
|
|
box-shadow: var(--lgs-shadow-10mm);
|
|
border: 0;
|
|
border-radius: var(--lgs-r-capsule);
|
|
color: var(--lgs-text-1);
|
|
text-shadow: none;
|
|
}
|
|
|
|
/* empty grid message */
|
|
%{GamepadLibrary} %{EmptyGridMessageContainer},
|
|
%{GamepadLibrary} %{EmptyGridMessageContainer} %{EmptyGridMessageLine} {
|
|
font: var(--lgs-type-callout);
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
color: var(--lgs-text-2);
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* 6. Collections tab and collection pages */
|
|
/* ==================================================================== */
|
|
|
|
/* Steam's tiles (3D display case kept), radius 30; label 20 Semibold in
|
|
Steam's case; count Medium .70; empty collections dimmed (T2 tag). The
|
|
tile lifts like a poster. */
|
|
%{GamepadLibrary} %{Collection} {
|
|
border-radius: var(--lgs-r-platter);
|
|
outline: none !important;
|
|
box-shadow: none !important;
|
|
scale: calc(1 + (var(--lgs-card-lift) - 1) * var(--lgs-lift));
|
|
transition: scale var(--lgs-motion-fade), box-shadow var(--lgs-motion-hover-in), opacity var(--lgs-motion-fade);
|
|
}
|
|
%{GamepadLibrary} %{Collection} :is(%{CollectionBG}, %{CollectionImage}) {
|
|
border-radius: inherit;
|
|
}
|
|
/* Steam's hover and focus fill (a light blue, rgb(98 120 163)) goes: the
|
|
tile keeps its rest face, and the dwell (or
|
|
gamepad focus) lifts it, as on posters (P-19, P-89). T2 makes every tile
|
|
a dwell host (data-lgs-dwell), since Steam's tile is a role=link div that
|
|
P3's default dwell selector does not match, and its count's tooltip
|
|
source covers the tile (D-C2c-15). */
|
|
%{GamepadLibrary} %{Collection}:is(:hover, :focus),
|
|
&.lgs-input-laser %{GamepadLibrary} %{Collection}.gpfocus,
|
|
&:not(.lgs-input-laser) %{GamepadLibrary} %{Collection}.gpfocus {
|
|
background-color: rgb(49 61 83) !important;
|
|
}
|
|
&:not(.lgs-input-laser) %{GamepadLibrary} %{Collection}.gpfocus.gpfocus,
|
|
&.lgs-input-laser %{GamepadLibrary} %{Collection}:is(.lgs-dwell:hover, .lgs-attend-80),
|
|
&:not(.lgs-input-laser):not(.lgs-input-pad) %{GamepadLibrary} %{Collection}:hover {
|
|
box-shadow: var(--lgs-shadow-15mm), 0 0 30px 2px rgb(255 255 255 / .16) !important;
|
|
}
|
|
/* empty: the tile's face dims, its label keeps full contrast (P-39) */
|
|
%{GamepadLibrary} %{Collection}[data-lgs-empty] > %{CollectionImage} {
|
|
opacity: .62;
|
|
}
|
|
/* the 3D display case's mini capsules: Steam's hairline border goes */
|
|
%{GamepadLibrary} %{Collection} %{Container>CapsuleImage} {
|
|
border: 0 !important;
|
|
}
|
|
%{GamepadLibrary} %{Collection} %{CollectionLabel} {
|
|
font: 600 20px/1.25 var(--lgs-font);
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
color: var(--lgs-text-1);
|
|
text-shadow: var(--lgs-text-shadow);
|
|
}
|
|
%{GamepadLibrary} %{Collection} %{CollectionLabel} %{CollectionLabelCount} {
|
|
font: 500 20px/1.25 var(--lgs-font);
|
|
letter-spacing: 0;
|
|
color: var(--lgs-text-2);
|
|
}
|
|
/* Steam's count is a 20 px tall focusable tooltip source inside the tile;
|
|
it is spread over the whole tile (its text stays under the name), so
|
|
every point of the tile takes the laser and the tile's click (P-08,
|
|
P-80). The label fills the tile and stacks name and count at its foot. */
|
|
%{GamepadLibrary} %{Collection} > %{CollectionLabel} {
|
|
inset: 0 !important;
|
|
width: auto !important;
|
|
height: auto !important;
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: flex-end;
|
|
align-items: stretch;
|
|
/* Steam's name column: the tile less 21 px a side (same line breaks) */
|
|
padding: 0 21px 16px;
|
|
text-align: center;
|
|
}
|
|
%{GamepadLibrary} %{Collection} > %{CollectionLabel} > :not(.tool-tip-source) {
|
|
margin-bottom: 26px;
|
|
}
|
|
%{GamepadLibrary} %{Collection} > %{CollectionLabel} > .tool-tip-source {
|
|
position: absolute;
|
|
inset: 0;
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
align-items: flex-end;
|
|
justify-content: center;
|
|
padding: 0 21px 16px;
|
|
border-radius: inherit;
|
|
}
|
|
%{GamepadLibrary} %{Collection} > %{CollectionLabel} > .tool-tip-source > * {
|
|
flex: 1 1 auto;
|
|
text-align: center;
|
|
}
|
|
/* collection page title and the Collections header (Steam's own case) */
|
|
%{GamepadLibrary} :is(%{CollectionHeader}, %{CollectionsHeader}) {
|
|
font: var(--lgs-type-title3);
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
color: var(--lgs-text-1);
|
|
text-shadow: none;
|
|
}
|
|
%{GamepadLibrary} %{CollectionsHeaderHR} {
|
|
display: none;
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* 7. Steam's laser Sort & Filter pill: toolbar trailing items */
|
|
/* ==================================================================== */
|
|
|
|
/* Steam renders the pill only in laser mode, inside the page, at the
|
|
bottom right (y 628-685). The window's glass clip (C1a, y 656) cuts
|
|
page content there, so the pill could not show or take clicks in the
|
|
ornament (D-C2c-12). It becomes the toolbar row's trailing group, as
|
|
visionOS places a window's sort and filter: one 60 px capsule (raised
|
|
fill) holding Steam's two buttons, 80 px hits, right-aligned 24 px from
|
|
the glass edge, in the toolbar row (y 24-84). Steam's current sort and
|
|
"Filter" stay Steam's text, in Steam's case (P-84). The node, its
|
|
handlers and its laser-only rendering are untouched (no D-pad change). */
|
|
%{GamepadLibrary} %{SortAndFilterContainer} {
|
|
position: fixed;
|
|
z-index: 3;
|
|
box-sizing: border-box;
|
|
/* fixed inside Steam's transformed tabbed page (it starts at y 40, or
|
|
at 108 under lgs-hdr-108), so the toolbar row's y is reached from it */
|
|
top: calc(var(--lgs-hdr-pad, 68px) - 94px);
|
|
bottom: auto;
|
|
left: auto;
|
|
right: 24px;
|
|
height: 80px;
|
|
/* >= 20 px clear of the search capsule's visible right edge (x 900 on
|
|
section roots): 1256 - 920 = 336 (PLAN R2-2); the sort name ellipsizes
|
|
first, the inset and the gap never shrink */
|
|
max-width: 336px;
|
|
margin: 0;
|
|
padding: 0;
|
|
gap: 0;
|
|
display: flex;
|
|
align-items: stretch;
|
|
border: 0;
|
|
border-radius: 0;
|
|
background: none;
|
|
backdrop-filter: none;
|
|
box-shadow: none;
|
|
pointer-events: none;
|
|
isolation: isolate;
|
|
--lgs-edge: none;
|
|
}
|
|
/* the group's capsule: 60 visible inside the 80 box */
|
|
%{GamepadLibrary} %{SortAndFilterContainer}::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 10px 0;
|
|
z-index: -1;
|
|
border-radius: var(--lgs-r-capsule);
|
|
background: var(--lgs-raised-bg);
|
|
pointer-events: none;
|
|
}
|
|
%{SortAndFilterButton} {
|
|
position: relative;
|
|
isolation: isolate;
|
|
box-sizing: border-box;
|
|
height: 80px;
|
|
min-width: 0;
|
|
margin: 0;
|
|
padding: 0 18px;
|
|
gap: 8px;
|
|
border: 10px solid transparent;
|
|
border-inline-width: 0;
|
|
background-clip: padding-box !important;
|
|
/* square hit box (the capsule is the group's ::before and the light
|
|
layer's radius), so the corners take the laser too (P-08) */
|
|
border-radius: 0;
|
|
box-shadow: none !important;
|
|
justify-content: center;
|
|
align-items: center;
|
|
color: var(--lgs-text-1);
|
|
text-shadow: none;
|
|
pointer-events: auto;
|
|
--lgs-ill: raised;
|
|
--lgs-ill-inset: 10px 0;
|
|
--lgs-ill-r: var(--lgs-r-capsule);
|
|
--lgs-ill-z: -1;
|
|
}
|
|
/* Steam's own fills go (the group's capsule is the rest look), except
|
|
while the button is the source of its open menu: then C1c's white .94
|
|
with the dark label shows (PLAN §1.4; review R2 Q4), drawn as a 60 px
|
|
capsule inside the 80 px box (border-radius 30 / 40 leaves a 30 px
|
|
radius on the 60 px padding box) */
|
|
%{SortAndFilterButton}:not([data-lgs-menu-source]) {
|
|
background: none !important;
|
|
}
|
|
%{SortAndFilterButton}[data-lgs-menu-source] {
|
|
border-radius: 30px / 40px;
|
|
}
|
|
%{SortAndFilterButton},
|
|
%{SortAndFilterButton} * {
|
|
font-family: var(--lgs-font);
|
|
font-size: var(--lgs-fs-callout);
|
|
font-weight: 600;
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
}
|
|
%{SortAndFilterButton} :is(div, span):not(%{CompatFooterIcons}) {
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
/* the sort shrinks (its name ellipsizes); Filter keeps its width */
|
|
:nth-child(1 of %{SortAndFilterButton}) {
|
|
flex: 0 1 auto;
|
|
}
|
|
:nth-child(2 of %{SortAndFilterButton}) {
|
|
flex: none;
|
|
}
|
|
/* collection pages: the 640 search capsule ends at x 960, so the group
|
|
is at most 276 wide (x 980-1256); both buttons show Steam's label only
|
|
(the glyphs are decorations; the names stay), 14 px padding */
|
|
%{BasicUiRoot}[data-lgs-search="nested"] %{GamepadLibrary} %{SortAndFilterContainer} {
|
|
max-width: 276px;
|
|
}
|
|
%{BasicUiRoot}[data-lgs-search="nested"] %{SortAndFilterButton} {
|
|
padding: 0 14px;
|
|
}
|
|
%{BasicUiRoot}[data-lgs-search="nested"] %{SortAndFilterButton} svg {
|
|
display: none;
|
|
}
|
|
/* Steam's account-alert circle (#header_profile, C1a: a 60 px circle at
|
|
the 24 px inset) stays outermost; the group sits 20 px to its left */
|
|
&:has(#header_profile) %{GamepadLibrary} %{SortAndFilterContainer} {
|
|
right: 104px;
|
|
max-width: 256px;
|
|
}
|
|
&:has(#header_profile) %{BasicUiRoot}[data-lgs-search="nested"] %{GamepadLibrary} %{SortAndFilterContainer} {
|
|
max-width: 196px;
|
|
}
|
|
%{SortAndFilterButton} svg {
|
|
flex: none;
|
|
width: 22px;
|
|
height: 22px;
|
|
opacity: .70;
|
|
}
|
|
%{CompatFooterIcons} {
|
|
background-color: rgb(255 255 255 / .16);
|
|
box-shadow: none;
|
|
border-radius: var(--lgs-r-capsule);
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* 8. The ornament on library routes (PLAN §1.10, WN §3.4) */
|
|
/* ==================================================================== */
|
|
|
|
/* Default: C1a's capsule hugs Steam's legends in Steam's order (Filter,
|
|
Sort By, Options, Select, Back in gamepad mode; Options, Select, Back
|
|
under the laser), so it never shows empty slots when Steam drops its
|
|
X / Y / menu legends (focus on the header rows, laser mode). The Y
|
|
legend carries the current sort ("Sort By · Alphabetical", thin spaces
|
|
round the dot): at most 334 px, so "Alphabetical" fits whole and the
|
|
capsule stays <= 960 with all five members in the regular style (957);
|
|
a longer sort name ellipsizes with "…" inside
|
|
the label (review R2 Q1). Steam's label is a flex box, which never
|
|
draws an ellipsis (its text and our ::after are flex items), so this
|
|
label becomes a single line box. A capsule that would pass 960 turns
|
|
compact (C1a's T2: 20 px labels, 16 px padding) before anything is cut
|
|
further. */
|
|
%{BasicUiRoot} #Footer%{BasicFooter} [data-lgs-btn="OPTIONS"] {
|
|
max-width: 334px;
|
|
min-width: 0;
|
|
}
|
|
%{BasicUiRoot} #Footer%{BasicFooter} [data-lgs-btn="OPTIONS"] :is(%{ActionButtonLabel}, %{CompatFooterDescription}) {
|
|
display: block;
|
|
flex: 0 1 auto;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
/* "Sort By · <current sort>" (T2 copies Steam's sort name into the
|
|
attribute; no new string) */
|
|
%{BasicUiRoot} #Footer%{BasicFooter} [data-lgs-btn="OPTIONS"] [data-lgs-lib-sort]::after {
|
|
content: "\2009·\2009" attr(data-lgs-lib-sort);
|
|
font-weight: 500;
|
|
color: var(--lgs-text-2);
|
|
}
|
|
|
|
/* Flag libFixedSlots (off): PLAN §1.10's five fixed slots. C1a's T2 holds
|
|
the capsule at x 200-1080 (--lgs-orn-x, 880 wide); slots 300 · 130 ·
|
|
156 · 134 · 120, gap 4, padding 12; each member is its slot plus a 2 px
|
|
share of each gap, so neighbours' hit boxes abut (P-07). */
|
|
%{BasicUiRoot} #Footer%{BasicFooter}[data-lgs-orn-fixed="library"] %{ActionButtonLegend} {
|
|
position: absolute;
|
|
/* C1a lifts the ornament row (and its capsule) by --lgs-c1a-orn-lift */
|
|
top: calc(-1 * var(--lgs-c1a-orn-lift, 0px));
|
|
box-sizing: border-box;
|
|
height: var(--lgs-ornament, 84px);
|
|
margin: 0;
|
|
padding: 0 18px;
|
|
justify-content: center;
|
|
min-width: 0;
|
|
max-width: none;
|
|
--lgs-ill-inset: 12px 2px;
|
|
}
|
|
%{BasicUiRoot} #Footer%{BasicFooter}[data-lgs-orn-fixed="library"] [data-lgs-btn="OPTIONS"] {
|
|
left: calc(var(--lgs-orn-x, 200px) + 10px);
|
|
width: 304px;
|
|
}
|
|
%{BasicUiRoot} #Footer%{BasicFooter}[data-lgs-orn-fixed="library"] [data-lgs-btn="SECONDARY"] {
|
|
left: calc(var(--lgs-orn-x, 200px) + 314px);
|
|
width: 134px;
|
|
}
|
|
%{BasicUiRoot} #Footer%{BasicFooter}[data-lgs-orn-fixed="library"] [data-lgs-btn="MENU"] {
|
|
left: calc(var(--lgs-orn-x, 200px) + 448px);
|
|
width: 160px;
|
|
}
|
|
%{BasicUiRoot} #Footer%{BasicFooter}[data-lgs-orn-fixed="library"] [data-lgs-btn="OK"] {
|
|
left: calc(var(--lgs-orn-x, 200px) + 608px);
|
|
width: 138px;
|
|
}
|
|
%{BasicUiRoot} #Footer%{BasicFooter}[data-lgs-orn-fixed="library"] [data-lgs-btn="CANCEL"] {
|
|
left: calc(var(--lgs-orn-x, 200px) + 746px);
|
|
width: 124px;
|
|
}
|
|
%{BasicUiRoot} #Footer%{BasicFooter}[data-lgs-orn-fixed="library"] %{ActionButtonLegend} :is(%{ActionButtonLabel}, %{CompatFooterDescription}) {
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
%{BasicUiRoot} #Footer%{BasicFooter}[data-lgs-orn-fixed="library"] %{ActionButtonLegend} + %{ActionButtonLegend}:is([data-lgs-btn="OK"], [data-lgs-btn="CANCEL"]) {
|
|
margin-left: 0;
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* 9. Library Filters (Steam's dialog in C1c's sheet frame), T1 */
|
|
/* ==================================================================== */
|
|
|
|
/* C1c draws the sheet frame (thick glass, <= 960, under the toolbar row,
|
|
the close circle at (24, 24)). HA §9's T1: the sheet is 960 wide; a title
|
|
row pinned at its top holds the close circle and "Library Filters"
|
|
centred (review R2 Q2); the content scrolls under it in its own body
|
|
(the card stops scrolling), with scroll-edge fades; the compatibility
|
|
rows are 80 px single-choice rows in one platter with a trailing blue
|
|
check and Steam's icons; each FilterBucket is a platter of 80 px check
|
|
rows; Reset and Save are 60 px capsules at the end. Nothing is moved out
|
|
of its parent or hidden. */
|
|
/* the card: Steam's scroller (%{DialogWrapper}) is not a direct .Panel
|
|
child of ModalPosition, so C1c's generic sheet rule does not reach it
|
|
(REQ-10); it takes the same sheet look here. Steam's navy fill goes. */
|
|
%{DialogWrapper}:has(> %{DialogWrapper>CompatFilterDialog}) {
|
|
box-sizing: border-box;
|
|
width: var(--lgs-sheet-max-w, 960px) !important;
|
|
max-width: 100%;
|
|
/* C1c's modal box holds the card to 488 (552 on window-full); the card
|
|
is a column: the pinned title row, then the scrolling body */
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
border: 0;
|
|
border-radius: var(--lgs-r-sheet, 44px);
|
|
/* Steam's modal layer is the backdrop root here, so the blur cannot
|
|
reach the page: a denser tint under the material keeps the content
|
|
legible over posters (P-39) */
|
|
background: linear-gradient(rgb(20 22 30 / .62), rgb(20 22 30 / .62)), var(--lgs-mat-thick-bg) !important;
|
|
backdrop-filter: var(--lgs-mat-thick-blur);
|
|
box-shadow: var(--lgs-mat-thick-shade), var(--lgs-shadow-10mm);
|
|
--lgs-edge: thick;
|
|
animation: lgs-sheet-in var(--lgs-motion-sheet-in) backwards;
|
|
}
|
|
%{DialogWrapper>CompatFilterDialog} {
|
|
/* glass before content on entry (G-MOTION): the content waits for the
|
|
card's first quarter, as in C1c's sheets */
|
|
animation: lgs-sheet-content-in var(--lgs-motion-sheet-in) backwards;
|
|
box-sizing: border-box;
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
margin: 0;
|
|
padding: 24px 0 0;
|
|
border: 0;
|
|
border-radius: 0;
|
|
background: transparent !important;
|
|
box-shadow: none;
|
|
/* Body for Steam's unstyled text (section labels such as "Steam Frame
|
|
Compatibility:"): >= 18 px, >= 500 (P-38) */
|
|
font: 500 22px/1.3 var(--lgs-font);
|
|
letter-spacing: 0;
|
|
}
|
|
/* the title row: the close circle's row (y 24-84 of the card), the title
|
|
centred and clear of the circle (24 + 60 + 12 on each side) */
|
|
%{DialogWrapper>CompatFilterDialog} > .DialogHeader {
|
|
box-sizing: border-box;
|
|
flex: none;
|
|
height: 60px;
|
|
margin: 0;
|
|
padding: 0 96px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
text-align: center;
|
|
border: 0;
|
|
}
|
|
%{DialogWrapper>CompatFilterDialogTitle} {
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font: var(--lgs-type-title2);
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
color: var(--lgs-text-1);
|
|
text-shadow: none;
|
|
}
|
|
/* the body scrolls under the title row; 12 px and 56 px scroll-edge fades
|
|
(HA §9); the focused row is kept clear of both (P-23's intent) */
|
|
%{DialogWrapper>CompatFilterDialog} > %{DialogWrapper>DialogBody} {
|
|
box-sizing: border-box;
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
margin: 0;
|
|
padding: 12px 24px 32px;
|
|
overflow-x: hidden;
|
|
overflow-y: auto;
|
|
scroll-padding-block: 16px 56px;
|
|
-webkit-mask-image: linear-gradient(180deg, transparent 0, #000 12px, #000 calc(100% - 56px), transparent 100%);
|
|
mask-image: linear-gradient(180deg, transparent 0, #000 12px, #000 calc(100% - 56px), transparent 100%);
|
|
}
|
|
%{DialogWrapper>CompatFilterDialogDescription} {
|
|
margin: 0 0 16px;
|
|
font: var(--lgs-type-callout);
|
|
letter-spacing: 0;
|
|
color: var(--lgs-text-2);
|
|
}
|
|
/* "Steam Frame Compatibility:": a section label (CTL §10) */
|
|
%{DialogWrapper>CompatFilterDialog} > %{DialogWrapper>DialogBody} > div:not([class]) {
|
|
margin: 0 0 8px 24px;
|
|
font: 600 22px/1.3 var(--lgs-font);
|
|
color: var(--lgs-text-2);
|
|
}
|
|
%{CompatFilterOptions} {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0;
|
|
border: 0;
|
|
border-radius: 0;
|
|
background: none;
|
|
overflow: visible;
|
|
}
|
|
/* the four compatibility rows: one platter of contiguous 80 px rows, 2 px
|
|
apart (CTL §10); the label and Steam's description on two lines */
|
|
%{CompatFilterOptions} %{DialogWrapper>CompatFilterDialogRow} {
|
|
position: relative;
|
|
isolation: isolate;
|
|
box-sizing: border-box;
|
|
min-height: 80px;
|
|
margin: 0 0 2px;
|
|
padding: 10px 24px;
|
|
gap: 16px;
|
|
align-items: center;
|
|
border: 0;
|
|
border-radius: 0;
|
|
background-color: rgb(0 0 0 / .14);
|
|
color: var(--lgs-text-1);
|
|
font: 500 24px/1.3 var(--lgs-font);
|
|
box-shadow: none !important;
|
|
--lgs-ill: row;
|
|
--lgs-ill-z: -1;
|
|
}
|
|
%{CompatFilterOptions} %{DialogWrapper>CompatFilterDialogRow}:first-child {
|
|
border-radius: var(--lgs-r-platter) var(--lgs-r-platter) 0 0;
|
|
}
|
|
%{CompatFilterOptions} %{DialogWrapper>CompatFilterDialogRow}:last-child {
|
|
margin-bottom: 0;
|
|
border-radius: 0 0 var(--lgs-r-platter) var(--lgs-r-platter);
|
|
}
|
|
/* Steam's own hover and focus fills go back to the platter (the light
|
|
hook carries both states) */
|
|
%{CompatFilterOptions} %{DialogWrapper>CompatFilterDialogRow}:hover,
|
|
&:not(.lgs-input-laser) %{CompatFilterOptions} %{DialogWrapper>CompatFilterDialogRow}.gpfocus {
|
|
background-color: rgb(0 0 0 / .14) !important;
|
|
color: var(--lgs-text-1) !important;
|
|
}
|
|
%{DialogWrapper>CompatFilterLabel} {
|
|
font: 500 24px/1.3 var(--lgs-font);
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
}
|
|
%{DialogWrapper>CompatFilterDialogRow} %{DialogWrapper>CompatFilterDescription} {
|
|
font: var(--lgs-type-footnote);
|
|
letter-spacing: 0;
|
|
color: var(--lgs-text-2);
|
|
}
|
|
/* on the focus light the secondary line keeps 4.5 : 1 (P-39) */
|
|
&:not(.lgs-input-laser) %{DialogWrapper>CompatFilterDialogRow}.gpfocus %{DialogWrapper>CompatFilterDescription},
|
|
&:not(.lgs-input-laser) %{Filters>Container} %{FilterArea>Row}.gpfocuswithin %{Filters>Description} {
|
|
color: var(--lgs-text-1);
|
|
}
|
|
/* the row's grid: the label and Steam's icons on the first line, the
|
|
description across the row on the second, the check trailing (so every
|
|
description fits one line and the four rows are even, review R2 m4).
|
|
Steam's wrapper of label and description is a plain div, so it lets its
|
|
two lines take the grid's cells (display: contents); the radio becomes
|
|
the trailing check of a single-choice row (HA §9 T1; review R2 m8):
|
|
empty when not chosen, a blue check when chosen (a decoration: the row
|
|
is the control). Nothing focusable moves (D2 §12) */
|
|
%{CompatFilterOptions} %{DialogWrapper>CompatFilterDialogRow} {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr) auto 28px;
|
|
grid-template-areas: "label icons check" "desc desc check";
|
|
column-gap: 16px;
|
|
row-gap: 2px;
|
|
}
|
|
%{DialogWrapper>CompatFilterDialogRow} > div:not([class]) {
|
|
display: contents;
|
|
}
|
|
%{DialogWrapper>CompatFilterDialogRow} %{DialogWrapper>CompatFilterLabel} {
|
|
grid-area: label;
|
|
min-width: 0;
|
|
}
|
|
%{DialogWrapper>CompatFilterDialogRow} %{DialogWrapper>CompatFilterDescription} {
|
|
grid-area: desc;
|
|
min-width: 0;
|
|
}
|
|
%{DialogWrapper>CompatFilterDialogRow} %{DialogWrapper>CompatFilterDialogIcons} {
|
|
grid-area: icons;
|
|
}
|
|
%{DialogWrapper>CompatFilterDialogRow} %{DialogWrapper>RadioButton} {
|
|
grid-area: check;
|
|
flex: none;
|
|
width: 28px;
|
|
height: 28px;
|
|
margin: 0;
|
|
border: 0;
|
|
border-radius: 0;
|
|
background: none;
|
|
box-shadow: none;
|
|
}
|
|
%{DialogWrapper>CompatFilterDialogRow}%{DialogWrapper>Active} %{DialogWrapper>RadioButton} {
|
|
background: var(--lgs-blue);
|
|
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28 28' fill='none' stroke='black' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 14.5l5.5 5.5L22 8.5'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28 28' fill='none' stroke='black' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 14.5l5.5 5.5L22 8.5'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
}
|
|
/* Steam's divider under the rows: room only */
|
|
%{CompatFilterOptions} %{FilterSummary} {
|
|
margin: 16px 0 0;
|
|
border: 0;
|
|
background: none;
|
|
}
|
|
|
|
/* advanced filter buckets: platters with their section label */
|
|
%{Filters>Container} %{FilterBucket} {
|
|
margin: 0 0 16px;
|
|
padding: 8px 0;
|
|
border: 0;
|
|
border-radius: var(--lgs-r-platter);
|
|
background-color: rgb(0 0 0 / .14);
|
|
box-shadow: none;
|
|
}
|
|
%{Filters>Container} %{FilterBucket} %{FilterBucketLabel},
|
|
%{Filters>Container} %{SearchBucketLabel} {
|
|
margin: 0;
|
|
padding: 8px 24px 4px;
|
|
font: 600 22px/1.3 var(--lgs-font);
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
color: var(--lgs-text-2);
|
|
}
|
|
%{Filters>Container} %{FilterBucket} %{FilterBucketBoxes} %{FilterArea>Row} {
|
|
box-sizing: border-box;
|
|
min-height: 80px;
|
|
margin: 0 8px;
|
|
padding: 0;
|
|
border-radius: var(--lgs-r-row);
|
|
color: var(--lgs-text-1);
|
|
font: 500 24px/1.3 var(--lgs-font);
|
|
box-shadow: none !important;
|
|
transition: background-color var(--lgs-motion-fade);
|
|
}
|
|
&:not(.lgs-input-laser) %{Filters>Container} %{FilterBucket} %{FilterBucketBoxes} %{FilterArea>Row}.gpfocuswithin {
|
|
background-color: rgb(255 255 255 / var(--lgs-focus-add, .32)) !important;
|
|
color: var(--lgs-text-1) !important;
|
|
}
|
|
/* Steam's check line (its focusable tooltip source) fills its 80 px row,
|
|
so the row's whole width and height take the laser (P-08, P-80); the
|
|
label is Body 24 Medium. (C4a draws the check box itself.) */
|
|
%{Filters>Container} %{FilterArea>Row} > .tool-tip-source {
|
|
box-sizing: border-box;
|
|
flex: 1 1 auto;
|
|
width: 100%;
|
|
min-width: 0;
|
|
}
|
|
%{Filters>Container} %{FilterArea>Row} .DialogCheckbox_Container {
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
min-height: 80px;
|
|
margin: 0;
|
|
padding: 0 16px;
|
|
align-items: center;
|
|
}
|
|
%{Filters>Container} %{FilterArea>Row} .DialogCheckbox_Container :is(span, label, div):not(.DialogCheckbox, .DialogCheckbox *) {
|
|
font: 500 24px/1.3 var(--lgs-font);
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
}
|
|
%{Filters>Container} %{FilterBucket} %{FilterBucketBoxes} %{FilterArea>Row} %{Filters>Description} {
|
|
font: 500 20px/1.3 var(--lgs-font);
|
|
color: var(--lgs-text-2);
|
|
}
|
|
/* the drop-downs (Gamepad Support, Language) and the two text fields sit
|
|
in their platters at the 24 px inset */
|
|
%{Filters>Container} %{FilterBucket} %{CompatDropDown},
|
|
%{Filters>Container} %{SearchBoxes} .DialogInput_Wrapper {
|
|
margin: 8px 24px;
|
|
}
|
|
%{Filters>Container} %{SearchBoxes} {
|
|
margin: 0 0 16px;
|
|
}
|
|
/* tag search results drop-down (only while typing): a recessed list in
|
|
the sheet, not a second glass (P-45, review R2 m2) */
|
|
%{Filters>Container} %{FilterBucket}%{SearchResultsBox} %{FilterBucketBoxes} {
|
|
background: var(--lgs-fill-thick);
|
|
backdrop-filter: none;
|
|
box-shadow: none;
|
|
border-radius: var(--lgs-r-row);
|
|
}
|
|
/* Reset and Save: 60 px capsules at the end, in Steam's order */
|
|
%{Filters>Container} %{Filters>Buttons} {
|
|
gap: 12px;
|
|
margin: 8px 0 0;
|
|
}
|
|
|
|
/* a focused section (Steam's FilterBucket is itself a focusable group: the
|
|
D-pad moves from section to section and A enters one, as on stock) lights
|
|
as one platter with P4's light (focus + white .32, the spot and the arc),
|
|
instead of the generic FocusRing plate with its 24 px corners */
|
|
%{Filters>Container} %{FilterBucket} {
|
|
position: relative;
|
|
isolation: isolate;
|
|
--lgs-ill: raised;
|
|
--lgs-ill-r: var(--lgs-r-platter);
|
|
--lgs-ill-z: -1;
|
|
}
|
|
|
|
/* T3 (flag libFilterChips, 40-library.js): the section's options as toggle
|
|
capsules that wrap into lines (HA §9; D-C2c-17). The patch gives the
|
|
section flow-children "grid", so the D-pad follows what is drawn (D2
|
|
§12), and C4a's data-lgs-noprim (the capsule draws its own check). Each
|
|
capsule: Steam's row and its focusable check line as one 80 px hit box
|
|
(they abut across the 12 px gaps), a 60 px visible capsule (24 px
|
|
padding, 24 px Semibold); off = the raised fill, on = white .94 with a
|
|
dark label and a leading check. Without the patch the rows above stay. */
|
|
%{FilterBucket}.lgs-lib-chips %{FilterBucketBoxes} {
|
|
display: flex;
|
|
flex-direction: row;
|
|
flex-wrap: wrap;
|
|
align-items: flex-start;
|
|
gap: 0;
|
|
padding: 0 18px 4px;
|
|
}
|
|
%{FilterBucket}.lgs-lib-chips %{FilterBucketBoxes} %{FilterArea>Row} {
|
|
flex: 0 0 auto;
|
|
width: auto;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
margin: 0;
|
|
padding: 0;
|
|
border-radius: 0;
|
|
background: none !important;
|
|
}
|
|
%{FilterBucket}.lgs-lib-chips %{FilterArea>Row} > .tool-tip-source {
|
|
position: relative;
|
|
isolation: isolate;
|
|
box-sizing: border-box;
|
|
flex: none;
|
|
width: auto;
|
|
height: 80px;
|
|
padding: 0;
|
|
border: 10px solid transparent;
|
|
border-inline-width: 6px;
|
|
/* at least 168 wide: a short label keeps 85 % of Steam's 552 x 28 row
|
|
(G-AUD's SHRUNK); the label centres in it */
|
|
min-width: 168px;
|
|
justify-content: center;
|
|
border-radius: 36px / 40px;
|
|
background: var(--lgs-raised-bg) padding-box;
|
|
box-shadow: none;
|
|
color: var(--lgs-text-1);
|
|
transition: background-color var(--lgs-motion-fade), color var(--lgs-motion-fade);
|
|
}
|
|
%{FilterBucket}.lgs-lib-chips %{FilterArea>Row} > .tool-tip-source:has(.DialogCheckbox.Active) {
|
|
background: var(--lgs-selected-fill) padding-box;
|
|
color: var(--lgs-text-on-selected);
|
|
}
|
|
/* the capsule's light (hover, gamepad focus, press) is on Steam's check
|
|
line, the element that takes focus and fills the 60 px capsule; white
|
|
capsules show focus as the outer glow (P-16). Being a light host also
|
|
turns P4's FocusRing plate off for it */
|
|
%{FilterBucket}.lgs-lib-chips %{FilterArea>Row} .DialogCheckbox_Container {
|
|
position: relative;
|
|
isolation: isolate;
|
|
border-radius: 30px;
|
|
--lgs-ill: raised;
|
|
--lgs-ill-r: 30px;
|
|
--lgs-ill-z: -1;
|
|
}
|
|
%{FilterBucket}.lgs-lib-chips %{FilterArea>Row} .DialogCheckbox_Container:has(.DialogCheckbox.Active) {
|
|
--lgs-ill: white;
|
|
}
|
|
%{FilterBucket}.lgs-lib-chips %{FilterArea>Row} .DialogCheckbox_Container {
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
justify-content: center;
|
|
min-height: 60px;
|
|
height: 60px;
|
|
margin: 0;
|
|
padding: 0 24px;
|
|
gap: 8px;
|
|
align-items: center;
|
|
color: inherit;
|
|
}
|
|
%{FilterBucket}.lgs-lib-chips %{FilterArea>Row} .DialogCheckbox_Container :is(span, label, div):not(.DialogCheckbox, .DialogCheckbox *) {
|
|
font: 600 24px/1.2 var(--lgs-font);
|
|
letter-spacing: 0;
|
|
text-transform: none;
|
|
white-space: nowrap;
|
|
color: inherit;
|
|
}
|
|
/* the check: Steam's own 22 px check box, unscaled (C4a's 40 px circle and
|
|
its 80 px hit extender are for rows; the capsule is the hit here): a
|
|
faint disc while off, Steam's check drawn dark on the white capsule
|
|
while on; no light of its own (the capsule's light carries the states) */
|
|
%{FilterBucket}.lgs-lib-chips .DialogCheckbox {
|
|
flex: none;
|
|
scale: 1;
|
|
width: 22px;
|
|
height: 22px;
|
|
margin: 0;
|
|
border: 0;
|
|
border-radius: 50%;
|
|
background: rgb(255 255 255 / .16) !important;
|
|
box-shadow: none;
|
|
--lgs-ill: none;
|
|
&::before { inset: 0; }
|
|
}
|
|
%{FilterBucket}.lgs-lib-chips .DialogCheckbox.Active {
|
|
background: none !important;
|
|
--lgs-ill: none;
|
|
}
|
|
%{FilterBucket}.lgs-lib-chips .DialogCheckbox .SVGIcon_DialogCheck path {
|
|
stroke: var(--lgs-text-on-selected);
|
|
}
|
|
/* the row-focus fill of the T1 rows does not apply to capsules (their own
|
|
light carries focus) */
|
|
&:not(.lgs-input-laser) %{Filters>Container} %{FilterBucket}.lgs-lib-chips %{FilterBucketBoxes} %{FilterArea>Row}.gpfocuswithin {
|
|
background: none !important;
|
|
}
|
|
%{FilterBucket}.lgs-lib-chips %{FilterArea>Row} %{Filters>Description} {
|
|
display: block;
|
|
font: 500 18px/1.2 var(--lgs-font);
|
|
color: inherit;
|
|
opacity: .70;
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* 10. The letter scrubber (T3, flag libScrubber; HA §7) */
|
|
/* ==================================================================== */
|
|
|
|
/* While 40-library.js mounts the scrubber (Alphabetical, 4 rows or more)
|
|
the grid's columns step in by 72 px, so posters end at x 1172 and the
|
|
scrubber has its own column (D-C2c-2; the module unmounts it if Steam's
|
|
grid then loses a column) */
|
|
%{GamepadLibrary} %{GridWithControls}[data-lgs-scrub] {
|
|
padding-inline-end: 72px !important;
|
|
}
|
|
/* our node: an 80 px wide hit column at x 1186-1266, y 270-610, a 60 px
|
|
recessed capsule inside (black .14, the inset shade), letters 20 px
|
|
Semibold .70 with 6 px dots when they do not all fit, the current letter
|
|
in a 40 px white circle that travels on snappy. It is docked in a zero-
|
|
height sticky wrapper at the end of the grid's scroller, held at the
|
|
scroller's bottom edge (y 720), so it composites with the scrolling
|
|
content instead of over it (G-PERF) */
|
|
& .lgs-lib-scrub-dock {
|
|
position: sticky;
|
|
bottom: 0;
|
|
z-index: 4;
|
|
display: block;
|
|
width: 100%;
|
|
height: 0;
|
|
margin: 0;
|
|
padding: 0;
|
|
pointer-events: none;
|
|
}
|
|
& .lgs-lib-scrub {
|
|
position: absolute;
|
|
right: var(--lgs-scrub-right, 14px);
|
|
bottom: var(--lgs-scrub-bottom, 110px);
|
|
box-sizing: border-box;
|
|
width: 80px;
|
|
height: 340px;
|
|
touch-action: none;
|
|
user-select: none;
|
|
cursor: default;
|
|
pointer-events: auto;
|
|
--lgs-maxside: 340;
|
|
animation: lgs-mat-glass-in var(--lgs-motion-mat-in) backwards;
|
|
}
|
|
& .lgs-lib-scrub-track {
|
|
position: absolute;
|
|
inset: 0 10px;
|
|
border-radius: var(--lgs-r-capsule);
|
|
background: var(--lgs-fill-regular);
|
|
box-shadow: var(--lgs-recessed-shadow);
|
|
pointer-events: none;
|
|
transition: background-color var(--lgs-motion-fade);
|
|
}
|
|
&:not(.lgs-input-pad) .lgs-lib-scrub:is(:hover, .is-active) .lgs-lib-scrub-track {
|
|
background-color: rgb(255 255 255 / var(--lgs-hover-add, .08));
|
|
}
|
|
& .lgs-lib-scrub-list {
|
|
position: absolute;
|
|
inset: 20px 10px;
|
|
pointer-events: none;
|
|
}
|
|
/* each letter at its own share of the column (T3 writes --lgs-scrub-at) */
|
|
& .lgs-lib-scrub-list > * {
|
|
position: absolute;
|
|
left: 50%;
|
|
top: calc(var(--lgs-scrub-at, 0) * 100%);
|
|
translate: -50% -50%;
|
|
}
|
|
& .lgs-lib-scrub-l {
|
|
display: block;
|
|
height: 20px;
|
|
font: 600 20px/20px var(--lgs-font);
|
|
letter-spacing: 0;
|
|
color: var(--lgs-text-2);
|
|
}
|
|
& .lgs-lib-scrub-dot {
|
|
display: block;
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 50%;
|
|
background: var(--lgs-text-3);
|
|
}
|
|
& .lgs-lib-scrub-at {
|
|
display: block;
|
|
width: 0;
|
|
height: 0;
|
|
}
|
|
& .lgs-lib-scrub-cur {
|
|
position: absolute;
|
|
left: 50%;
|
|
top: 20px;
|
|
box-sizing: border-box;
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
background: var(--lgs-selected-fill);
|
|
box-shadow: var(--lgs-shadow-5mm);
|
|
color: var(--lgs-text-on-selected);
|
|
font: 600 22px/40px var(--lgs-font);
|
|
text-align: center;
|
|
/* the column's 300 px of letters */
|
|
translate: -50% calc(var(--lgs-scrub-at, 0) * 300px - 20px);
|
|
pointer-events: none;
|
|
transition: translate var(--lgs-motion-snappy);
|
|
}
|
|
& .lgs-lib-scrub-cur:empty {
|
|
opacity: 0;
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* 11. Reduce Motion, High Contrast */
|
|
/* ==================================================================== */
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
%{GamepadLibrary} :is(%{LibraryItemBox}, %{Collection}) {
|
|
scale: 1;
|
|
}
|
|
}
|
|
@media (prefers-contrast: more) {
|
|
%{GamepadLibrary} %{TabHeaderRowWrapper}::before {
|
|
background: var(--lgs-mat-thick-bg);
|
|
backdrop-filter: none;
|
|
-webkit-mask-image: none;
|
|
mask-image: none;
|
|
}
|
|
%{GamepadLibrary} %{SortAndFilterContainer}::before {
|
|
background: var(--lgs-mat-thick-bg);
|
|
}
|
|
}
|