/* 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 · " (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); } }