Files
MichaelScottsman--LiquidAss…/theme/21-search.css
T

1050 lines
36 KiB
CSS
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Glass Shell phase 2: search (package C1b, T1 + the T3 hooks).
Concept: docs/phase2/concepts/window-nav.md (WN) §4 and §9.1; PLAN §1.2,
§1.3, §1.6-§1.9, §1.12. Evidence: docs/phase2/wp/C1b.md.
Contracts: tokens.md (P4: materials, --lgs-edge, --lgs-ill, --lgs-lift),
motion.md (P5: lgs-sheet-in, lgs-sheet-content-in, lgs-fade-in).
Wrapped as html.lgs-on { ... } by the bundler ("&" is html.lgs-on).
Search is a sheet over the page you were on (PLAN §1.9, WN §4.2):
- T1 (always): Steam's own search page becomes the sheet. Its tabbed
page (%{GamepadTabbedPage}) is a 960 x 500 thick-glass slab at
(160, 100) under the toolbar field, its tab row is the scope bar (one
64 px segmented track, 56 px segments, title case, Steam's counts),
Steam's virtualised grid scrolls inside under a 26 px fade, and the
rest of the window is dimmed by the .35 scrim (PLAN §1.8).
- T3 (device/rt/21-search.js, flag wp.c1b, html.lgs-c1b): the
snapshot of the page you left (div.lgs-snap) under our scrim
(.lgs-search-scrim, a click goes back), the zero state
([data-lgs-search="sheet"][data-lgs-search-state="zero"], 880 x 500
at (200, 100)), and Steam's page hosted as the sheet once you type.
This file took over Phase 1's search-route rules from 20-shell.css §6
(PLAN §6 two-step move; C1a deleted §6 on 2026-10-07).
Never touched: Steam's handlers, the grid's virtualisation, the tab
row's scroll or ModalPosition. Only geometry, paint and type. */
/* ------------------------------------------------------------------ */
/* 0. Inputs */
/* ------------------------------------------------------------------ */
%{GamepadSearch},
.lgs-search-layer,
.lgs-search-zero,
.lgs-search-glass {
--lgs-c1b-x: 160px; /* sheet left (centred: (1280 - 960) / 2) */
--lgs-c1b-y: 108px; /* PLAN §1.12 [R2-6]: sheets from y 108,
under the toolbar row (the field ends
at 86) */
--lgs-c1b-w: var(--lgs-sheet-max-w, 960px);
--lgs-c1b-h: 488px; /* R2-6: <= 488 on `window` routes; bottom
596, centre y 352 (P-35: 24 px of the
glass centre 328); ornament from 628 */
--lgs-c1b-bar: 96px; /* scope bar band: 16 + 64 track + 16 */
--lgs-c1b-pad: 14px; /* results inset: WN's 32 would cut Steam's
five columns to 170 px (AUD SHRUNK < .85);
14 keeps them at 177 x 82 */
}
/* ------------------------------------------------------------------ */
/* 1. The dimmed page and the sheet */
/* ------------------------------------------------------------------ */
/* T1: nothing of the page you left is mounted, so the scrim dims the
window glass itself (black .35, PLAN §1.8; the toolbar row at z 6000
and the ornament at 7000 stay above it). With T3 the scrim is our own
element over the snapshot, and Steam's page lets clicks through to it
outside the sheet. */
&:not(.lgs-c1b) %{GamepadSearch} {
background: var(--lgs-scrim, rgb(0 0 0 / .35));
animation: lgs-fade-in var(--lgs-motion-fade, 441ms linear) backwards;
}
&.lgs-c1b %{GamepadSearch} {
background: none;
}
/* With T3 the glass is our own slab (.lgs-search-glass, same rect) in the
picture layer under Steam's content: Steam's route content lives in a
transformed 3D context in which a backdrop-filter samples nothing (probe:
brightness(.15) left the snapshot behind it untouched). Steam's page
keeps the content, the clip and the radius. */
&.lgs-c1b %{GamepadSearch} > %{GamepadTabbedPage} {
z-index: 3;
background: none !important;
backdrop-filter: none !important;
box-shadow: none !important;
--lgs-edge: none;
}
/* The sheet: thick glass (PLAN §1.6), radius 44, ≤ 960 wide (§1.12),
the 10 mm depth shadow in CSS-only mode; in native mode the whole
sheet is a +10 mm crop over a thick slab (theme/layers/21-search.json)
and 05-native.css zeroes the --lgs-mat-thick-* paint under the pop. */
%{GamepadSearch} > %{GamepadTabbedPage},
.lgs-search-glass {
position: absolute !important;
z-index: 2; /* over the snapshot (0) and the scrim (1) */
left: var(--lgs-c1b-x) !important;
top: var(--lgs-c1b-y) !important;
width: var(--lgs-c1b-w) !important;
height: var(--lgs-c1b-h) !important;
min-height: 0 !important;
max-height: none !important;
box-sizing: border-box;
border-radius: var(--lgs-r-sheet, 44px);
overflow: hidden;
background: var(--lgs-mat-thick-bg);
backdrop-filter: var(--lgs-mat-thick-blur);
box-shadow: var(--lgs-mat-thick-shade), var(--lgs-shadow-10mm);
--lgs-edge: thick;
--lgs-edge-z: 3;
/* present (MO §4.8): sheet-in 735 from the field; the swell starts at
.985, not the preset's .97: the sheet is 960 wide, and VP P-53 allows a
surface wider than 600 px no scale change over 1.5 % */
transform-origin: 50% 0;
--lgs-sheet-s0: .985;
animation: lgs-sheet-in var(--lgs-motion-sheet-in, 735ms linear) backwards;
}
%{GamepadSearch} > %{GamepadTabbedPage} > *,
.lgs-search-zero {
animation: lgs-sheet-content-in var(--lgs-motion-sheet-in, 735ms linear) backwards;
}
/* ------------------------------------------------------------------ */
/* 2. The scope bar (Steam's tab row, WN §4.6) */
/* ------------------------------------------------------------------ */
/* the pinned band: the sheet's own top, no slab and no blur (no glass on
glass, P-45); results fade out under it through the scroller's mask */
%{GamepadSearch} %{TabHeaderRowWrapper} {
box-sizing: border-box;
height: var(--lgs-c1b-bar);
left: 0;
right: 0;
width: auto;
padding: 0 !important; /* Steam's 36 px side padding would put
the arrows 36 px inside the sheet and
leave the track 728 px (review R2 M1) */
}
%{GamepadSearch} %{TabHeaderRowWrapper}::before {
background: none !important;
backdrop-filter: none !important;
-webkit-mask-image: none;
mask-image: none;
box-shadow: none !important;
opacity: 0 !important;
}
/* the row: ‹ (80) + the scope track centred in the rest + › (80); Steam's
spacers take no width, so the track gets 960 - 160 = 800 px and sits on
the sheet's centre line (P-36: equal insets) */
%{GamepadSearch} %{TabRow} {
box-sizing: border-box;
height: var(--lgs-c1b-bar);
width: 100%;
margin: 0;
padding: 0;
align-items: center;
}
%{GamepadSearch} %{TabRowTabs},
%{GamepadSearch} %{TabsRowScroll} {
height: var(--lgs-c1b-bar);
flex: 1 1 auto;
min-width: 0;
}
%{GamepadSearch} %{FixCenterAlignScroll} {
height: var(--lgs-c1b-bar);
align-items: center;
justify-content: center;
gap: 0;
padding: 0;
margin: 0 auto;
}
%{GamepadSearch} %{TabRowSpacer} {
height: 60px;
width: 0;
min-width: 0;
flex: 0 0 0;
padding: 0;
margin: 0;
}
/* the 64 px track: black .30 recessed fill around the segments (4 px),
anchored to the first and last segment so it hugs them in any language */
%{GamepadSearch} %{FixCenterAlignScroll} > :nth-child(1 of %{GamepadTabbedPage>Tab}) {
anchor-name: --lgs-search-tab-first;
}
%{GamepadSearch} %{FixCenterAlignScroll} > :nth-last-child(1 of %{GamepadTabbedPage>Tab}) {
anchor-name: --lgs-search-tab-last;
}
%{GamepadSearch} %{FixCenterAlignScroll} > :nth-child(1 of %{GamepadTabbedPage>Tab}):nth-last-child(1 of %{GamepadTabbedPage>Tab}) {
anchor-name: --lgs-search-tab-first, --lgs-search-tab-last;
}
%{GamepadSearch} %{TabHeaderRowWrapper}::after {
content: "";
position: absolute;
top: calc(anchor(--lgs-search-tab-first top, 18px) - 2px);
bottom: calc(anchor(--lgs-search-tab-first bottom, 18px) - 2px);
left: calc(anchor(--lgs-search-tab-first left, 100%) - 2px);
right: calc(anchor(--lgs-search-tab-last right, 0px) - 2px);
z-index: -1;
border-radius: var(--lgs-r-capsule, 999px);
background: var(--lgs-fill-thick, rgb(0 0 0 / .30));
backdrop-filter: none;
box-shadow: none;
pointer-events: none;
}
/* segments: the compact segment (PLAN §1.10 "too many members": 20 px
labels, 16 px padding; E-SEG compact: ≥ 120 wide), because six regular
ones (≥ 140) do not fit the 800 px between the arrows. 60 tall in the 64
track (PLAN §1.3: 56-60), sized to their content and never shrunk, so a
count always sits 8 px after its own label and 32 px before the next one.
Each segment is the containing block of its own light (P4's --lgs-ill
::after, tokens.md §3.2). T2 tags them data-lgs-exempt="E-SEG". */
%{GamepadSearch} %{GamepadTabbedPage>Tab} {
position: relative;
box-sizing: border-box;
display: inline-flex;
flex: 0 0 auto;
align-items: center;
justify-content: center;
gap: 8px;
height: 60px !important;
max-height: none !important;
min-width: 120px;
margin: 0;
padding: 0 16px;
border-radius: var(--lgs-r-capsule, 999px);
background-color: transparent;
color: var(--lgs-text-1);
font: 600 20px/1 var(--lgs-font);
white-space: nowrap;
text-transform: none;
letter-spacing: 0;
--lgs-ill: raised;
transition: background-color var(--lgs-motion-snappy, 488ms ease), color var(--lgs-d-swap, 120ms) linear;
}
/* no ring in any input mode (P-17; the light is P4's glow): the doubled
class outranks any `Tab.gpfocus` ring (Phase 1's 20-shell.css §6 drew one
until C1a deleted §6 on 2026-10-07) without naming .gpfocus */
%{GamepadSearch} %{GamepadTabbedPage>Tab}%{GamepadTabbedPage>Tab} {
box-shadow: none !important;
outline: none !important;
}
%{GamepadSearch} %{GamepadTabbedPage>Tab} %{GamepadTabbedPage>TabTitle} {
font: inherit;
text-transform: none;
letter-spacing: 0;
color: inherit;
margin: 0; /* Steam's 8 px; the segment's gap sets it */
}
%{GamepadSearch} %{GamepadTabbedPage>Tab} %{TabCount} {
font: 500 20px/1 var(--lgs-font);
letter-spacing: 0;
color: var(--lgs-text-2);
opacity: 1;
margin: 0;
}
&:not(.lgs-input-pad) %{GamepadSearch} %{GamepadTabbedPage>Tab}:hover {
background-color: transparent;
color: var(--lgs-text-1);
}
/* selected: the white pill with a dark label; focus and hover on it are
the outer white glow (--lgs-ill: white, P-16), never a fill */
%{GamepadSearch} %{GamepadTabbedPage>Tab}%{GamepadTabbedPage>Selected},
%{GamepadSearch} %{GamepadTabbedPage>Tab}%{GamepadTabbedPage>Selected}:hover {
background-color: var(--lgs-selected-fill) !important;
color: var(--lgs-text-on-selected) !important;
--lgs-ill: white;
}
%{GamepadSearch} %{GamepadTabbedPage>Tab}%{GamepadTabbedPage>Selected} %{TabCount} {
color: var(--lgs-text-on-selected-2) !important;
opacity: 1 !important;
}
/* gamepad focus selects in Steam's tabbed page; the focused segment is the
white pill and its outer glow (never under the laser, P-01 / P-02) */
&:not(.lgs-input-laser) %{GamepadSearch} %{GamepadTabbedPage>Tab}.gpfocus {
background-color: var(--lgs-selected-fill) !important;
color: var(--lgs-text-on-selected) !important;
--lgs-ill: white;
/* the segment's own outer glow under P4's: on the sheet's dark frost
the hook's glow alone measured +19.5 L in the P-16 band (needs +20);
blurred, never a ring (P-17, P-42) */
box-shadow: var(--lgs-white-glow, 0 0 22px 8px rgb(255 255 255 / .55)) !important;
}
&:not(.lgs-input-laser) %{GamepadSearch} %{GamepadTabbedPage>Tab}.gpfocus %{TabCount} {
color: var(--lgs-text-on-selected-2) !important;
opacity: 1 !important;
}
/* Steam's paging arrows ‹ ›: 60 px plain circles in an 80 px hit box
(the light is drawn inset 10 by the illumination hook) */
%{GamepadSearch} %{TabHeaderRowWrapper} %{Arrows} {
position: relative;
box-sizing: border-box;
flex: 0 0 auto;
width: 80px;
height: 80px;
padding: 27px;
--lgs-ill-inset: 10px;
border-radius: var(--lgs-r-capsule, 999px);
color: var(--lgs-text-1);
background-color: transparent;
filter: none;
box-shadow: none !important;
--lgs-ill: raised;
}
%{GamepadSearch} %{TabHeaderRowWrapper} %{Arrows} svg {
width: 26px;
height: 26px;
}
&:not(.lgs-input-pad) %{GamepadSearch} %{TabHeaderRowWrapper} %{Arrows}:hover,
&:not(.lgs-input-laser) %{GamepadSearch} %{TabHeaderRowWrapper} %{Arrows}.gpfocus {
color: var(--lgs-text-1) !important;
background-color: transparent !important;
}
/* ------------------------------------------------------------------ */
/* 3. Results (Steam's grid in the sheet) */
/* ------------------------------------------------------------------ */
%{GamepadSearch} %{TabContents},
%{GamepadSearch} %{TabContentsScroll} {
top: 0 !important;
height: 100% !important;
min-height: 0 !important;
}
/* the scroller: starts under the scope bar, 32 px inset, a 26 px fade
at both ends (P-73), no scrollbar (P-72). No backdrop-filter lives
inside it, so the mask is safe (MO R1). */
%{GamepadSearch} %{TabContentsScroll} {
box-sizing: border-box;
padding-top: var(--lgs-c1b-bar) !important;
padding-left: var(--lgs-c1b-pad) !important;
padding-right: var(--lgs-c1b-pad) !important;
padding-bottom: 24px;
scroll-padding-top: calc(var(--lgs-c1b-bar) + 12px);
scroll-padding-bottom: 24px;
scrollbar-width: none;
-webkit-mask-image: linear-gradient(180deg, transparent calc(var(--lgs-c1b-bar) - 26px), #000 var(--lgs-c1b-bar), #000 calc(100% - 26px), transparent);
mask-image: linear-gradient(180deg, transparent calc(var(--lgs-c1b-bar) - 26px), #000 var(--lgs-c1b-bar), #000 calc(100% - 26px), transparent);
}
%{GamepadSearch} %{TabContentsScroll}::-webkit-scrollbar {
display: none;
}
/* art is content: opaque, card radius, never glass. Focus and dwell lift
the card ×1.05 (PLAN §1.4: --lgs-card-lift, keyed on --lgs-lift) with
the content sheen (--lgs-ill: card) and a contact shadow; no ring. */
%{GamepadSearch} %{ResultTemplateImage} {
border-radius: var(--lgs-r-card, 20px);
--lgs-ill: card;
scale: calc(1 + (var(--lgs-card-lift, 1.05) - 1) * var(--lgs-lift, 0));
box-shadow: 0 calc(6px * var(--lgs-lift, 0)) calc(18px * var(--lgs-lift, 0)) rgb(0 0 0 / calc(.35 * var(--lgs-lift, 0))) !important;
transition: scale var(--lgs-motion-snappy, 488ms ease), box-shadow var(--lgs-motion-snappy, 488ms ease);
}
%{GamepadSearch} %{ResultTemplateImage} %{PortraitImage>Container},
%{GamepadSearch} %{ResultTemplateImage} %{PortraitImage>Image},
%{GamepadSearch} %{ResultTemplateImage} %{FriendResultImage},
%{GamepadSearch} %{ResultTemplateImage} %{ProfileBackground},
%{GamepadSearch} %{ResultTemplateImage} %{RedirectResultBackground} {
border-radius: var(--lgs-r-card, 20px);
}
%{GamepadSearch} %{ResultTemplateImage} %{PortraitImage>Container} {
background-color: var(--lgs-fill-1, rgb(255 255 255 / .06));
}
/* Phase 1's ring on the focused tile goes (P-42: no rings) */
&:not(.lgs-input-laser) %{GamepadSearch} %{ResultTemplate}.gpfocuswithin %{ResultTemplateImage} {
box-shadow: 0 6px 18px rgb(0 0 0 / .35) !important;
}
/* the label under each tile: 18 Medium title case, white .70 (G-TYPE) */
%{GamepadSearch} %{GamepadSearch>Description} {
font: 500 18px/1.2 var(--lgs-font);
text-transform: none;
letter-spacing: 0;
color: var(--lgs-text-2);
text-shadow: none;
}
%{GamepadSearch} %{GamepadSearch>Icon} {
color: var(--lgs-text-2);
}
&:not(.lgs-input-laser) %{GamepadSearch} %{ResultTemplate}.gpfocuswithin %{GamepadSearch>Description},
&:not(.lgs-input-laser) %{GamepadSearch} %{ResultTemplate}.gpfocuswithin %{GamepadSearch>Icon},
&:not(.lgs-input-pad) %{GamepadSearch} %{ResultTemplate}:hover %{GamepadSearch>Description},
&:not(.lgs-input-pad) %{GamepadSearch} %{ResultTemplate}:hover %{GamepadSearch>Icon} {
color: var(--lgs-text-1) !important;
}
/* "View more in the Store": a fill tile inside the sheet (no glass on
glass), Steam's text in Callout */
%{GamepadSearch} %{RedirectResultBackground} {
background: rgb(255 255 255 / .10);
box-shadow: none;
}
%{GamepadSearch} %{OverlaidText} {
font: 600 22px/1.25 var(--lgs-font);
text-transform: none;
letter-spacing: 0;
color: var(--lgs-text-1);
text-shadow: none;
}
/* No results: Steam's "No Results Found" as a centred Title 3, and the
query echoed under it in Callout, in quotes (P-62). T2 copies the query
into data-lgs-q on Steam's node (21-search.js); the echo is generated
content, so Steam's text node stays Steam's. */
%{GamepadSearch} %{NoResultsFound} {
font: 700 28px/1.25 var(--lgs-font);
text-transform: none;
letter-spacing: 0;
color: var(--lgs-text-1);
text-shadow: none;
text-align: center;
}
%{GamepadSearch} %{NoResultsFound}[data-lgs-q]::after {
content: "\201C" attr(data-lgs-q) "\201D";
display: block;
margin-top: 10px;
font: 500 22px/1.3 var(--lgs-font);
color: var(--lgs-text-2);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* ------------------------------------------------------------------ */
/* 4. T3: the picture layer and the zero state (device/rt/21-search.js) */
/* %{BasicHome} > .lgs-search-layer (z -1, under Steam's content): */
/* snapshot (0), the scrim (1) and the sheet's glass (2). Steam's */
/* page, or the zero state's content, paints above it. */
/* ------------------------------------------------------------------ */
.lgs-search-layer {
position: absolute;
inset: 0;
z-index: -1; /* last child of %{BasicHome} (a stacking context:
clip-path), painted before Steam's content */
pointer-events: none;
}
/* zero state -> results: the glass widens 880 -> 960 on snappy (WN §4.9).
The glass keeps the results rect (960 at x 160) and the zero state is a
horizontal scale of it (880 / 960 about the centre), so the morph is a
scale transition (LAB: scale, translate, opacity only), never left or
width; the radius is pre-stretched so the scaled corners stay 44 px
round. The state is set in the render that mounts the glass, so the
first paint already has it (no 960 -> 880 jump on a stale query). */
.lgs-search-glass {
transform-origin: 50% 0;
transition: scale var(--lgs-motion-snappy, 488ms ease);
}
.lgs-search-glass[data-lgs-search-state="zero"] {
scale: calc(880 / 960) 1;
border-radius: calc(var(--lgs-r-sheet, 44px) * 960 / 880) / var(--lgs-r-sheet, 44px);
--lgs-sheet-s0: calc(.985 * 880 / 960) .985;
}
/* the page you left: a decorative clone, never focused, never clicked.
In-page backdrop-filter does not frost on the device (C1c's M2-open
finding; probe: brightness(.15) in the sheet's backdrop-filter left the
page behind untouched), so the frost is drawn: the picture itself is
blurred and darkened (static, one layer: the cheap way, G-PERF), and a
second, sharp copy (.lgs-snap-sharp) lies over it everywhere except
under the sheet's rounded rect. The page you were on stays sharp under
the .35 scrim (WN §4.2, review R2 m2), and only what lies behind the
sheet is frosted. */
.lgs-snap,
.lgs-snap-sharp {
position: absolute;
inset: 0;
z-index: 0;
isolation: isolate; /* its own stacking context: Steam's z-indexed
rows inside the clone stay under the scrim */
overflow: hidden;
pointer-events: none;
}
.lgs-snap {
filter: blur(14px) saturate(1.15) brightness(.8);
}
.lgs-snap > *,
.lgs-snap-sharp > * {
position: absolute !important;
inset: 0;
animation: none !important;
transition: none !important;
}
/* the hole under the sheet: the glass's own rect (zero 880 at x 200,
results 960 at x 160), radius 44 */
.lgs-search-layer > .lgs-snap-sharp,
.lgs-search-layer:has(> .lgs-search-glass[data-lgs-pop]) > :is(.lgs-snap, .lgs-search-scrim) {
-webkit-mask:
url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 960 488' preserveAspectRatio='none'><rect width='960' height='488' rx='44' ry='44'/></svg>") var(--lgs-c1b-cut-x, var(--lgs-c1b-x)) var(--lgs-c1b-y) / var(--lgs-c1b-cut-w, var(--lgs-c1b-w)) var(--lgs-c1b-h) no-repeat,
linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
.lgs-search-layer:has(> .lgs-search-glass[data-lgs-search-state="zero"]) {
--lgs-c1b-cut-x: 200px;
--lgs-c1b-cut-w: 880px;
}
.lgs-search-scrim {
position: absolute;
inset: 0;
z-index: 1;
background: var(--lgs-scrim, rgb(0 0 0 / .35));
animation: lgs-fade-in var(--lgs-motion-fade, 441ms linear) backwards;
}
.lgs-search-zero {
font-family: var(--lgs-font);
}
/* The sheet's glass (CSS-only): over the frosted picture, on ::after, the
thick tint (denser under text than the preset alone) and the material's
inner shade; the E3 edge on ::before (z 3). */
.lgs-search-glass {
z-index: 2;
pointer-events: none;
isolation: isolate;
background: none;
backdrop-filter: none;
box-shadow: var(--lgs-shadow-10mm);
}
.lgs-search-glass::after {
content: "";
position: absolute;
inset: 0;
z-index: 1;
border-radius: inherit;
background: linear-gradient(rgb(26 28 36 / .34), rgb(26 28 36 / .34)), var(--lgs-mat-thick-bg);
box-shadow: var(--lgs-mat-thick-shade);
pointer-events: none;
}
/* Native mode, once glassd acknowledges the sheet's pop (the reporter's
data-lgs-pop on the glass, theme/layers/21-search.json): glassd's thick
slab is the glass, its shadow and its edge. The crop must carry only the
sheet's content over that slab (review R2 M6), so the tint and the
shadow go, and the picture and the scrim are cut out under the sheet's
rounded rect too (above): under the crop the base shows glassd's hole,
filled with the scrim's tone ("fill": "scrim", PLAN §1.8). An
unacknowledged sheet keeps the CSS look. */
.lgs-search-glass[data-lgs-pop] {
box-shadow: none;
--lgs-edge: none;
}
.lgs-search-glass[data-lgs-pop]::after {
display: none;
}
/* Dismiss (WN §4.9, PLAN §1.5: inside Steam's ≤ 200 ms exit; content
before glass). Steam keeps the search route mounted for its own exit
transition (0.8-1.1 s after B), and the picture layer lives outside the
route, so 21-search.js sets html.lgs-c1b-leaving on the history event
that leaves /search: the content fades out on page-out (150 ms,
linear) at once, the glass and the scrim on page-out after the page
delay (40 ms), so content leads glass and the sheet is gone by 190 ms
(tokens only, P-58). The picture of the page stays: the real page comes
back under it (Steam's route transition paints it above this layer) and
is the same picture, so nothing blinks between the sheet leaving and the
page returning; the layer goes when the route unmounts. Under Reduce
Motion these are already fades of <= 200 ms. */
&.lgs-c1b-leaving .lgs-search-zero,
&.lgs-c1b-leaving %{GamepadSearch} > %{GamepadTabbedPage} > * {
animation: lgs-fade-out var(--lgs-motion-page-out, 150ms linear) forwards !important;
}
&.lgs-c1b-leaving .lgs-search-layer > :is(.lgs-search-scrim, .lgs-search-glass) {
animation: lgs-fade-out var(--lgs-motion-page-out, 150ms linear) var(--lgs-delay-page, 40ms) forwards;
}
/* zero state -> results: the zero state's content stays for the cross-fade
(21-search.js keeps it 200 ms as .is-out) while Steam's page fades in */
.lgs-search-zero.is-out {
pointer-events: none;
animation: lgs-fade-out var(--lgs-motion-page-out, 150ms linear) forwards !important;
}
/* Zero state -> results (the first keystroke): the sheet is already
presented, so Steam's page does not present it again (no second
sheet-in); its content cross-fades in on fade while the glass morphs
880 -> 960 under it (html.lgs-c1b-presented, set by 21-search.js once the
sheet has been on screen) */
&.lgs-c1b.lgs-c1b-presented %{GamepadSearch} > %{GamepadTabbedPage} {
animation: none;
}
&.lgs-c1b.lgs-c1b-presented %{GamepadSearch} > %{GamepadTabbedPage} > * {
animation: lgs-fade-in var(--lgs-motion-fade, 441ms linear) backwards;
}
/* zero state: 880 x 500 at (200, 100), content inset 40 (WN §4.5) */
.lgs-search-zero {
position: absolute;
z-index: 2;
left: 200px;
top: var(--lgs-c1b-y);
width: 880px;
height: var(--lgs-c1b-h);
box-sizing: border-box;
border-radius: var(--lgs-r-sheet, 44px);
overflow: hidden;
padding: 28px 40px 32px;
display: flex;
flex-direction: column;
gap: 14px;
color: var(--lgs-text-1);
}
.lgs-search-h {
align-self: flex-start; /* as wide as its text (G-MOCK rects) */
margin: 0;
font: 700 28px/1.2 var(--lgs-font);
letter-spacing: -.01em;
color: var(--lgs-text-1);
}
.lgs-search-row {
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 24px;
}
/* Recent Games: 120 px discs (content, not glass: specular arc + contact
shadow), one-line 20 px Medium label, cells 160 wide on a 160 px pitch
(WN §4.5: five cells fill the 800 px between the 40 px insets) */
.lgs-search-row[data-lgs-search="recent"] {
gap: 0;
}
.lgs-search-row[data-lgs-search="recent-search"] {
align-self: flex-start; /* the row hugs its capsules */
}
.lgs-search-game {
box-sizing: border-box;
width: 160px;
height: 180px; /* the whole cell is the target (WN §4.5) */
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
border-radius: 24px;
outline: none;
}
.lgs-search-disc {
position: relative;
width: 120px;
height: 120px;
border-radius: 50%;
overflow: hidden;
background: rgb(255 255 255 / .08);
box-shadow: var(--lgs-shadow-contact, 0 6px 18px rgb(0 0 0 / .35)), inset 0 1.5px 1.5px -1px rgb(255 255 255 / .45);
--lgs-ill: card;
transition: scale var(--lgs-motion-hover-in, 294ms ease), box-shadow var(--lgs-motion-hover-in, 294ms ease);
}
/* attention (PLAN §1.4, Home's disc recipe, 41-home.css C3): gamepad focus
at once; laser only after the 80 ms dwell (P3's .lgs-dwell, P-06), the
sheen at once through --lgs-ill: card; plain hover only with P3 off */
&:not(.lgs-input-laser) .lgs-search-game.gpfocus .lgs-search-disc,
&.lgs-input-laser .lgs-search-game:is(.lgs-dwell:hover, .lgs-attend-80) .lgs-search-disc,
&:not(.lgs-input-laser):not(.lgs-input-pad) .lgs-search-game:hover .lgs-search-disc {
scale: var(--lgs-disc-lift, 1.10);
box-shadow: 0 6px 18px rgb(0 0 0 / .40), 0 0 26px 2px rgb(255 255 255 / .22), inset 0 1.5px 1.5px -1px rgb(255 255 255 / .45);
}
&:not(.lgs-input-laser) .lgs-search-game.gpfocus .lgs-search-disc {
--lgs-focus: 1;
}
.lgs-search-disc img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.lgs-search-disc img.lgs-search-logo {
inset: 20% 12%;
width: 76%;
height: 60%;
object-fit: contain;
filter: drop-shadow(0 2px 6px rgb(0 0 0 / .5));
}
.lgs-search-disc .lgs-search-mono {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font: 700 38px/1 var(--lgs-font);
color: var(--lgs-text-1);
}
.lgs-search-name {
max-width: 152px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font: 500 20px/1.2 var(--lgs-font);
color: var(--lgs-text-1);
text-align: center;
}
/* capsules: 60 tall, 24 padding, 24 Semibold, a fill inside the sheet
(no glass on glass); hover and focus from the illumination hook */
.lgs-search-cap {
position: relative;
box-sizing: border-box;
display: inline-flex;
align-items: center;
gap: 8px;
height: 60px;
padding: 0 24px;
border-radius: var(--lgs-r-capsule, 999px);
background: rgb(255 255 255 / .10);
color: var(--lgs-text-1);
font: 600 24px/1 var(--lgs-font);
white-space: nowrap;
outline: none;
--lgs-ill: raised;
}
&:not(.lgs-input-laser) .lgs-search-cap.gpfocus {
background: rgb(255 255 255 / .42);
}
&:not(.lgs-input-pad) .lgs-search-cap:hover {
background: rgb(255 255 255 / .18);
}
.lgs-search-cap svg {
width: 24px;
height: 24px;
flex: 0 0 auto;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
.lgs-search-foot {
display: flex;
align-items: center;
gap: 24px;
margin-top: auto;
}
.lgs-search-hint {
font: 500 22px/1.3 var(--lgs-font);
color: var(--lgs-text-2);
}
.lgs-search-empty {
font: 500 22px/1.3 var(--lgs-font);
color: var(--lgs-text-2);
}
/* ------------------------------------------------------------------ */
/* 5. The All summary (21-search.js LgsAll; WN §4.6, Q6) */
/* In Steam's own scroller under the scope bar. Row 1: the Top Hit */
/* (372 x 170) and Steam's next library matches as posters */
/* (113 x 170). Row 2: the Software cell (300 x 84, providers), then */
/* "Store (N)" over Steam's own first store tiles and See All. Below: */
/* the other library matches and friends, in Steam's own grid. */
/* ------------------------------------------------------------------ */
.lgs-all {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 18px;
padding: 0 18px 30px; /* + the scroller's 14: content inset 32;
the first row 96 under the sheet's top */
font-family: var(--lgs-font);
color: var(--lgs-text-1);
}
.lgs-all-anchor {
display: none;
}
.lgs-all-row1 {
display: flex;
align-items: flex-start;
gap: 16px;
min-height: 204px; /* 170 cards; the attended poster's name
and status hang under it into the row
gap (to 220) */
}
.lgs-all-row2 {
display: flex;
align-items: flex-start;
gap: 16px;
}
.lgs-all-block {
display: flex;
flex-direction: column;
gap: 10px;
min-width: 0;
}
/* Title 3 section headers: Steam's own strings with Steam's counts */
.lgs-all-h {
align-self: flex-start;
margin: 0;
font: 600 28px/1.25 var(--lgs-font);
letter-spacing: -.01em;
color: var(--lgs-text-1);
white-space: nowrap;
}
/* content cards: opaque art, never glass (P-45); the sheen at once on
attention (--lgs-ill: card), the x1.05 lift and its contact shadow on
gamepad focus or after the 80 ms laser dwell (--lgs-lift, P-06) */
.lgs-all-hit,
.lgs-all-poster {
position: relative;
flex: 0 0 auto;
box-sizing: border-box;
outline: none;
--lgs-ill: card;
scale: calc(1 + (var(--lgs-card-lift, 1.05) - 1) * var(--lgs-lift, 0));
box-shadow: 0 calc(6px + 8px * var(--lgs-lift, 0)) calc(18px + 14px * var(--lgs-lift, 0)) rgb(0 0 0 / calc(.30 + .16 * var(--lgs-lift, 0)));
transition: scale var(--lgs-motion-snappy, 488ms ease), box-shadow var(--lgs-motion-snappy, 488ms ease);
}
.lgs-all-hit {
margin-right: 8px; /* 24 to the first poster, 16 between them */
width: 372px;
height: 170px;
border-radius: var(--lgs-r-card, 20px);
overflow: hidden;
background: rgb(255 255 255 / .06);
}
.lgs-all-poster {
width: 113px;
height: 170px;
border-radius: 16px;
background: rgb(255 255 255 / .06);
}
.lgs-all-art {
position: absolute;
inset: 0;
overflow: hidden;
border-radius: inherit;
pointer-events: none;
}
.lgs-all-art img.lgs-all-img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.lgs-all-art img.lgs-all-logo {
display: none; /* the Top Hit names itself in Title 3 */
}
.lgs-all-mono {
position: absolute;
inset: 0;
display: grid;
place-items: center;
padding: 10px;
box-sizing: border-box;
text-align: center;
font: 600 20px/1.2 var(--lgs-font);
color: var(--lgs-text-1);
background: linear-gradient(160deg, #3b4a63, #1a2232);
}
/* the Top Hit: a scrim under its text, the tag, the name, Steam's status
and Open (a fill drawn on the card: the card is the one target) */
.lgs-all-hit::before {
content: "";
position: absolute;
inset: 0;
z-index: 1;
border-radius: inherit;
pointer-events: none;
background: linear-gradient(0deg, rgb(0 0 0 / .66), rgb(0 0 0 / .05) 62%);
}
.lgs-all-tag {
position: absolute;
left: 14px;
top: 14px;
z-index: 2;
height: 30px;
padding: 0 12px;
border-radius: 15px;
display: grid;
place-items: center;
background: rgb(0 0 0 / .45);
font: 600 18px/1 var(--lgs-font);
color: var(--lgs-text-1);
}
.lgs-all-hit-name {
position: absolute;
left: 18px;
bottom: 40px;
z-index: 2;
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font: 600 28px/1.15 var(--lgs-font);
letter-spacing: -.01em;
color: var(--lgs-text-1);
}
.lgs-all-hit-status {
position: absolute;
left: 18px;
bottom: 14px;
z-index: 2;
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font: 500 18px/1.3 var(--lgs-font);
color: rgb(255 255 255 / .82);
}
.lgs-all-open {
position: absolute;
right: 12px;
bottom: 12px;
z-index: 2;
height: 60px;
padding: 0 24px;
box-sizing: border-box;
border-radius: var(--lgs-r-capsule, 999px);
display: grid;
place-items: center;
background: rgb(255 255 255 / .24);
font: 600 24px/1 var(--lgs-font);
color: var(--lgs-text-1);
pointer-events: none;
}
/* posters: art only at rest; the attended poster shows its name and
Steam's status under it (DESIGN2 §7.8), keyed on the input mode */
.lgs-all-poster-name,
.lgs-all-poster-status {
position: absolute;
left: 50%;
translate: -50% 0;
width: 160px;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
opacity: 0;
pointer-events: none;
transition: opacity var(--lgs-motion-fade, 441ms linear);
}
.lgs-all-poster-name {
top: 174px;
font: 600 20px/1.2 var(--lgs-font);
color: var(--lgs-text-1);
}
.lgs-all-poster-status {
top: 198px;
font: 500 18px/1.2 var(--lgs-font);
color: var(--lgs-text-2);
}
&:not(.lgs-input-laser) .lgs-all-poster.gpfocus > :is(.lgs-all-poster-name, .lgs-all-poster-status),
&.lgs-input-laser .lgs-all-poster:is(.lgs-dwell:hover, .lgs-attend-80) > :is(.lgs-all-poster-name, .lgs-all-poster-status),
&:not(.lgs-input-laser):not(.lgs-input-pad) .lgs-all-poster:hover > :is(.lgs-all-poster-name, .lgs-all-poster-status) {
opacity: 1;
}
/* a program as the Top Hit (S-A) and the Software cell (S-B): a fill
inside the sheet (no glass on glass), the program's icon on a disc */
.lgs-all-prog,
.lgs-all-soft {
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: auto auto;
align-content: center;
column-gap: 14px;
box-sizing: border-box;
background: rgb(255 255 255 / .10);
color: var(--lgs-text-1);
outline: none;
}
.lgs-all-prog {
padding: 0 24px;
column-gap: 20px;
}
.lgs-all-prog::before {
content: none;
}
.lgs-all-soft {
position: relative;
width: 300px;
height: 84px;
padding: 0 26px 0 14px;
border-radius: var(--lgs-r-capsule, 999px); /* a text control: a capsule
(VP P-83) */
--lgs-ill: raised;
}
.lgs-all-chip {
grid-row: 1 / span 2;
position: relative;
width: 56px;
height: 56px;
border-radius: 50%;
display: grid;
place-items: center;
overflow: hidden;
background: rgb(255 255 255 / .11);
}
.lgs-all-prog .lgs-all-chip {
width: 96px;
height: 96px;
}
.lgs-all-chip img {
width: 48px;
height: 48px;
display: block;
}
.lgs-all-prog .lgs-all-chip img {
width: 80px;
height: 80px;
}
.lgs-all-chip .lgs-all-mono {
font-size: 26px;
}
.lgs-all-prog-name {
align-self: end;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font: 600 24px/1.25 var(--lgs-font);
}
.lgs-all-prog .lgs-all-prog-name {
font-size: 28px;
letter-spacing: -.01em;
}
.lgs-all-prog-line {
align-self: start;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font: 500 18px/1.3 var(--lgs-font);
color: var(--lgs-text-2);
}
/* Steam's own result grid in a run: as wide as its columns, Steam's tiles
and handlers inside (section 3 styles them as in Steam's grid) */
.lgs-all-run {
position: relative;
flex: 0 0 auto;
}
.lgs-all-storerow {
display: flex;
align-items: flex-start;
gap: 12px;
}
.lgs-all-seeall {
flex: 0 0 auto;
margin-top: 11px; /* centred on the tiles' art (82) */
}
.lgs-all-seeall svg {
width: 22px;
height: 22px;
color: var(--lgs-text-2);
}
/* Reduce Motion: fades only (MO C8) */
@media (prefers-reduced-motion: reduce) {
%{GamepadSearch} > %{GamepadTabbedPage},
.lgs-search-glass,
.lgs-search-zero {
animation: lgs-fade-in var(--lgs-d-reduce, 180ms) linear backwards;
}
%{GamepadSearch} %{ResultTemplateImage},
.lgs-search-disc,
.lgs-search-glass,
.lgs-all-hit,
.lgs-all-poster {
transition: none; /* the 880 -> 960 morph is a cut; the content
cross-fade carries the change (P-56) */
}
.lgs-all-poster-name,
.lgs-all-poster-status {
transition: opacity var(--lgs-d-reduce, 180ms) linear;
}
}