/* Glass Shell foundation: materials (package P4). Contract: docs/phase2/contracts/tokens.md §2. Sources: PLAN §1.6, DESIGN2 §6, WN §8.2 (edge lobe) and §8.4 (degraded glass), GM §1.7. Wrapped as html.lgs-on { ... } by the bundler ("&" is html.lgs-on). Edges come from light, never from lines (VP P-42): E3 is a conic arc from the one key light with gaps on both sides plus a lobe at 26 % of the width; E4/E5 (dark inner edge, thickness) are inset shadows in the --lgs-mat--shade tokens; the fill is the --lgs-mat--bg token. 1. The edge hook: --lgs-edge: on an element → E3 on its ::before. 2. Markup classes for elements we create (T2/T3). 3. Utilities: media dimming, scroll edges. 4. High Contrast: the one allowed stroke. */ /* ------------------------------------------------------------------ */ /* 1. The edge hook */ /* ------------------------------------------------------------------ */ /* The subjects are generated host lists, never "*" (G-PERF, review R1 M1: a universal *::before inside @container makes Blink resolve ::before for every element on every style recalc). Each "lgs-hosts" block is written by `python docs/phase2/fontkit.py --hooks` from every theme rule that sets --lgs-edge (to that material): the rightmost compound of its selector (a superset; the style query still decides), one flat :where() entry per host, so the hook keeps the specificity the universal subject had. Never edit the blocks by hand. */ @container style(--lgs-edge: window) or style(--lgs-edge: panel) or style(--lgs-edge: liquid) or style(--lgs-edge: thick) or style(--lgs-edge: clear) or style(--lgs-edge: control) { /* lgs-hosts edge ::before begin */ :where(.lgs-hosts-edge)::before, :where(#Footer%{BasicFooter})::before, :where(%{*BasicContextMenuModal>contextMenuContents})::before, :where(%{*GamepadDialogContent>ModalPosition} > div.Panel:not(%{*GamepadDialogContent>ModalClickToDismiss}, %{FocusRing}, %{FocusRingRoot}, .BasicUIContextMenu, %{*GamepadDialogContent_InnerWidth>GamepadDialogContent}))::before, :where(%{*GamepadDialogContent_InnerWidth>GamepadDialogContent})::before, :where(%{BarSurfaceSection}%{LowBatteryGauge>Tabs})::before, :where(%{BarSurfaceSection}%{Tray})::before, :where(%{BarSurface}%{Bookend})::before, :where(%{BasicUiRoot})::before, :where(%{Container>MenuButton})::before, :where(%{DashboardBarPopupContents})::before, :where(%{DashboardBarPopupList})::before, :where(%{DashboardMenu}%{Variant_TabMenu})::before, :where(%{DialogWrapper})::before, :where(%{GamepadTabbedPage})::before, :where(%{HiddenFrame>InvitesList})::before, :where(%{PlayButtonContainer>PlayButton})::before, :where(%{PopupBody>Tooltip})::before, :where(%{QuickAccessMenu})::before, :where(%{RecentlyCompletedItem})::before, :where(%{ShortTemplate})::before, :where(%{TabRowTabs})::before, :where(%{TextBoxCarouselContents})::before, :where(%{VRVirtualKeyboardContents})::before, :where(%{VirtualKeyboardStandaloneContainer>VirtualKeyboardContainer})::before, :where(%{VolumeSliderLabel>VolumePopin}%{VolumeSliderLabel>VR})::before, :where(.lgs-asspod)::before, :where(.lgs-btn)::before, :where(.lgs-cc-circle-face)::before, :where(.lgs-cc-tile)::before, :where(.lgs-edge)::before, :where(.lgs-glass[data-lgs-mat="plate"])::before, :where(.lgs-home-card)::before, :where(.lgs-home-disc)::before, :where(.lgs-home-plate)::before, :where(.lgs-home-seg)::before, :where(.lgs-home-wn-glass)::before, :where(.lgs-search-glass)::before, :where(.lgs-tabcap)::before, :where(.lgs-tune)::before, :where([data-lgs-mat="clear"])::before, :where([data-lgs-mat="liquid"])::before, :where([data-lgs-mat="panel"])::before, :where([data-lgs-mat="thick"])::before, :where([data-lgs-mat="window"])::before, :where(button.DialogButton%{*GamepadDialogContent>Button})::before, :where(div.lgs-c2b-panel[data-lgs-c2b])::before /* lgs-hosts edge ::before end */ { content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-sizing: border-box; /* under the host's children by default (controls: the label never covers a 1.5 px rim); containers whose content reaches their edge set --lgs-edge-z on the host (the window's default is above its header) */ --lgs-edge-z: inherit; z-index: var(--lgs-edge-z, auto); --lgs-focus: inherit; --_lgs-rim: var(--lgs-rim-control); /* --lgs-edge-dim (inherited, 0..1): how much a scrim over the slab dims its rim */ --_lgs-k: calc(var(--_lgs-rim) * (1 + .5 * var(--lgs-focus)) * (1 - var(--lgs-edge-dim, 0))); padding: var(--_lgs-rim-w, var(--lgs-rim-w)); background: conic-gradient(from var(--lgs-light), rgb(255 255 255 / calc(.62 * var(--_lgs-k))) 0deg, rgb(255 255 255 / calc(.20 * var(--_lgs-k))) 38deg, rgb(255 255 255 / 0) 72deg, rgb(255 255 255 / 0) 148deg, rgb(255 255 255 / calc(.16 * var(--_lgs-k))) 180deg, rgb(255 255 255 / 0) 214deg, rgb(255 255 255 / 0) 288deg, rgb(255 255 255 / calc(.22 * var(--_lgs-k))) 322deg, rgb(255 255 255 / calc(.62 * var(--_lgs-k))) 360deg), radial-gradient(70% 120% at 26% 0%, rgb(255 255 255 / calc(.18 * var(--_lgs-k))) 0, rgb(255 255 255 / calc(.06 * var(--_lgs-k))) 46%, rgb(255 255 255 / 0) 75%); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude; transition: --lgs-focus var(--lgs-ill-d-out, 441ms) var(--lgs-ill-ease, ease-out); } } @container style(--lgs-edge: window) { /* the window's content (header, full-bleed art, modals) reaches its edge: the rim sits above it like a real rim, as Phase 1's did (z 20000) */ /* lgs-hosts edge:window ::before begin */ :where(.lgs-hosts-edge-window)::before, :where(%{BasicUiRoot})::before, :where([data-lgs-mat="window"])::before /* lgs-hosts edge:window ::before end */ { --_lgs-rim: var(--lgs-rim-window); --_lgs-rim-w: var(--lgs-rim-w-window); z-index: var(--lgs-edge-z, 20000); } /* ...so under an alert or sheet scrim (Steam's modal overlay, z 1500) it dims with the window instead of glowing above it (review R1 m8); the scrim is black .35 (--lgs-scrim). Steam keeps an empty, invisible .ModalOverlayBackground in the page at all times: the scrim shows while a .ModalOverlayContent is .active, except for a context menu alone (C1c: menus have no scrim unless a dialog is .inactive below) */ &:is(:has(.ModalOverlayContent.inactive), :has(.ModalOverlayContent.active):not(:has(.ModalOverlayContent.active .BasicUIContextMenu))) /* lgs-hosts edge:window where::before begin */ :where(.lgs-hosts-edge-window, %{BasicUiRoot}, [data-lgs-mat="window"])::before /* lgs-hosts edge:window where::before end */ { --lgs-edge-dim: .35; } } @container style(--lgs-edge: panel) { /* lgs-hosts edge:panel ::before begin */ :where(.lgs-hosts-edge-panel)::before, :where(#Footer%{BasicFooter}%{FloatingVRFooter})::before, :where(%{DashboardBarPopupContents})::before, :where(%{DashboardBarPopupList})::before, :where(%{DashboardMenu}%{Variant_TabMenu})::before, :where(%{HiddenFrame>InvitesList})::before, :where(%{QuickAccessMenu})::before, :where(%{ShortTemplate})::before, :where(%{VolumeSliderLabel>VolumePopin}%{VolumeSliderLabel>VR})::before, :where(.lgs-cc-tile)::before, :where(.lgs-glass[data-lgs-mat="plate"])::before, :where([data-lgs-mat="panel"])::before, :where(div.lgs-c2b-panel[data-lgs-c2b])::before /* lgs-hosts edge:panel ::before end */ { --_lgs-rim: var(--lgs-rim-panel); } } @container style(--lgs-edge: liquid) { /* lgs-hosts edge:liquid ::before begin */ :where(.lgs-hosts-edge-liquid)::before, :where(#Footer%{BasicFooter})::before, :where(%{BarSurfaceSection}%{LowBatteryGauge>Tabs})::before, :where(%{BarSurfaceSection}%{Tray})::before, :where(%{BarSurface}%{Bookend})::before, :where(%{PlayButtonContainer>PlayButton})::before, :where(%{RecentlyCompletedItem})::before, :where(%{TabRowTabs})::before, :where(%{TextBoxCarouselContents})::before, :where(.lgs-asspod)::before, :where(.lgs-cc-circle-face)::before, :where(.lgs-home-card)::before, :where(.lgs-home-disc)::before, :where(.lgs-home-plate)::before, :where(.lgs-home-seg)::before, :where(.lgs-home-wn-glass)::before, :where(.lgs-tabcap)::before, :where(.lgs-tune)::before, :where([data-lgs-mat="liquid"])::before /* lgs-hosts edge:liquid ::before end */ { --_lgs-rim: var(--lgs-rim-liquid); } } @container style(--lgs-edge: thick) { /* lgs-hosts edge:thick ::before begin */ :where(.lgs-hosts-edge-thick)::before, :where(%{*BasicContextMenuModal>contextMenuContents})::before, :where(%{*GamepadDialogContent>ModalPosition} > div.Panel:not(%{*GamepadDialogContent>ModalClickToDismiss}, %{FocusRing}, %{FocusRingRoot}, .BasicUIContextMenu, %{*GamepadDialogContent_InnerWidth>GamepadDialogContent}))::before, :where(%{*GamepadDialogContent_InnerWidth>GamepadDialogContent})::before, :where(%{DialogWrapper})::before, :where(%{GamepadTabbedPage})::before, :where(%{PopupBody>Tooltip})::before, :where(%{VRVirtualKeyboardContents})::before, :where(%{VirtualKeyboardStandaloneContainer>VirtualKeyboardContainer})::before, :where(.lgs-search-glass)::before, :where([data-lgs-mat="thick"])::before /* lgs-hosts edge:thick ::before end */ { --_lgs-rim: var(--lgs-rim-thick); } } @container style(--lgs-edge: clear) { /* lgs-hosts edge:clear ::before begin */ :where(.lgs-hosts-edge-clear)::before, :where(%{Container>MenuButton})::before, :where(%{TabRowTabs})::before, :where([data-lgs-mat="clear"])::before /* lgs-hosts edge:clear ::before end */ { --_lgs-rim: var(--lgs-rim-clear); } } /* High Contrast: the one allowed stroke, 2 px solid white .70 (§4) */ @media (prefers-contrast: more) { @container style(--lgs-edge: window) or style(--lgs-edge: panel) or style(--lgs-edge: liquid) or style(--lgs-edge: thick) or style(--lgs-edge: clear) or style(--lgs-edge: control) { /* lgs-hosts edge ::before begin */ :where(.lgs-hosts-edge)::before, :where(#Footer%{BasicFooter})::before, :where(%{*BasicContextMenuModal>contextMenuContents})::before, :where(%{*GamepadDialogContent>ModalPosition} > div.Panel:not(%{*GamepadDialogContent>ModalClickToDismiss}, %{FocusRing}, %{FocusRingRoot}, .BasicUIContextMenu, %{*GamepadDialogContent_InnerWidth>GamepadDialogContent}))::before, :where(%{*GamepadDialogContent_InnerWidth>GamepadDialogContent})::before, :where(%{BarSurfaceSection}%{LowBatteryGauge>Tabs})::before, :where(%{BarSurfaceSection}%{Tray})::before, :where(%{BarSurface}%{Bookend})::before, :where(%{BasicUiRoot})::before, :where(%{Container>MenuButton})::before, :where(%{DashboardBarPopupContents})::before, :where(%{DashboardBarPopupList})::before, :where(%{DashboardMenu}%{Variant_TabMenu})::before, :where(%{DialogWrapper})::before, :where(%{GamepadTabbedPage})::before, :where(%{HiddenFrame>InvitesList})::before, :where(%{PlayButtonContainer>PlayButton})::before, :where(%{PopupBody>Tooltip})::before, :where(%{QuickAccessMenu})::before, :where(%{RecentlyCompletedItem})::before, :where(%{ShortTemplate})::before, :where(%{TabRowTabs})::before, :where(%{TextBoxCarouselContents})::before, :where(%{VRVirtualKeyboardContents})::before, :where(%{VirtualKeyboardStandaloneContainer>VirtualKeyboardContainer})::before, :where(%{VolumeSliderLabel>VolumePopin}%{VolumeSliderLabel>VR})::before, :where(.lgs-asspod)::before, :where(.lgs-btn)::before, :where(.lgs-cc-circle-face)::before, :where(.lgs-cc-tile)::before, :where(.lgs-edge)::before, :where(.lgs-glass[data-lgs-mat="plate"])::before, :where(.lgs-home-card)::before, :where(.lgs-home-disc)::before, :where(.lgs-home-plate)::before, :where(.lgs-home-seg)::before, :where(.lgs-home-wn-glass)::before, :where(.lgs-search-glass)::before, :where(.lgs-tabcap)::before, :where(.lgs-tune)::before, :where([data-lgs-mat="clear"])::before, :where([data-lgs-mat="liquid"])::before, :where([data-lgs-mat="panel"])::before, :where([data-lgs-mat="thick"])::before, :where([data-lgs-mat="window"])::before, :where(button.DialogButton%{*GamepadDialogContent>Button})::before, :where(div.lgs-c2b-panel[data-lgs-c2b])::before /* lgs-hosts edge ::before end */ { padding: 2px; background: rgb(255 255 255 / .70); } } } /* ------------------------------------------------------------------ */ /* 2. Markup classes (elements we create, or Steam nodes T2 tags) */ /* ------------------------------------------------------------------ */ & .lgs-edge { --lgs-edge: control; } & :is(.lgs-edge, .lgs-glass)[data-lgs-mat="window"] { --lgs-edge: window; } & :is(.lgs-edge, .lgs-glass)[data-lgs-mat="panel"] { --lgs-edge: panel; } & :is(.lgs-edge, .lgs-glass)[data-lgs-mat="liquid"] { --lgs-edge: liquid; } & :is(.lgs-edge, .lgs-glass)[data-lgs-mat="thick"] { --lgs-edge: thick; } & :is(.lgs-edge, .lgs-glass)[data-lgs-mat="clear"] { --lgs-edge: clear; } & .lgs-glass { position: relative; box-sizing: border-box; color: var(--lgs-text-1); box-shadow: var(--_lgs-shade, none), var(--_lgs-dz, 0 0 0 transparent); } & .lgs-glass[data-lgs-mat="window"] { --_lgs-shade: var(--lgs-mat-window-shade); background: var(--lgs-mat-window-bg); border-radius: var(--lgs-r-window); } & .lgs-glass[data-lgs-mat="panel"] { --_lgs-shade: var(--lgs-mat-panel-shade); background: var(--lgs-mat-panel-bg); } & .lgs-glass[data-lgs-mat="liquid"] { --_lgs-shade: var(--lgs-mat-liquid-shade); background: var(--lgs-mat-liquid-bg); backdrop-filter: var(--lgs-mat-liquid-blur); } /* liquid glass with nothing of the page behind it (outside the window) */ & .lgs-glass[data-lgs-mat="liquid"][data-lgs-over="room"] { background: var(--lgs-mat-liquid-room-bg); backdrop-filter: none; } & .lgs-glass[data-lgs-mat="thick"] { --_lgs-shade: var(--lgs-mat-thick-shade); background: var(--lgs-mat-thick-bg); backdrop-filter: var(--lgs-mat-thick-blur); border-radius: var(--lgs-r-menu); } & .lgs-glass[data-lgs-mat="clear"] { --_lgs-shade: var(--lgs-mat-clear-shade); background: var(--lgs-mat-clear-bg); backdrop-filter: var(--lgs-mat-clear-blur); } & .lgs-glass[data-lgs-mat="plate"] { --_lgs-shade: var(--lgs-mat-panel-shade); --lgs-edge: panel; background: var(--lgs-mat-plate-bg); } /* depth shadow, sized by depth (DESIGN2 §3.8) */ & [data-lgs-dz="10"] { --_lgs-dz: var(--lgs-shadow-10mm); } & [data-lgs-dz="15"] { --_lgs-dz: var(--lgs-shadow-15mm); } & [data-lgs-dz="25"] { --_lgs-dz: var(--lgs-shadow-25mm); } & [data-lgs-dz="30"] { --_lgs-dz: var(--lgs-shadow-30mm); } /* selection vocabulary as markup (contract §3.3) */ & [data-lgs-sel="on"] { background: var(--lgs-selected-fill) !important; color: var(--lgs-text-on-selected) !important; --lgs-ill: white; } & [data-lgs-sel="nav"] { background: var(--lgs-fill-nav); box-shadow: var(--lgs-sel-arc); font-weight: var(--lgs-nav-weight); } /* ------------------------------------------------------------------ */ /* 3. Utilities */ /* ------------------------------------------------------------------ */ /* the 35 % dimming layer under clear glass over media (DESIGN2 §6.4) */ & .lgs-media-dim { background: var(--lgs-media-dim); } /* scroll edges (DESIGN2 §6.7): a mask on a scroller we tag. Never on a scroller that holds backdrop-filter (a mask makes it a backdrop root) */ & .lgs-scroll-fade-bottom { -webkit-mask-image: var(--lgs-scroll-mask-bottom); mask-image: var(--lgs-scroll-mask-bottom); } & .lgs-scroll-fade-top { -webkit-mask-image: var(--lgs-scroll-mask-top); mask-image: var(--lgs-scroll-mask-top); } & [data-lgs-scroll-band="top"] { --lgs-scroll-band: top; } & [data-lgs-scroll-band="bottom"] { --lgs-scroll-band: bottom; } /* the scroll-edge dimming band, on the scroller's non-scrolling container (hosts generated like the edge's) */ @container style(--lgs-scroll-band: top) or style(--lgs-scroll-band: bottom) { /* lgs-hosts band ::after begin */ :where(.lgs-hosts-band)::after, :where([data-lgs-scroll-band="bottom"])::after, :where([data-lgs-scroll-band="top"])::after /* lgs-hosts band ::after end */ { content: ""; position: absolute; left: 0; right: 0; height: var(--lgs-scroll-band-h); pointer-events: none; z-index: 1; } } @container style(--lgs-scroll-band: top) { /* lgs-hosts band:top ::after begin */ :where(.lgs-hosts-band-top)::after, :where([data-lgs-scroll-band="top"])::after /* lgs-hosts band:top ::after end */ { top: 0; background: var(--lgs-scroll-band-top); border-radius: inherit; } } @container style(--lgs-scroll-band: bottom) { /* lgs-hosts band:bottom ::after begin */ :where(.lgs-hosts-band-bottom)::after, :where([data-lgs-scroll-band="bottom"])::after /* lgs-hosts band:bottom ::after end */ { bottom: 0; background: var(--lgs-scroll-band-bottom); border-radius: inherit; } } /* ------------------------------------------------------------------ */ /* 4. High Contrast: the one allowed stroke, 2 px solid white .70 */ /* ------------------------------------------------------------------ */ /* (the edge's stroke is in §1, inside the host rule) */ @media (prefers-contrast: more) { & .lgs-glass { backdrop-filter: none; } }