@mkbabb/glass-ui 3.10.1 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{CardAction-DrAcAV4d.js → CardAction-DuArVky-.js} +2 -2
- package/dist/{CommandShortcut-mV2e1aG6.js → CommandShortcut-CefCj5mt.js} +2 -2
- package/dist/{DataTable-C3E6jZ5B.js → DataTable-CUHMukiQ.js} +2 -2
- package/dist/DeckProgress-Dmk4_AKN.js +23 -0
- package/dist/{DialogContent-BxaaafFi.js → DialogContent-BRkT1cjP.js} +1 -1
- package/dist/{HoverPopover-CDA_SaCe.js → HoverPopover-2LOIW34a.js} +1 -1
- package/dist/InstrumentRail-CUJ05YVG.js +44 -0
- package/dist/{MetricBadge-idgrkqoP.js → MetricBadge-BZVWIxNy.js} +7 -5
- package/dist/{Notification-Cu0nN17_.js → Notification-DLMhblXR.js} +1 -1
- package/dist/{NumberFieldContent-CHEWghnY.js → NumberFieldContent-BbLOh8RU.js} +2 -2
- package/dist/{PopoverContent-DpUvdYY-.js → PopoverContent-BId6ggf_.js} +1 -1
- package/dist/{Progress-BW-StFw2.js → Progress-iEuD9pxG.js} +1 -1
- package/dist/{ScrollingText-DruYCni_.js → ScrollingText-BPP84wKo.js} +1 -1
- package/dist/{SelectScrollDownButton-pirzjm--.js → SelectScrollDownButton-T32cVFeM.js} +3 -3
- package/dist/{Switch-DeAqg6pi.js → Switch-Ch-k3Rv-.js} +1 -1
- package/dist/{TabsList-BAVzOZpg.js → TabsList-B8CVOQPC.js} +2 -2
- package/dist/{Toaster-CvuuW_y9.js → Toaster-1rYWrmAm.js} +46 -49
- package/dist/{ToggleGroupItem-DtD37b4G.js → ToggleGroupItem-COu_4IW4.js} +21 -17
- package/dist/animated-digit.js +2 -2
- package/dist/api/index.d.ts +5 -4
- package/dist/api.js +2 -2
- package/dist/aurora.js +70 -70
- package/dist/{badge-NdCQMvvB.js → badge-CaoFJzRb.js} +1 -1
- package/dist/badge.js +1 -1
- package/dist/{button-DyiWFAOE.js → button-DN39cGqL.js} +12 -12
- package/dist/button.js +1 -1
- package/dist/canvas.js +2 -2
- package/dist/card.js +1 -1
- package/dist/carousel.js +5 -5
- package/dist/collapsible.js +1 -1
- package/dist/{color-B65wWyQz.js → color-C5ls_QF7.js} +7 -9
- package/dist/color.js +1 -1
- package/dist/command.js +1 -1
- package/dist/components/custom/aurora/constants/presets.d.ts +62 -5
- package/dist/components/custom/aurora/constants/shaders/mediums.glsl.d.ts +1 -1
- package/dist/components/custom/aurora/index.d.ts +1 -1
- package/dist/components/custom/constellation/Constellation.vue.d.ts +0 -12
- package/dist/components/custom/constellation/constellationField.d.ts +151 -10
- package/dist/components/custom/constellation/index.d.ts +1 -3
- package/dist/components/custom/controls/DarkModeToggle.vue.d.ts +0 -11
- package/dist/components/custom/deck-progress/DeckProgress.vue.d.ts +27 -0
- package/dist/components/custom/deck-progress/index.d.ts +2 -0
- package/dist/components/custom/dialog-native/GlassDialogNative.vue.d.ts +57 -0
- package/dist/components/custom/dialog-native/index.d.ts +1 -0
- package/dist/components/custom/dock/DockTabButton.vue.d.ts +4 -3
- package/dist/components/custom/dock/GlassDock.vue.d.ts +115 -3
- package/dist/components/custom/dock/composables/dockMorphContext.d.ts +0 -36
- package/dist/components/custom/dock/composables/useLayerTransition.d.ts +0 -44
- package/dist/components/custom/expandable-container/ExpandableContainer.vue.d.ts +37 -5
- package/dist/components/custom/fourier-field/index.d.ts +1 -1
- package/dist/components/custom/fourier-field/math.d.ts +0 -17
- package/dist/components/custom/glass-panel/GlassPanel.vue.d.ts +41 -0
- package/dist/components/custom/glass-panel/index.d.ts +2 -0
- package/dist/components/custom/goo-blob/composables/useBlobPointer.d.ts +3 -4
- package/dist/components/custom/goo-blob/composables/useMetaballRenderer.d.ts +8 -11
- package/dist/components/custom/handmark/HandMark.vue.d.ts +29 -0
- package/dist/components/custom/handmark/brush.d.ts +242 -0
- package/dist/components/custom/handmark/freehand.d.ts +69 -0
- package/dist/components/custom/handmark/geometry.d.ts +50 -0
- package/dist/components/custom/handmark/index.d.ts +21 -0
- package/dist/components/custom/handmark/ink.d.ts +11 -0
- package/dist/components/custom/handmark/texture.d.ts +39 -0
- package/dist/components/custom/handmark/types.d.ts +53 -0
- package/dist/components/custom/handmark/useHandMark.d.ts +87 -0
- package/dist/components/custom/header-ribbon/HeaderRibbon.vue.d.ts +31 -0
- package/dist/components/custom/header-ribbon/index.d.ts +2 -0
- package/dist/components/custom/header-ribbon/types.d.ts +7 -0
- package/dist/components/custom/instrument-chassis/InstrumentChassis.vue.d.ts +2 -2
- package/dist/components/custom/instrument-rail/InstrumentRail.vue.d.ts +91 -0
- package/dist/components/custom/instrument-rail/index.d.ts +2 -0
- package/dist/components/custom/metric-badge/MetricBadge.vue.d.ts +8 -0
- package/dist/components/ui/drawer/Drawer.vue.d.ts +0 -1
- package/dist/components/ui/dropdown-menu/DropdownMenuSubContent.vue.d.ts +2 -2
- package/dist/components/ui/slider/index.d.ts +19 -22
- package/dist/components/ui/toast/use-toast.d.ts +2 -2
- package/dist/components/ui/toggle/Toggle.vue.d.ts +1 -1
- package/dist/components/ui/toggle/index.d.ts +1 -1
- package/dist/components/ui/toggle-group/ToggleGroup.vue.d.ts +7 -1
- package/dist/components/ui/toggle-group/ToggleGroupItem.vue.d.ts +3 -1
- package/dist/components/ui/toggle-group/toggleGroupContext.d.ts +13 -0
- package/dist/composables/color/index.d.ts +0 -12
- package/dist/composables/dark/index.d.ts +1 -1
- package/dist/composables/dark/useGlobalDark.d.ts +21 -1
- package/dist/composables/glass/useSpecularTracking.d.ts +3 -12
- package/dist/composables/motion/core/index.d.ts +1 -0
- package/dist/composables/motion/index.d.ts +0 -2
- package/dist/composables/motion/useRouteTransition.d.ts +42 -0
- package/dist/composables/motion/useSpringMount.d.ts +1 -1
- package/dist/configurator.js +1 -1
- package/dist/confirm-dialog.js +2 -2
- package/dist/constellation.js +225 -260
- package/dist/controls.js +14 -48
- package/dist/dark.d.ts +1 -1
- package/dist/dark.js +1 -1
- package/dist/data-table.js +1 -1
- package/dist/deck-progress.d.ts +1 -0
- package/dist/deck-progress.js +2 -0
- package/dist/dialog.js +2 -2
- package/dist/dock.js +345 -340
- package/dist/dockContext-BKeIMmNk.js +9 -0
- package/dist/{dropdown-menu-DVnQcA0E.js → dropdown-menu-D77jX6Df.js} +4 -7
- package/dist/dropdown-menu.js +1 -1
- package/dist/expandable-container.js +79 -38
- package/dist/forms.js +2 -2
- package/dist/fourier-field.js +102 -137
- package/dist/fourier-math.js +2 -2
- package/dist/glass-panel.d.ts +1 -0
- package/dist/glass-panel.js +44 -0
- package/dist/glass-ui.css +1 -1
- package/dist/glass-ui.js +209 -287
- package/dist/goo-blob.js +212 -243
- package/dist/handmark.d.ts +1 -0
- package/dist/handmark.js +662 -0
- package/dist/header-ribbon.d.ts +1 -0
- package/dist/header-ribbon.js +63 -0
- package/dist/hover-popover.js +1 -1
- package/dist/icon-tooltip.js +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/instrument-rail.d.ts +1 -0
- package/dist/instrument-rail.js +2 -0
- package/dist/keyboard.js +1 -1
- package/dist/labeled-field.js +5 -5
- package/dist/{math-BUU9hHID.js → math-DTpRVC0e.js} +2 -18
- package/dist/metric-badge.js +1 -1
- package/dist/metric-stack.js +1 -1
- package/dist/motion-core.js +70 -69
- package/dist/motion.js +37 -38
- package/dist/notification.js +1 -1
- package/dist/number-field.js +1 -1
- package/dist/popover.js +1 -1
- package/dist/{presets-1OhFpaIC.js → presets-BP_ALM0m.js} +14 -1
- package/dist/progress.js +1 -1
- package/dist/pulse.js +1 -1
- package/dist/reactive.js +1 -1
- package/dist/scrolling-text.js +1 -1
- package/dist/search.js +6 -6
- package/dist/select.js +1 -1
- package/dist/{sheet-ChSXcJpU.js → sheet-VZEtNFvO.js} +1 -1
- package/dist/sheet.js +1 -1
- package/dist/{slider-sfl8gFDD.js → slider-ClgpP7EB.js} +4 -4
- package/dist/slider.js +1 -1
- package/dist/sortable-list.js +2 -2
- package/dist/stacked-icons.js +6 -6
- package/dist/styles/animations.css +111 -15
- package/dist/styles/components.css +1 -1
- package/dist/styles/dock/layer-group.css +0 -22
- package/dist/styles/dock/layers.css +1 -11
- package/dist/styles/dock/morph.css +19 -95
- package/dist/styles/dock/shell.css +0 -12
- package/dist/styles/dock-controls.css +31 -28
- package/dist/styles/drawer.css +0 -2
- package/dist/styles/floating-panel.css +0 -2
- package/dist/styles/glass-refract.css +0 -2
- package/dist/styles/glass.css +1188 -31
- package/dist/styles/hover-popover.css +0 -2
- package/dist/styles/index.css +6 -24
- package/dist/styles/instrument-chassis.css +0 -4
- package/dist/styles/paper.css +68 -2
- package/dist/styles/theme.css +20 -13
- package/dist/styles/tokens.css +2360 -22
- package/dist/styles/typography.css +6 -18
- package/dist/styles/utilities.css +1291 -19
- package/dist/styles/view-transition.css +51 -0
- package/dist/switch.js +1 -1
- package/dist/tabs.js +98 -98
- package/dist/timeline.js +2 -2
- package/dist/toast.js +1 -1
- package/dist/toggle-group.js +1 -1
- package/dist/typewriter.js +1 -1
- package/dist/{useAnimatedNumber-DfVgP4te.js → useAnimatedNumber-Bcpy5Etf.js} +1 -1
- package/dist/{useConfiguratorState-BdQXh9ie.js → useConfiguratorState-DxAqksK6.js} +1 -1
- package/dist/useGlassRenderer-D7YGOYiM.js +69 -0
- package/dist/{useGlobalDark-D7R9OypB.js → useGlobalDark-D1wgEqfW.js} +21 -2
- package/dist/useSpecularTracking-BVRZsRHA.js +26 -0
- package/dist/useSpringMount-BJSHcLl3.js +113 -0
- package/dist/useViewTransition-DaN5Zrox.js +21 -0
- package/dist/vReveal-zUIQk-rJ.js +53 -0
- package/dist/watercolor-dot.js +2 -2
- package/package.json +57 -33
- package/src/styles/animations.css +111 -15
- package/src/styles/dock/layer-group.css +0 -22
- package/src/styles/dock/layers.css +1 -11
- package/src/styles/dock/morph.css +19 -95
- package/src/styles/dock/shell.css +0 -12
- package/src/styles/dock-controls.css +31 -28
- package/src/styles/glass.css +1188 -31
- package/src/styles/index.css +6 -24
- package/src/styles/paper.css +68 -2
- package/src/styles/theme.css +20 -13
- package/src/styles/tokens.css +2360 -22
- package/src/styles/typography.css +6 -18
- package/src/styles/utilities.css +1291 -19
- package/src/styles/view-transition.css +51 -0
- package/dist/components/custom/aurora/constants/shaders/vangogh-medium.glsl.d.ts +0 -1
- package/dist/components/custom/constellation/composables/createConstellationField.d.ts +0 -35
- package/dist/components/custom/constellation/composables/useConstellationPointer.d.ts +0 -35
- package/dist/components/custom/constellation/constants.d.ts +0 -36
- package/dist/components/custom/constellation/constellationDraw.d.ts +0 -18
- package/dist/components/custom/constellation/constellationInteraction.d.ts +0 -102
- package/dist/components/custom/dock/composables/useDockMorphWindow.d.ts +0 -15
- package/dist/components/custom/dock/composables/useDockShellProps.d.ts +0 -131
- package/dist/components/custom/dock/constants.d.ts +0 -23
- package/dist/components/custom/fourier-field/presets.d.ts +0 -54
- package/dist/components/custom/goo-blob/composables/buildMetaballProgram.d.ts +0 -18
- package/dist/components/custom/goo-blob/composables/uploadBlobUniforms.d.ts +0 -41
- package/dist/components/custom/goo-blob/constants.d.ts +0 -52
- package/dist/components/custom/goo-blob/shaders/metaball-uniforms.glsl.d.ts +0 -1
- package/dist/components/custom/tabs/constants.d.ts +0 -12
- package/dist/components/custom/underline/GlassUnderline.vue.d.ts +0 -84
- package/dist/components/custom/underline/index.d.ts +0 -2
- package/dist/components/custom/underline/types.d.ts +0 -56
- package/dist/composables/motion/curves.d.ts +0 -48
- package/dist/composables/motion/springPresets.d.ts +0 -33
- package/dist/composables/motion/suite.d.ts +0 -2
- package/dist/dockContext-bho8B1A4.js +0 -17
- package/dist/motion-curves.d.ts +0 -1
- package/dist/motion-curves.js +0 -130
- package/dist/springPresets-aRSm-4_k.js +0 -40
- package/dist/styles/glass/a11y-fallback.css +0 -245
- package/dist/styles/glass/ladder.css +0 -304
- package/dist/styles/glass/material.css +0 -238
- package/dist/styles/glass/progress-rail.css +0 -78
- package/dist/styles/glass/squircle.css +0 -66
- package/dist/styles/glass/surfaces.css +0 -306
- package/dist/styles/tokens/color-radius.css +0 -250
- package/dist/styles/tokens/dark-arm.css +0 -194
- package/dist/styles/tokens/glass.css +0 -413
- package/dist/styles/tokens/light-dark.css +0 -158
- package/dist/styles/tokens/offsets-sizing.css +0 -406
- package/dist/styles/tokens/property-regs.css +0 -169
- package/dist/styles/tokens/scale-paper.css +0 -361
- package/dist/styles/tokens/scheme-motion.css +0 -334
- package/dist/styles/tokens/shadow.css +0 -132
- package/dist/styles/utilities/a11y-overrides.css +0 -140
- package/dist/styles/utilities/animate.css +0 -26
- package/dist/styles/utilities/base.css +0 -365
- package/dist/styles/utilities/btn.css +0 -399
- package/dist/styles/utilities/components.css +0 -317
- package/dist/underline.d.ts +0 -1
- package/dist/underline.js +0 -85
- package/dist/useSpecularTracking-DFK_fhpG.js +0 -32
- package/dist/useSpringMount-DXUzxBjV.js +0 -108
- package/dist/vReveal-CqaCz_mC.js +0 -44
- package/src/styles/glass/a11y-fallback.css +0 -245
- package/src/styles/glass/ladder.css +0 -304
- package/src/styles/glass/material.css +0 -238
- package/src/styles/glass/progress-rail.css +0 -78
- package/src/styles/glass/squircle.css +0 -66
- package/src/styles/glass/surfaces.css +0 -306
- package/src/styles/tokens/color-radius.css +0 -250
- package/src/styles/tokens/dark-arm.css +0 -194
- package/src/styles/tokens/glass.css +0 -413
- package/src/styles/tokens/light-dark.css +0 -158
- package/src/styles/tokens/offsets-sizing.css +0 -406
- package/src/styles/tokens/property-regs.css +0 -169
- package/src/styles/tokens/scale-paper.css +0 -361
- package/src/styles/tokens/scheme-motion.css +0 -334
- package/src/styles/tokens/shadow.css +0 -132
- package/src/styles/utilities/a11y-overrides.css +0 -140
- package/src/styles/utilities/animate.css +0 -26
- package/src/styles/utilities/base.css +0 -365
- package/src/styles/utilities/btn.css +0 -399
- package/src/styles/utilities/components.css +0 -317
- /package/dist/{CollapsibleContent-Drow5WkV.js → CollapsibleContent-CP9qF1v4.js} +0 -0
- /package/dist/{DialogFooter-DufLHi0y.js → DialogFooter-Gprw8H_A.js} +0 -0
- /package/dist/{IconTooltip-BrQ5p181.js → IconTooltip-Cus0fvK0.js} +0 -0
- /package/dist/{Input-B-F818RU.js → Input-xuDwALQR.js} +0 -0
- /package/dist/{_plugin-vue_export-helper-DWghtRt7.js → _plugin-vue_export-helper-ok8pgSl5.js} +0 -0
- /package/dist/{compile-Ce_V55zi.js → compile-BOXjLUxu.js} +0 -0
- /package/dist/{constants-C8NMonuo.js → constants-yKeKnE84.js} +0 -0
- /package/dist/{prng-CjVgzjQT.js → prng-D27-B3tK.js} +0 -0
- /package/dist/{resolveCanvasColor-CRvVzOvP.js → resolveCanvasColor-BBiYfp8g.js} +0 -0
- /package/dist/{useCanvas2D-CSeH8h16.js → useCanvas2D-ClrQuZtp.js} +0 -0
- /package/dist/{useIntersectionPause-DAdVPVp4.js → useIntersectionPause-Cck5Uxk8.js} +0 -0
- /package/dist/{useInterval-D3USn0yc.js → useInterval-Dsmqwi03.js} +0 -0
- /package/dist/{useKeyboardShortcuts-i8tHd5WO.js → useKeyboardShortcuts-CyIYrndH.js} +0 -0
- /package/dist/{useScrollProgress-CcgmgMQt.js → useScrollProgress-CffFGLa5.js} +0 -0
- /package/dist/{useSortable-DnyGXKKY.js → useSortable-C8ReQ50P.js} +0 -0
- /package/dist/{useTextHighlight-B3b10W6r.js → useTextHighlight-CWEi25hf.js} +0 -0
package/src/styles/glass.css
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
/* Glass utilities — glassmorphism surface classes referencing --glass-* tokens
|
|
2
2
|
*
|
|
3
|
-
* ── No-
|
|
4
|
-
* Apple's Liquid Glass guidance
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* ── No-glass-on-glass discipline (AV.W15 D5) ───────────────────────────────
|
|
4
|
+
* Apple's Liquid Glass guidance: "glass is best reserved for the navigation
|
|
5
|
+
* layer that floats above the content" — there is no glass-on-glass. glass-ui
|
|
6
|
+
* adopts the same RUNG-PAIRING rule across three layer bands (the existing
|
|
7
|
+
* z-index registry already encodes them, tokens.css §3 `--z-*`):
|
|
7
8
|
*
|
|
8
|
-
* content (--z-background … --z-content) — the page substrate.
|
|
9
|
+
* content (--z-background … --z-content) — the page substrate; NOT glass.
|
|
9
10
|
* navigation (--z-controls … --z-dock/panel) — the glass band (dock, panels,
|
|
10
11
|
* floating chrome).
|
|
11
12
|
* overlay (--z-overlay … --z-modal) — the glass band (dialog/sheet
|
|
12
13
|
* over content).
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* COMMON composition. The depth-3 ceiling + `contain: paint` budget that keeps
|
|
20
|
-
* the nested blur cheap is gated by W-A11Y-PERF (the enforcement half); this
|
|
21
|
-
* header is the doc half. Bottom line: do not nest a glass PANEL inside a glass
|
|
22
|
-
* panel; a glass CONTROL on a glass plate is fine and budget-gated.
|
|
15
|
+
* A glass surface nested INSIDE another glass surface is a discipline violation
|
|
16
|
+
* — the inner surface should read as a FLAT tier (a `--card` / `--muted` fill),
|
|
17
|
+
* not a second `.glass-*` plate (the blur stacks muddy, the rim doubles, the
|
|
18
|
+
* read collapses). Reach for glass in the navigation/overlay bands only; inside
|
|
19
|
+
* a glass panel, compose flat tiers.
|
|
23
20
|
*
|
|
24
21
|
* ── iOS-spring-on-controls cross-ref (AV.W15 C/M cross-ref) ────────────────
|
|
25
22
|
* The momentum-gated press squish (the control's "lift up on touch, quiet at
|
|
@@ -29,22 +26,1182 @@
|
|
|
29
26
|
* (AV.W9, velocity continuity) and the slider arm (AV.W11); it is cross-
|
|
30
27
|
* referenced here and authored there, not re-derived. See
|
|
31
28
|
* `src/components/custom/dock/README.md` for the canonical discipline.
|
|
32
|
-
*
|
|
33
|
-
* ── Thin @import ROOT (cohesion-aware carve) ───────────────────────────────
|
|
34
|
-
* The glass rule body is carved into cohesive `glass/*.css` partials (each
|
|
35
|
-
* under the 500-line no-god-module bound), @import-ed below in CASCADE ORDER.
|
|
36
|
-
* The base ladder splits into three adjacent `@layer components` blocks
|
|
37
|
-
* (material · ladder · surfaces) — same layer, same source order, so the carve
|
|
38
|
-
* is isomorphic. The squircle `@supports` arm and the a11y `@media`/`@supports`
|
|
39
|
-
* overrides MUST stay AFTER the base ladder (they override it). See
|
|
40
|
-
* docs/precepts/design-idioms.md §cohesion-aware partials.
|
|
41
29
|
*/
|
|
42
30
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
31
|
+
@layer components {
|
|
32
|
+
/* ── The unified glass material (AW.W22) ──────────────────────────────
|
|
33
|
+
ONE source for the three material atoms that AV.W15 scattered onto
|
|
34
|
+
opt-in classes + single tiers:
|
|
35
|
+
(a) the moving-specular `::before` catch-light (folded here from
|
|
36
|
+
glass-specular-track.css — that file now keeps only the thin
|
|
37
|
+
`.glass-specular-track` ALIAS + the brackets that point HERE),
|
|
38
|
+
(b) the `--glass-edge-light` full-perimeter rim (moved off the
|
|
39
|
+
floating-only block so EVERY rung carries it),
|
|
40
|
+
(c) the pointer interaction-light hooks (the `--specular-*`
|
|
41
|
+
`@property` channel mapped from the consumer's --mouse-x/--mouse-y
|
|
42
|
+
write).
|
|
43
|
+
The five ladder rungs + `.glass-card` + the `.dock-icon-button` CONTROL
|
|
44
|
+
compose `.glass-material`, so a Switch thumb, a glass Button, a Dialog,
|
|
45
|
+
and a Sheet all read as the SAME material without per-component wiring.
|
|
46
|
+
|
|
47
|
+
The `.glass-dock` SHELL is OUT of this group BY DESIGN — it hand-rolls a
|
|
48
|
+
parallel surface (its own `--glass-*-dock` family + a local rim + an
|
|
49
|
+
instrument-strip `::before`) the dock arm owns. The COHESION CONSEQUENCE is
|
|
50
|
+
DELIBERATE: the dock shell carries NO shared edge-gleam / moving-specular
|
|
51
|
+
`::before` (its only `::before` is the `variant-instrument-strip` engraved
|
|
52
|
+
bezel, dock/shell.css — a different effect). The dock's catch-light lives on
|
|
53
|
+
its CONTROLS (`.dock-icon-button` et al., listed below + in the opt-in
|
|
54
|
+
transition block), which ARE in the material group — NOT on the shell plate.
|
|
55
|
+
Adding a shell gleam would re-introduce glass-on-glass over the controls, so
|
|
56
|
+
the shell-without-gleam is a NAMED EXEMPTION, gated as `dock-shell-exempt` in
|
|
57
|
+
proof:glass-cohesion (the only exemption beside the legibility allowlist).
|
|
58
|
+
|
|
59
|
+
The selector GROUP is the single source. Adding a surface to the band
|
|
60
|
+
material is one comma-entry here, not a new opt-in class string. */
|
|
61
|
+
.glass-material,
|
|
62
|
+
.glass-wash,
|
|
63
|
+
.glass-quiet,
|
|
64
|
+
.glass-resting,
|
|
65
|
+
.glass-floating,
|
|
66
|
+
.glass-overlay,
|
|
67
|
+
.glass-card,
|
|
68
|
+
.glass-specular-track,
|
|
69
|
+
.dock-icon-button,
|
|
70
|
+
.dock-tab-button,
|
|
71
|
+
.dock-select-trigger,
|
|
72
|
+
.dock-dropdown-trigger {
|
|
73
|
+
position: relative;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* (b) The rim — UNIFORM across all rungs (ONE `--glass-edge-light` token,
|
|
77
|
+
not a per-rung family). The floating/overlay tiers previously wired it
|
|
78
|
+
into their box-shadow stack; it now rides the material group as a
|
|
79
|
+
dedicated `::after` inset ring so it composes alongside (not inside) each
|
|
80
|
+
tier's own shadow recipe. The `::after` grain overlay already claims the
|
|
81
|
+
five rungs' `::after`; the rim is folded INTO that same overlay's
|
|
82
|
+
box-shadow below (the grain `::after` gets the rim inset ring) so no
|
|
83
|
+
pseudo-element collides. */
|
|
84
|
+
|
|
85
|
+
/* (a)+(c) The moving-specular `::before` catch-light — the iOS-26
|
|
86
|
+
illuminate-from-within behaviour. A masked radial-gradient glow rides the
|
|
87
|
+
surface at the pointer position (--mouse-x/--mouse-y → the typed
|
|
88
|
+
--specular-* channel). Folded VERBATIM from glass-specular-track.css so
|
|
89
|
+
there is exactly ONE specular source. */
|
|
90
|
+
.glass-material::before,
|
|
91
|
+
.glass-wash::before,
|
|
92
|
+
.glass-quiet::before,
|
|
93
|
+
.glass-resting::before,
|
|
94
|
+
.glass-floating::before,
|
|
95
|
+
.glass-overlay::before,
|
|
96
|
+
.glass-card::before,
|
|
97
|
+
.glass-specular-track::before,
|
|
98
|
+
.dock-icon-button::before,
|
|
99
|
+
.dock-tab-button::before,
|
|
100
|
+
.dock-select-trigger::before,
|
|
101
|
+
.dock-dropdown-trigger::before {
|
|
102
|
+
content: "";
|
|
103
|
+
position: absolute;
|
|
104
|
+
inset: 0;
|
|
105
|
+
border-radius: inherit;
|
|
106
|
+
pointer-events: none;
|
|
107
|
+
z-index: 1;
|
|
108
|
+
/* Map the consumer's pointer write onto the typed properties, ON THE
|
|
109
|
+
PSEUDO. The `--mouse-x/--mouse-y` write lands on the HOST (inline
|
|
110
|
+
style) as UNREGISTERED props that inherit down to this `::before`;
|
|
111
|
+
the registered `--specular-*` props are `inherits: false`, so the
|
|
112
|
+
mapping is declared HERE where the pseudo owns its typed value. The
|
|
113
|
+
`var(--mouse-x, 50%)` floor is the centred fallback. */
|
|
114
|
+
--specular-x: var(--mouse-x, 50%);
|
|
115
|
+
--specular-y: var(--mouse-y, 50%);
|
|
116
|
+
/* The catch-light intensity at REST — reads the token cohort
|
|
117
|
+
(tokens.css §specular-intensity), default 0 so a static, unwired
|
|
118
|
+
surface is CLEAN and the @property `initial-value: 0` dormancy is no
|
|
119
|
+
longer defeated by a floor. Hover/active lift it through the same
|
|
120
|
+
cohort (the interaction-light state machine below). */
|
|
121
|
+
--specular-intensity: var(--glass-specular-intensity-rest, 0);
|
|
122
|
+
/* AX.W52 D19 — a thin, BOUNDED, pointer-anchored EDGE gleam (not the prior
|
|
123
|
+
full-plate central disc that washed small tiles muddy). The geometry is an
|
|
124
|
+
EXPLICIT-radius circle bounded by `--glass-specular-size` (36% of the
|
|
125
|
+
plate) whose falloff reaches `/0` well INSIDE the plate (70%) — a tracked
|
|
126
|
+
catch-light reading the surface near the lit edge, NEVER a whole-surface
|
|
127
|
+
wash. The core is genuinely WARM-CREAM (L < 100% so the warm hue survives;
|
|
128
|
+
the prior `hsl(40 30% 100%)` blew out to white). The `--specular-intensity`
|
|
129
|
+
multiplier drives the layer `opacity`, NOT a per-stop alpha (a
|
|
130
|
+
registered-`@property` var() nested in an hsl() alpha computes to 0 in
|
|
131
|
+
Chromium — the cross-engine-safe path); the per-stop alphas are literals so
|
|
132
|
+
even the lifted hover/active states read as a lens, not a flash. */
|
|
133
|
+
background: radial-gradient(
|
|
134
|
+
circle var(--glass-specular-size, 36%) at var(--specular-x, 50%) var(--specular-y, 50%),
|
|
135
|
+
hsl(40 35% 92% / 0.5) 0%,
|
|
136
|
+
hsl(40 35% 92% / 0) 70%,
|
|
137
|
+
transparent 100%
|
|
138
|
+
);
|
|
139
|
+
opacity: var(--specular-intensity, 0);
|
|
140
|
+
/* Clip the gleam to ITS OWN radius (not the whole plate) so it stays a
|
|
141
|
+
bounded glint. `mask-image` Baseline 2023-12-07; a non-supporting engine
|
|
142
|
+
ignores it and the `inset: 0` + radius-inherit + the in-paint falloff
|
|
143
|
+
already bound the gleam to the surface. */
|
|
144
|
+
mask-image: radial-gradient(
|
|
145
|
+
circle var(--glass-specular-size, 36%) at var(--specular-x, 50%) var(--specular-y, 50%),
|
|
146
|
+
black 0%,
|
|
147
|
+
black 60%,
|
|
148
|
+
transparent 100%
|
|
149
|
+
);
|
|
150
|
+
/* AX.W52 D19 — `plus-lighter` (additive but HDR-clamped, Safari 16.4+) over
|
|
151
|
+
the prior `screen` (a non-linear lightener that lifted the whole tile
|
|
152
|
+
toward white — the muddy-card defect). A non-supporting engine degrades to
|
|
153
|
+
a plain low-alpha warm overlay (the gleam composes normally — no blend, no
|
|
154
|
+
blowout), the no-workaround floor; NOT a `screen` fallback. */
|
|
155
|
+
mix-blend-mode: plus-lighter;
|
|
156
|
+
/* The moving-specular TRANSITION is NOT declared here. An idle/unwired
|
|
157
|
+
`::before` is STATIC (no interpolation track), so a bare glass rung / an
|
|
158
|
+
un-armed Card / a glass Button at rest attaches ZERO animation tracks (the
|
|
159
|
+
keyframes.js runtime counts 0 idle tracks, not 19). The transition rides
|
|
160
|
+
ONLY the WIRED + interactive surfaces — the opt-in scoped block below. */
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* AY.W-GLASS — the moving-specular transition is OPT-IN (the Card wire-or-omit
|
|
164
|
+
pattern, generalized). It attaches ONLY to a WIRED surface
|
|
165
|
+
(`.glass-specular-track` — Card emits it conditionally, the dock controls + the
|
|
166
|
+
Slider thumb compose it) OR to any glass surface under `:hover`/`:active` (the
|
|
167
|
+
pointer is present, so the interpolation is legitimately live). An idle/unwired
|
|
168
|
+
`.glass-floating` / `.glass-wash` / bare-rung `::before` carries NO transition
|
|
169
|
+
and thus NO keyframes.js track. The §6 register is PRESERVED (still
|
|
170
|
+
`--ease-standard` on the position-tracked + surface-opacity legs — H-motion §6
|
|
171
|
+
"position-tracked → --ease-standard"; only the SELECTOR scope changes). The dock
|
|
172
|
+
controls keep the always-on transition (they are interactive controls; their rest
|
|
173
|
+
intensity is 0 so they paint nothing at rest, but a control legitimately carries
|
|
174
|
+
the transition). */
|
|
175
|
+
.glass-specular-track::before,
|
|
176
|
+
.glass-material:hover::before, .glass-material:active::before,
|
|
177
|
+
.glass-wash:hover::before, .glass-wash:active::before,
|
|
178
|
+
.glass-quiet:hover::before, .glass-quiet:active::before,
|
|
179
|
+
.glass-resting:hover::before, .glass-resting:active::before,
|
|
180
|
+
.glass-floating:hover::before, .glass-floating:active::before,
|
|
181
|
+
.glass-overlay:hover::before, .glass-overlay:active::before,
|
|
182
|
+
.glass-card:hover::before, .glass-card:active::before,
|
|
183
|
+
.dock-icon-button::before,
|
|
184
|
+
.dock-tab-button::before,
|
|
185
|
+
.dock-select-trigger::before,
|
|
186
|
+
.dock-dropdown-trigger::before {
|
|
187
|
+
/* Smooth the position + intensity between pointer samples (the typed
|
|
188
|
+
@property regs make the percentage stops interpolable). */
|
|
189
|
+
transition:
|
|
190
|
+
--specular-x var(--duration-fast, 150ms) var(--ease-standard, ease-out),
|
|
191
|
+
--specular-y var(--duration-fast, 150ms) var(--ease-standard, ease-out),
|
|
192
|
+
opacity var(--duration-normal, 240ms) var(--ease-standard, ease-out);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.dark .glass-material::before,
|
|
196
|
+
.dark .glass-wash::before,
|
|
197
|
+
.dark .glass-quiet::before,
|
|
198
|
+
.dark .glass-resting::before,
|
|
199
|
+
.dark .glass-floating::before,
|
|
200
|
+
.dark .glass-overlay::before,
|
|
201
|
+
.dark .glass-card::before,
|
|
202
|
+
.dark .glass-specular-track::before,
|
|
203
|
+
.dark .dock-icon-button::before,
|
|
204
|
+
.dark .dock-tab-button::before,
|
|
205
|
+
.dark .dock-select-trigger::before,
|
|
206
|
+
.dark .dock-dropdown-trigger::before {
|
|
207
|
+
/* Softer over the deep canvas — `plus-lighter` still lifts on dark, so the
|
|
208
|
+
`.dark` cohort runs a lower hover/active rung (tokens.css §specular-
|
|
209
|
+
intensity `.dark` arm); the rest rung is already 0 so the dark arm only
|
|
210
|
+
softens hover/active via the cohort. The rest rung re-reads the (dark)
|
|
211
|
+
token here so a dark `:root` override resolves. */
|
|
212
|
+
--specular-intensity: var(--glass-specular-intensity-rest, 0);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* (c) Interaction-light lockstep — hover/active lift the intensity in the
|
|
216
|
+
SAME rungs the scale-press squish keys off, so material-light fires in
|
|
217
|
+
lockstep with the press across every glass surface. Settles back on leave
|
|
218
|
+
via the opacity transition above. */
|
|
219
|
+
.glass-material:hover::before,
|
|
220
|
+
.glass-wash:hover::before,
|
|
221
|
+
.glass-quiet:hover::before,
|
|
222
|
+
.glass-resting:hover::before,
|
|
223
|
+
.glass-floating:hover::before,
|
|
224
|
+
.glass-overlay:hover::before,
|
|
225
|
+
.glass-card:hover::before,
|
|
226
|
+
.glass-specular-track:hover::before,
|
|
227
|
+
.dock-icon-button:hover::before,
|
|
228
|
+
.dock-tab-button:hover::before,
|
|
229
|
+
.dock-select-trigger:hover::before,
|
|
230
|
+
.dock-dropdown-trigger:hover::before {
|
|
231
|
+
--specular-intensity: var(--glass-specular-intensity-hover, 0.1);
|
|
232
|
+
}
|
|
233
|
+
.glass-material:active::before,
|
|
234
|
+
.glass-wash:active::before,
|
|
235
|
+
.glass-quiet:active::before,
|
|
236
|
+
.glass-resting:active::before,
|
|
237
|
+
.glass-floating:active::before,
|
|
238
|
+
.glass-overlay:active::before,
|
|
239
|
+
.glass-card:active::before,
|
|
240
|
+
.glass-specular-track:active::before,
|
|
241
|
+
.dock-icon-button:active::before,
|
|
242
|
+
.dock-tab-button:active::before,
|
|
243
|
+
.dock-select-trigger:active::before,
|
|
244
|
+
.dock-dropdown-trigger:active::before {
|
|
245
|
+
--specular-intensity: var(--glass-specular-intensity-active, 0.16);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* AX.W45-TUNE C5 — the SELECTED dock control reads as the keyframes-dock glass
|
|
249
|
+
register: a glass-tier active fill (`--dock-control-active-bg` → the floating
|
|
250
|
+
plate, tokens.css) WITH the specular gleam waking over it, not a flat ink
|
|
251
|
+
stamp. The CSS `:active` pseudo above only fires on press-down; a SELECTED tile
|
|
252
|
+
(`.is-active` / `[aria-pressed]` / `[aria-expanded]` / `[aria-current]`) holds
|
|
253
|
+
the gleam lit so the selected element reads as a lit glass tier the substrate
|
|
254
|
+
shows through (the model the user named, `pass-3:13`). Family-scoped addition
|
|
255
|
+
on the dock-control `::before`, line-disjoint from the shared recipe above. */
|
|
256
|
+
.dock-icon-button:is(.is-active, .active, [aria-pressed="true"], [aria-expanded="true"], [aria-current="page"])::before,
|
|
257
|
+
.dock-tab-button:is(.is-active, .active, [aria-pressed="true"], [aria-expanded="true"], [aria-current="page"])::before,
|
|
258
|
+
.dock-select-trigger:is(.is-active, .active, [aria-pressed="true"], [aria-expanded="true"], [aria-current="page"])::before,
|
|
259
|
+
.dock-dropdown-trigger:is(.is-active, .active, [aria-pressed="true"], [aria-expanded="true"], [aria-current="page"])::before {
|
|
260
|
+
--specular-intensity: var(--glass-specular-intensity-active, 0.16);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* ── LIQUID by tier — the moving-specular hover catch-light is the
|
|
264
|
+
FLOATING / OVERLAY tier's PROPERTY, not a per-surface wire (D6.c M10) ──────
|
|
265
|
+
The shared `:hover::before` rule above already lifts `--specular-intensity`
|
|
266
|
+
to the hover rung on EVERY material surface, so the catch-light wakes on
|
|
267
|
+
hover library-wide. What this block promotes is the FLOATING / OVERLAY
|
|
268
|
+
tier's identity: a navigation/overlay-band surface (a Dialog, a Sheet, a
|
|
269
|
+
Popover, the dock crest's `.glass-floating`) reads as LIQUID OUT OF THE BOX
|
|
270
|
+
— the catch-light is a tier guarantee, documented here so a consumer reaches
|
|
271
|
+
for `variant="floating"` and inherits the lens without a `useSpecularTracking`
|
|
272
|
+
wire. The pointer-anchored TRACKING still rides `useSpecularTracking` (a
|
|
273
|
+
host can sharpen the lens to the cursor); un-wired, the catch-light sits at
|
|
274
|
+
the centred `50%` fallback and breathes on hover — felt, not garish.
|
|
275
|
+
|
|
276
|
+
The magnitude rides the SAME `--glass-specular-intensity-hover` cohort the
|
|
277
|
+
aurora-calibrated π-ledger judges (tokens.css §specular-intensity), so the
|
|
278
|
+
liquid tier never out-shouts a number — it is the SAME gleam, named as a
|
|
279
|
+
tier default. The OPT-OUT survives: `.glass-no-liquid` on a floating/overlay
|
|
280
|
+
surface pins the rest rung (a flat tier for dense data, the deft escape). */
|
|
281
|
+
.glass-floating:not(.glass-no-liquid),
|
|
282
|
+
.glass-overlay:not(.glass-no-liquid) {
|
|
283
|
+
--glass-liquid: 1; /* the tier carries the liquid identity (informational marker) */
|
|
284
|
+
}
|
|
285
|
+
.glass-floating.glass-no-liquid:hover::before,
|
|
286
|
+
.glass-overlay.glass-no-liquid:hover::before,
|
|
287
|
+
.glass-floating.glass-no-liquid:active::before,
|
|
288
|
+
.glass-overlay.glass-no-liquid:active::before {
|
|
289
|
+
/* The deft escape — a dense-data floating surface that wants a FLAT tier
|
|
290
|
+
pins the catch-light at the clean rest rung even on hover/active. */
|
|
291
|
+
--specular-intensity: var(--glass-specular-intensity-rest, 0);
|
|
292
|
+
}
|
|
293
|
+
@media (prefers-reduced-motion: reduce) {
|
|
294
|
+
/* PRM fence — the liquid tier degrades to the static centred gleam. The
|
|
295
|
+
`useSpecularTracking` write is already PRM-gated (it skips the pointer
|
|
296
|
+
write under reduce); this pins the CSS half (`--specular-x/y: 50%`
|
|
297
|
+
centred) AND kills the position-track `transition` so an un-wired liquid
|
|
298
|
+
tile does NOT animate a travelling catch-light on hover under PRM. The
|
|
299
|
+
gleam may still LIFT on hover (a discrete opacity step, no interpolation
|
|
300
|
+
track) — the correct PRM degrade: no MOTION, the surface still responds.
|
|
301
|
+
This mirrors the shared moving-specular PRM bracket (one static-pin
|
|
302
|
+
convention across the specular cohort). */
|
|
303
|
+
.glass-floating::before,
|
|
304
|
+
.glass-overlay::before {
|
|
305
|
+
--specular-x: 50%;
|
|
306
|
+
--specular-y: 50%;
|
|
307
|
+
transition: none;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* ── The GILT EDGE — `.glass-gilt`, the gilt-tooled glass modifier (D6.c M3/M12) ──
|
|
312
|
+
A fine book's cover is gold-tooled at the EDGE — never a gold fill. The
|
|
313
|
+
modifier composes on ANY tier (the tier is the FROST; the gilt is the
|
|
314
|
+
hairline gold catch on its rim) so a featured card OR the dock crest wears
|
|
315
|
+
the same recipe over its own backdrop. Two box-shadow layers ride INSIDE
|
|
316
|
+
the tier (they do not perturb the tier's own elevation stack — they are
|
|
317
|
+
additive inset rings):
|
|
318
|
+
(1) a 1px inset ring on `--gold-rim` — the tooled gold edge (D6.b lands
|
|
319
|
+
`--gold-rim` = `--gold` at ~40%α over the surface; referenced by name,
|
|
320
|
+
so the gilt resolves the moment the token cohort is minted), and
|
|
321
|
+
(2) a 0.5px gold top-catch highlight — the bevel light reading off the
|
|
322
|
+
upper edge, `--gold-light` at low α so it is a catch, not a band.
|
|
323
|
+
The surface still reads as cream-glass; the gold is the bevel light, the
|
|
324
|
+
SCARCE flourish (the consumption budget — at most one gilt surface per
|
|
325
|
+
route — is the atlas's D7 rule, not enforced here). */
|
|
326
|
+
.glass-gilt {
|
|
327
|
+
box-shadow:
|
|
328
|
+
inset 0 0 0 1px var(--gold-rim, color-mix(in oklab, var(--gold) 40%, transparent)),
|
|
329
|
+
inset 0 0.5px 0 0 color-mix(in oklab, var(--gold-light) 35%, transparent),
|
|
330
|
+
var(--glass-material-rim),
|
|
331
|
+
var(--glass-shadow-floating);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* The liquid-metal CATCH-LIGHT (D6.c M12) — `.glass-gilt` overrides the
|
|
335
|
+
moving-specular `::before` sweep COLOR from warm-cream to GOLD, so the light
|
|
336
|
+
travelling across the gilt edge is golden the way gold leaf catches a warm
|
|
337
|
+
highlight. This is the single richest moment in the platform — gold + liquid
|
|
338
|
+
+ specular — and therefore the most scarce (ONE surface ever, the title-page
|
|
339
|
+
flourish; the budget lives in D7). The geometry (bounded circle, the
|
|
340
|
+
`--glass-specular-size` falloff, the `plus-lighter` blend, the mask) is
|
|
341
|
+
INHERITED verbatim from the shared `::before`; only the gradient color stops
|
|
342
|
+
shift to the warm-gold mix. The intensity still rides the `--specular-intensity`
|
|
343
|
+
opacity multiplier (0 at rest), so a resting gilt surface paints NO gold sweep
|
|
344
|
+
— it wakes on hover like every liquid tier. */
|
|
345
|
+
.glass-gilt::before {
|
|
346
|
+
background: radial-gradient(
|
|
347
|
+
circle var(--glass-specular-size, 36%) at var(--specular-x, 50%) var(--specular-y, 50%),
|
|
348
|
+
color-mix(in oklab, var(--gold-light) 60%, hsl(40 35% 92%)) 0%,
|
|
349
|
+
hsl(40 35% 92% / 0) 70%,
|
|
350
|
+
transparent 100%
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/* ── Five-tier surface ladder (v0.8.0; visual only, no geometry) ──
|
|
355
|
+
Tier semantics, alpha-monotonic per R3-spec §A:
|
|
356
|
+
wash ~0.30α + light blur (was `subtle`)
|
|
357
|
+
quiet ~0.50α + medium blur (the missing rung)
|
|
358
|
+
resting ~0.65α + canonical blur (was `default`; the speedtest plate)
|
|
359
|
+
floating ~0.80α + heavier blur (was `elevated`)
|
|
360
|
+
overlay ~0.95α + heaviest blur (NEW; modal-over-modal) */
|
|
361
|
+
|
|
362
|
+
/* The five-rung surface ladder declares the unprefixed
|
|
363
|
+
`backdrop-filter` only. Lightning CSS / autoprefixer in the
|
|
364
|
+
consumer's pipeline emits the `-webkit-` legacy form when
|
|
365
|
+
browserslist targets demand it. Authoring both forms manually
|
|
366
|
+
caused Lightning CSS in Tailwind v4 to dedup-and-keep-prefixed,
|
|
367
|
+
which modern Chromium then dropped from the CSSOM (the prefixed
|
|
368
|
+
alias is parser-rejected post-Chrome 113). Single-source-of-truth
|
|
369
|
+
authorship lets the build pipeline deliver the right matrix. */
|
|
370
|
+
/* AW.W23 — every rung's background composes the adaptive-tint recipe
|
|
371
|
+
(`color-mix(in oklab, <rung bg>, var(--glass-tint-source) var(--glass-tint-
|
|
372
|
+
strength))`). With the default `--glass-tint-strength: 0%` the mix is a
|
|
373
|
+
no-op = byte-identical to the un-tinted rung; a consumer biases the surface
|
|
374
|
+
toward a sampled backdrop hue by setting `--glass-tint-source` + a
|
|
375
|
+
strength ≤30%. oklab is the mwg-preferred tint space (NEVER an
|
|
376
|
+
`oklch(from …)` lightness-shift — browsers don't gamut-map yet). */
|
|
377
|
+
.glass-wash {
|
|
378
|
+
position: relative;
|
|
379
|
+
background: color-mix(in oklab, var(--glass-bg-wash), var(--glass-tint-source) var(--glass-tint-strength));
|
|
380
|
+
backdrop-filter: var(--glass-blur-wash);
|
|
381
|
+
border: 1px solid var(--glass-border-wash);
|
|
382
|
+
box-shadow: var(--glass-material-rim), var(--glass-shadow-wash);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/* AL-W10 SLIM under-shadow wiring (tokens.css §AL-W10 SLIM —
|
|
386
|
+
UNDER-SHADOW recipe rungs). Each tier composes its canonical
|
|
387
|
+
iOS Liquid Glass under-shadow (a 0.5px dark hairline along the
|
|
388
|
+
bottom edge reading as glass thickness) as a NON-INSET layer
|
|
389
|
+
PREPENDED to the existing `--glass-shadow-*` recipe — the under-
|
|
390
|
+
shadow lays the floor; the existing shadow stack lays the lift.
|
|
391
|
+
quiet → `--glass-under-shadow-quiet` (0 2px 8px -1px α0.04)
|
|
392
|
+
resting → `--glass-under-shadow-default` (0 4px 16px -2px α0.08)
|
|
393
|
+
floating→ `--glass-under-shadow-vivid` (0 8px 32px -4px α0.12)
|
|
394
|
+
`.glass-wash` is sub-perceptual; `.glass-overlay` is modal-heavy
|
|
395
|
+
— neither composes an under-shadow rung (Y4/Y6/Y7 SLIM retention).
|
|
396
|
+
Spine tier wires its own rung at instrument-chassis.css. */
|
|
397
|
+
.glass-quiet {
|
|
398
|
+
position: relative;
|
|
399
|
+
background: color-mix(in oklab, var(--glass-bg-quiet), var(--glass-tint-source) var(--glass-tint-strength));
|
|
400
|
+
backdrop-filter: var(--glass-blur-quiet);
|
|
401
|
+
border: 1px solid var(--glass-border-quiet);
|
|
402
|
+
box-shadow:
|
|
403
|
+
var(--glass-material-rim),
|
|
404
|
+
var(--glass-under-shadow-quiet),
|
|
405
|
+
var(--glass-shadow-quiet);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.glass-resting {
|
|
409
|
+
position: relative;
|
|
410
|
+
background: color-mix(in oklab, var(--glass-bg-resting), var(--glass-tint-source) var(--glass-tint-strength));
|
|
411
|
+
backdrop-filter: var(--glass-blur-resting);
|
|
412
|
+
border: 1px solid var(--glass-border-resting);
|
|
413
|
+
box-shadow:
|
|
414
|
+
var(--glass-material-rim),
|
|
415
|
+
var(--glass-under-shadow-default),
|
|
416
|
+
var(--glass-shadow-resting);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/* AV.W15 M3 / AW.W22 — the floating tier carries the full-perimeter rim (the
|
|
420
|
+
catch-light that "defines the silhouette", WWDC25 sess. 219). The rim now
|
|
421
|
+
comes from the unified `--glass-material-rim` (= `--glass-edge-light`) so
|
|
422
|
+
it shares ONE source with the rest of the ladder; it rides INSIDE the
|
|
423
|
+
box-shadow stack so it composes with the under-shadow floor + the lift. */
|
|
424
|
+
.glass-floating {
|
|
425
|
+
position: relative;
|
|
426
|
+
background: color-mix(in oklab, var(--glass-bg-floating), var(--glass-tint-source) var(--glass-tint-strength));
|
|
427
|
+
backdrop-filter: var(--glass-blur-floating);
|
|
428
|
+
border: 1px solid var(--glass-border-floating);
|
|
429
|
+
box-shadow:
|
|
430
|
+
var(--glass-material-rim),
|
|
431
|
+
var(--glass-under-shadow-vivid),
|
|
432
|
+
var(--glass-shadow-floating);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.glass-overlay {
|
|
436
|
+
position: relative;
|
|
437
|
+
background: color-mix(in oklab, var(--glass-bg-overlay), var(--glass-tint-source) var(--glass-tint-strength));
|
|
438
|
+
backdrop-filter: var(--glass-blur-overlay);
|
|
439
|
+
border: 1px solid var(--glass-border-overlay);
|
|
440
|
+
box-shadow: var(--glass-material-rim), var(--glass-shadow-overlay);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/* ── Adaptive legibility: the bright-backdrop bucket (AX.W55 — the iOS-26/27
|
|
444
|
+
"locally darken the glass over light content" move; G2) ──────────────────
|
|
445
|
+
When a consumer sets `--glass-backdrop: light` on any ancestor (a near-white
|
|
446
|
+
card surface, a pale aurora bleed, a bright data grid), the `@container
|
|
447
|
+
style()` read engages here and LIFTS the tint toward ink: `--glass-tint-
|
|
448
|
+
strength` climbs from the `0%` rest default to the bounded AA floor
|
|
449
|
+
(`--glass-tint-strength-aa`, ≤24% — translucent floor preserved) and `--glass-
|
|
450
|
+
tint-source` re-points from `var(--card)` to the warm-ink `--glass-tint-ink`.
|
|
451
|
+
Both are INHERITING custom properties the rungs' `background:` composite
|
|
452
|
+
already reads, so re-pointing them on the material group re-resolves the
|
|
453
|
+
EXISTING `color-mix(in oklab, <rung bg>, …)` toward ink — ZERO new compositing
|
|
454
|
+
seam, the literal darken-over-light recipe. The `.glass-*::before` specular
|
|
455
|
+
`plus-lighter` gleam (W52) is untouched (it composes ON the darkened plate;
|
|
456
|
+
the darkening is the orthogonal oklab-tint axis). No `@supports` wrapper:
|
|
457
|
+
style queries introduce no new `container-type` value (every element is a
|
|
458
|
+
style container by default), so an engine without style-query support parses
|
|
459
|
+
the unknown at-rule as invalid, drops the block, and keeps the zero-delta
|
|
460
|
+
glass (the graceful degradation — the surface is today's-glass, the pre-W55
|
|
461
|
+
state; see utilities.css §density for the same precedent). */
|
|
462
|
+
@container style(--glass-backdrop: light) {
|
|
463
|
+
.glass-material,
|
|
464
|
+
.glass-wash,
|
|
465
|
+
.glass-quiet,
|
|
466
|
+
.glass-resting,
|
|
467
|
+
.glass-floating,
|
|
468
|
+
.glass-overlay,
|
|
469
|
+
.glass-card {
|
|
470
|
+
--glass-tint-source: var(--glass-tint-ink);
|
|
471
|
+
--glass-tint-strength: var(--glass-tint-strength-aa);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/* AX.W55 — the `contrast-color()` foreground-ink flip (Chrome 147+/Safari 26+).
|
|
476
|
+
A PROGRESSIVE-ENHANCEMENT layer, NEVER the load-bearing floor (Open-Q #5
|
|
477
|
+
ratified): the declarative bucket above already carries AA on ALL engines (it
|
|
478
|
+
darkens the translucent plate so dark text clears ~11:1 over white at the 18%
|
|
479
|
+
floor — the live π readback ratifies). On a supporting engine `contrast-color()`
|
|
480
|
+
REFINES the foreground-ink pick natively, anchored on the LIGHT BACKDROP signal
|
|
481
|
+
(`--card`, the warm-white surface base the bright bucket implies) — NOT on the
|
|
482
|
+
ink. `contrast-color(var(--card))` over the light backdrop resolves to the
|
|
483
|
+
maximally-legible DARK ink (anchoring on the dark ink instead would invert to
|
|
484
|
+
white, illegible on the still-light translucent plate). The bucket is the floor;
|
|
485
|
+
this is the native refinement. Scoped to the bright bucket. */
|
|
486
|
+
@supports (color: contrast-color(white)) {
|
|
487
|
+
@container style(--glass-backdrop: light) {
|
|
488
|
+
.glass-material,
|
|
489
|
+
.glass-wash,
|
|
490
|
+
.glass-quiet,
|
|
491
|
+
.glass-resting,
|
|
492
|
+
.glass-floating,
|
|
493
|
+
.glass-overlay,
|
|
494
|
+
.glass-card {
|
|
495
|
+
color: contrast-color(var(--card));
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/* ── The opaque escape (AX.W54) ───────────────────
|
|
501
|
+
The level-0 endpoint of the SAME glass machinery — a solid `--card` plate
|
|
502
|
+
(the opacity seam → 1) with `blur(0)` (the blur seam → 0), routed through
|
|
503
|
+
the ONE `--glass-level` knob. NOT a parallel solid `background:` recipe:
|
|
504
|
+
a `.glass-*` surface that ALSO carries `.glass-opaque` keeps its glass edge,
|
|
505
|
+
rim, and under-shadow (a solid plate with a glass edge, not a bare div), and
|
|
506
|
+
the @supports fallback + the a11y brackets compose coherently. This is the
|
|
507
|
+
explicit opt-out from the MAXIMAL glass default (dense data, forced-colors,
|
|
508
|
+
a consumer that wants a flat surface). */
|
|
509
|
+
.glass-opaque {
|
|
510
|
+
--glass-level: 0;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/* ── Content-aware under-shadow modifier (AV.W15 M4) ──────────────
|
|
514
|
+
Apple raises the under-shadow over text (so the glass reads as
|
|
515
|
+
lifting OFF the text) and drops it over solid light. The modifier
|
|
516
|
+
is ATTRIBUTE-DRIVEN, no JS: a consumer adds `.glass-over-text`
|
|
517
|
+
(or `[data-over-content="text"]`) to a text-bearing glass surface to
|
|
518
|
+
swap it ONE under-shadow rung heavier, or `[data-over-content="solid"]`
|
|
519
|
+
to lighten it one rung over a solid-light backdrop. It reuses the
|
|
520
|
+
existing `--glass-under-shadow-{quiet,default,vivid}` rungs (no new
|
|
521
|
+
shadow value — `vivid` is sufficient over text), redefining only the
|
|
522
|
+
leading under-shadow layer per tier; the lift stack is untouched.
|
|
523
|
+
|
|
524
|
+
The selectors are per-tier so the swap is rung-correct
|
|
525
|
+
(quiet→default, resting→vivid). `.glass-floating` already sits at the
|
|
526
|
+
`vivid` ceiling over text, so the heavier variant is a no-op there
|
|
527
|
+
(the rung saturates) — documented, not an error. */
|
|
528
|
+
.glass-quiet.glass-over-text,
|
|
529
|
+
.glass-quiet[data-over-content="text"] {
|
|
530
|
+
box-shadow: var(--glass-under-shadow-default), var(--glass-shadow-quiet);
|
|
531
|
+
}
|
|
532
|
+
.glass-resting.glass-over-text,
|
|
533
|
+
.glass-resting[data-over-content="text"] {
|
|
534
|
+
box-shadow: var(--glass-under-shadow-vivid), var(--glass-shadow-resting);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/* The inverse — over a solid-light backdrop the under-shadow lightens
|
|
538
|
+
one rung (the glass need not lift as hard off a flat bright field). */
|
|
539
|
+
.glass-resting[data-over-content="solid"] {
|
|
540
|
+
box-shadow: var(--glass-under-shadow-quiet), var(--glass-shadow-resting);
|
|
541
|
+
}
|
|
542
|
+
.glass-floating[data-over-content="solid"] {
|
|
543
|
+
box-shadow:
|
|
544
|
+
var(--glass-material-rim),
|
|
545
|
+
var(--glass-under-shadow-default),
|
|
546
|
+
var(--glass-shadow-floating);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/* Grain noise overlay — reuses paper texture SVG, no extra assets */
|
|
550
|
+
.glass-wash::after,
|
|
551
|
+
.glass-quiet::after,
|
|
552
|
+
.glass-resting::after,
|
|
553
|
+
.glass-floating::after,
|
|
554
|
+
.glass-overlay::after {
|
|
555
|
+
content: '';
|
|
556
|
+
position: absolute;
|
|
557
|
+
inset: 0;
|
|
558
|
+
border-radius: inherit;
|
|
559
|
+
background: var(--paper-clean-texture);
|
|
560
|
+
background-repeat: repeat;
|
|
561
|
+
background-size: var(--paper-texture-size);
|
|
562
|
+
/* D6.c M5 — the grain rung lifted off the dead 0.025 floor to a felt-but-
|
|
563
|
+
deft 0.038 (the page rung; the kitsch ceiling is ~0.05). The canonical
|
|
564
|
+
token still wins; this is the felt floor a no-override mount inherits. */
|
|
565
|
+
opacity: var(--glass-grain-opacity, 0.038);
|
|
566
|
+
pointer-events: none;
|
|
567
|
+
mix-blend-mode: overlay;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/* (b) The unified rim — `--glass-edge-light` full-perimeter inset ring on
|
|
571
|
+
EVERY material surface (AW.W22). Previously the rim was wired only into
|
|
572
|
+
the floating/overlay tiers' box-shadow stack; promoting it to the material
|
|
573
|
+
group makes wash/quiet/resting/card/dock-control all carry the same
|
|
574
|
+
silhouette catch-light from ONE token. The rim is its OWN `box-shadow`
|
|
575
|
+
declaration on the material group so it composes uniformly without
|
|
576
|
+
perturbing each tier's elevation recipe (the floating/overlay tiers drop
|
|
577
|
+
their now-redundant inline `--glass-edge-light` layer below). The alpha is
|
|
578
|
+
deliberately low so the rim never eats AA legibility over a busy
|
|
579
|
+
backdrop. */
|
|
580
|
+
.glass-material,
|
|
581
|
+
.glass-wash,
|
|
582
|
+
.glass-quiet,
|
|
583
|
+
.glass-resting,
|
|
584
|
+
.glass-floating,
|
|
585
|
+
.glass-overlay,
|
|
586
|
+
.glass-card,
|
|
587
|
+
.glass-specular-track,
|
|
588
|
+
.dock-icon-button {
|
|
589
|
+
--glass-material-rim: var(--glass-edge-light);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.dark .glass-wash::after,
|
|
593
|
+
.dark .glass-quiet::after,
|
|
594
|
+
.dark .glass-resting::after,
|
|
595
|
+
.dark .glass-floating::after,
|
|
596
|
+
.dark .glass-overlay::after {
|
|
597
|
+
mix-blend-mode: soft-light;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/* ── Convenience shorthands (tier + shape) ── */
|
|
601
|
+
|
|
602
|
+
/* Static surface utility — no hover lift, no transition.
|
|
603
|
+
Interactive cards live in <Card> (which owns its own hover composition)
|
|
604
|
+
or in components that explicitly opt into a hover variant. */
|
|
605
|
+
.glass-card {
|
|
606
|
+
/* Focus-elevation rungs (AQ.W3 §W3.1a) — identity = at-rest paint; the
|
|
607
|
+
`:has(:focus-visible)` rule redefines them (one source, shared with
|
|
608
|
+
the fallback). */
|
|
609
|
+
--card-focus-shadow: var(--shadow-card);
|
|
610
|
+
--card-focus-border: var(--glass-border-quiet);
|
|
611
|
+
position: relative;
|
|
612
|
+
background: color-mix(in oklab, var(--glass-bg-quiet), var(--glass-tint-source) var(--glass-tint-strength));
|
|
613
|
+
backdrop-filter: var(--glass-blur-quiet);
|
|
614
|
+
border: 1px solid var(--card-focus-border);
|
|
615
|
+
border-radius: var(--radius-card);
|
|
616
|
+
box-shadow: var(--glass-material-rim), var(--card-focus-shadow);
|
|
617
|
+
contain: layout style;
|
|
618
|
+
transition:
|
|
619
|
+
box-shadow var(--duration-fast) var(--ease-standard),
|
|
620
|
+
border-color var(--duration-fast) var(--ease-standard);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/* Elevate one rung on descendant KEYBOARD focus (`:focus-visible`, not
|
|
624
|
+
pointer). Scoped to `.glass-card`. `:has()` Baseline Widely; the
|
|
625
|
+
`@supports not selector(:has(*))` class fallback (below) ships anyway
|
|
626
|
+
(focus-elevation is UX-critical — child-state-styling MANDATORY clause). */
|
|
627
|
+
.glass-card:has(:focus-visible) {
|
|
628
|
+
--card-focus-shadow: var(--shadow-md);
|
|
629
|
+
--card-focus-border: var(--glass-border-floating);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/* ── Glassmorphic circular icon button ── */
|
|
633
|
+
|
|
634
|
+
.glass-btn {
|
|
635
|
+
@apply flex items-center justify-center cursor-pointer;
|
|
636
|
+
|
|
637
|
+
/* Individual-transform identity base (AQ.W3 §W3.2) — `scale: 1` at rest
|
|
638
|
+
mints the stacking context once so the hover/press scale below does
|
|
639
|
+
not flicker it into being (the z-index/anchor hazard). */
|
|
640
|
+
scale: 1;
|
|
641
|
+
width: var(--size-icon-btn);
|
|
642
|
+
height: var(--size-icon-btn);
|
|
643
|
+
border-radius: var(--radius-pill);
|
|
644
|
+
border: 1.5px solid var(--glass-border-wash);
|
|
645
|
+
background: var(--glass-bg-wash);
|
|
646
|
+
backdrop-filter: var(--glass-blur-wash);
|
|
647
|
+
color: var(--muted-foreground);
|
|
648
|
+
/* AX.W52 §6 doctrine — the surface legs ride the bezier `--ease-standard`;
|
|
649
|
+
the `scale` leg is reconciled onto `--spring-smooth` (was `--spring-snappy`)
|
|
650
|
+
so the icon-button hover settles on the SAME ONE register as `.btn-pill` /
|
|
651
|
+
`.tap-squish` / `btn-interactive` (the gate asserts one scale register
|
|
652
|
+
across all four). The press still reads crisp — `--spring-smooth` ζ=0.86
|
|
653
|
+
settles fast without a visible bounce. */
|
|
654
|
+
transition:
|
|
655
|
+
background var(--duration-fast) var(--ease-standard),
|
|
656
|
+
border-color var(--duration-fast) var(--ease-standard),
|
|
657
|
+
color var(--duration-fast) var(--ease-standard),
|
|
658
|
+
scale var(--duration-normal) var(--spring-smooth);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.glass-btn:hover:not(:disabled) {
|
|
662
|
+
background: color-mix(in srgb, var(--background) 85%, transparent);
|
|
663
|
+
border-color: var(--surface-tint-22);
|
|
664
|
+
color: var(--foreground);
|
|
665
|
+
scale: var(--scale-hover);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.glass-btn:active:not(:disabled) {
|
|
669
|
+
scale: var(--scale-press);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.glass-btn:focus-visible {
|
|
673
|
+
outline: none;
|
|
674
|
+
box-shadow: var(--focus-ring-shadow);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
.glass-btn:disabled {
|
|
678
|
+
opacity: 0.35;
|
|
679
|
+
cursor: not-allowed;
|
|
680
|
+
pointer-events: none;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
.glass-btn.is-active,
|
|
684
|
+
.glass-btn[aria-pressed="true"] {
|
|
685
|
+
background: var(--surface-tint-10);
|
|
686
|
+
border-color: var(--surface-tint-25);
|
|
687
|
+
color: var(--foreground);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/* ── Pill button base ─────────────────────────── */
|
|
691
|
+
.btn-pill {
|
|
692
|
+
@apply inline-flex items-center justify-center cursor-pointer font-medium border-none;
|
|
693
|
+
|
|
694
|
+
/* AX.W52 §6 identity base — `scale: 1` at rest so the hover/press scale
|
|
695
|
+
below animates the `scale` longhand (not `transform`) coherently and
|
|
696
|
+
does not flicker the stacking context into being (AQ.W3 §W3.2). */
|
|
697
|
+
scale: 1;
|
|
698
|
+
/* AX.W51 D18 — the pill GEOMETRY rides the ONE `--ui-scale` comfort axis.
|
|
699
|
+
gap / padding scale by the master scalar; the font reads the
|
|
700
|
+
`--control-text` register (`calc(--type-small * --ui-scale)`, the same
|
|
701
|
+
`text-sm` register the Button CVA paints), so a bare `.btn-pill` consumer
|
|
702
|
+
and a `<Button>` agree on the control font, and ONE `--ui-scale` override
|
|
703
|
+
grows the pill's height (via padding) + gap + font in lockstep. Desktop
|
|
704
|
+
default is the identity (scale 1 → the prior proportion). */
|
|
705
|
+
gap: calc(0.375rem * var(--ui-scale));
|
|
706
|
+
border-radius: var(--radius-pill);
|
|
707
|
+
padding: calc(0.5rem * var(--ui-scale)) calc(1rem * var(--ui-scale));
|
|
708
|
+
font-size: var(--control-text);
|
|
709
|
+
/* AX.W52 — the ONE coherent button transition (RED witness 5/6). The
|
|
710
|
+
SURFACE legs (bg/border/box-shadow/color/opacity) ride the bezier
|
|
711
|
+
`--ease-standard` (a colour cross-fade reads as a wobble on a spring);
|
|
712
|
+
the TRANSFORM leg (`scale`) rides `--spring-smooth` (the sub-perceptual
|
|
713
|
+
settle), so the hover lift + the surface cross-fade read as ONE glide.
|
|
714
|
+
The DEAD `transform` leg is retired — the variants animate the `scale`
|
|
715
|
+
longhand. CRITICAL: `.tap-squish` (composed alongside on the button
|
|
716
|
+
base, utilities.css) ALSO lists this full coherent set, so neither
|
|
717
|
+
class's `transition` shorthand can clobber the other down to a
|
|
718
|
+
scale-only animation — the cascade bug that left every button
|
|
719
|
+
transitioning ONLY `scale` (the live-readback RED, RED witness 5). The
|
|
720
|
+
doctrine: surface-props→bezier, transform→spring (tokens.css §2). */
|
|
721
|
+
transition:
|
|
722
|
+
background-color var(--duration-fast) var(--ease-standard),
|
|
723
|
+
border-color var(--duration-fast) var(--ease-standard),
|
|
724
|
+
box-shadow var(--duration-fast) var(--ease-standard),
|
|
725
|
+
color var(--duration-fast) var(--ease-standard),
|
|
726
|
+
opacity var(--duration-fast) var(--ease-standard),
|
|
727
|
+
scale var(--duration-fast) var(--spring-smooth);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.btn-pill > * {
|
|
731
|
+
@apply flex-shrink-0;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
.btn-pill:disabled {
|
|
736
|
+
opacity: var(--opacity-disabled);
|
|
737
|
+
cursor: not-allowed;
|
|
738
|
+
pointer-events: none;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/* ── Glass-button backdrop ─────────────────────────────────────────────
|
|
742
|
+
AX.W52 — the `glass`/`glass-wash` Button variants compose `.glass-wash`,
|
|
743
|
+
which resolves `--glass-blur-wash` = `blur(1px)` — a sub-perceptual
|
|
744
|
+
diffusion authored for a small detail TILE, not a button the user reads
|
|
745
|
+
as "glass" (the live readback found the glass variants at a negligible
|
|
746
|
+
1px). A hero control wants a real glass blur. `.btn-glass` re-points the
|
|
747
|
+
backdrop at `--glass-blur-btn` (the `quiet`-tier 10px radius + the gentle
|
|
748
|
+
`saturate(1.05) brightness(1.02)` light-concentration), so the glass
|
|
749
|
+
button variants actually read as liquid glass while the wash TILE tier
|
|
750
|
+
keeps its 1px untouched. Declares the UNPREFIXED `backdrop-filter` ONLY —
|
|
751
|
+
Lightning CSS emits the `-webkit-` companion per the single-source
|
|
752
|
+
authorship discipline above (hand-authoring both forms makes Lightning
|
|
753
|
+
dedup-and-keep-prefixed, which modern Chromium then drops). */
|
|
754
|
+
.btn-glass {
|
|
755
|
+
backdrop-filter: var(--glass-blur-btn);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/* ── Pill input ───────────────────────────────── */
|
|
759
|
+
.input-pill {
|
|
760
|
+
@apply flex w-full;
|
|
761
|
+
|
|
762
|
+
/* AX.W51 D18 — the input pill rides the ONE `--ui-scale` comfort axis: the
|
|
763
|
+
height reads the `--control-h-md` cohort (`max(calc(2.5rem * --ui-scale),
|
|
764
|
+
--control-floor)`, the h-10 register with the WCAG-44px coarse clamp), the
|
|
765
|
+
font reads `--control-text`, the inline padding scales — so ONE override
|
|
766
|
+
grows the field's height + padding + font in lockstep. Desktop default is
|
|
767
|
+
the identity. */
|
|
768
|
+
height: var(--control-h-md);
|
|
769
|
+
border-radius: var(--radius-pill);
|
|
770
|
+
/* AW.W13 — the resting field edge reads as an enclosed control at rest
|
|
771
|
+
(~15%α — ~2× the prior 8% glass-wash floor that vanished on cream). The
|
|
772
|
+
`:focus-visible` accent ring + the `:user-invalid`/`[aria-invalid]`
|
|
773
|
+
destructive ring below still win by order + specificity.
|
|
774
|
+
AX.W54 — moved off the srgb `--surface-tint-15` fork onto the glass
|
|
775
|
+
border seam (`--glass-border-floating`, the matching 15%α rung) so the
|
|
776
|
+
pill is ONE glass model with the rest of the ladder, not a divergent
|
|
777
|
+
tint axis. */
|
|
778
|
+
border: 1.5px solid var(--glass-border-floating);
|
|
779
|
+
background: var(--glass-bg-quiet);
|
|
780
|
+
/* AX.W54 — the pill reads as REAL glass: the quiet-tier 10px blur (the
|
|
781
|
+
same `--glass-blur-quiet` its `--glass-bg-quiet` background pairs with),
|
|
782
|
+
not the sub-perceptual `blur(1px)` wash it carried — and the
|
|
783
|
+
`--glass-level` knob now clarifies it in lockstep with every surface. */
|
|
784
|
+
backdrop-filter: var(--glass-blur-quiet);
|
|
785
|
+
padding: 0 calc(1rem * var(--ui-scale));
|
|
786
|
+
font-size: var(--control-text);
|
|
787
|
+
color: var(--foreground);
|
|
788
|
+
transition:
|
|
789
|
+
background-color var(--duration-fast) var(--ease-standard),
|
|
790
|
+
border-color var(--duration-fast) var(--ease-standard),
|
|
791
|
+
box-shadow var(--duration-fast) var(--ease-standard),
|
|
792
|
+
color var(--duration-fast) var(--ease-standard),
|
|
793
|
+
opacity var(--duration-fast) var(--ease-standard);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
.input-pill::placeholder {
|
|
797
|
+
color: var(--surface-tint-35);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/* AQ.W4 §W4.6 — keyboard-focus ring only. Was bare `:focus` (showed the
|
|
801
|
+
accent ring on a mouse click); `:focus-visible` matches the
|
|
802
|
+
`.focus-ring` / `.glass-btn` / dock-control discipline. Ordered BEFORE
|
|
803
|
+
the `:user-invalid` rungs so an invalid focused field shows destructive,
|
|
804
|
+
not accent (the `:user-invalid:focus-visible` rule below wins by order +
|
|
805
|
+
specificity). `:focus-visible` Baseline Widely — no fallback. */
|
|
806
|
+
.input-pill:focus-visible {
|
|
807
|
+
outline: none;
|
|
808
|
+
border-color: var(--color-accent-opaque, var(--ring));
|
|
809
|
+
box-shadow: 0 0 0 2px var(--color-accent, color-mix(in srgb, var(--ring) 30%, transparent));
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/* AQ.W4 §W4.1 — validity vocabulary on `.input-pill` (Input + Textarea).
|
|
813
|
+
The forgiving `:where(:user-invalid, .user-invalid-fallback,
|
|
814
|
+
[aria-invalid="true"])` group is ONE rule covering THREE trigger surfaces:
|
|
815
|
+
native constraint-validation (`:user-invalid`), the `useUserInvalidAria`
|
|
816
|
+
fallback class for engines without `:user-invalid` (css §3.2; NOT a
|
|
817
|
+
live+dead alias — the class is the SOLE fallback), AND (AW.W18) the
|
|
818
|
+
imperative `[aria-invalid="true"]` attribute. The `[aria-invalid]` arm
|
|
819
|
+
serves APP-DRIVEN validity — a custom key match, an async server check —
|
|
820
|
+
where there is no native constraint to trip `:user-invalid`, so the app
|
|
821
|
+
sets the ARIA attribute and the library's own ring paints with no
|
|
822
|
+
`:deep()` re-paint at the consumer. The destructive/success tints reuse
|
|
823
|
+
the semantic tokens via `color-mix` per the house pattern (W2). Non-color
|
|
824
|
+
reinforcement is the error TEXT (W4.5 `error` slot) + the `required`
|
|
825
|
+
asterisk — the border/bg shift is the supplementary cue, never the sole
|
|
826
|
+
one. `:user-valid` is intentionally subtle (border only, no bg fill) so
|
|
827
|
+
a long valid form is not a wall of green. */
|
|
828
|
+
.input-pill:where(:user-invalid, .user-invalid-fallback, [aria-invalid="true"]) {
|
|
829
|
+
border-color: var(--destructive);
|
|
830
|
+
background: color-mix(in srgb, var(--destructive) 8%, var(--glass-bg-quiet));
|
|
831
|
+
}
|
|
832
|
+
.input-pill:where(:user-valid, .user-valid-fallback) {
|
|
833
|
+
border-color: var(--success);
|
|
834
|
+
}
|
|
835
|
+
/* Invalid + keyboard-focused → destructive ring (overrides the accent
|
|
836
|
+
`:focus-visible` ring above by order + the extra invalid compound). The
|
|
837
|
+
`[aria-invalid]` arm rides the same widened group so an app-driven
|
|
838
|
+
invalid field shows the destructive ring on focus too. */
|
|
839
|
+
.input-pill:where(:user-invalid, .user-invalid-fallback, [aria-invalid="true"]):focus-visible {
|
|
840
|
+
border-color: var(--destructive);
|
|
841
|
+
box-shadow: 0 0 0 var(--focus-ring-width)
|
|
842
|
+
color-mix(in srgb, var(--destructive) 35%, transparent);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/* AQ.W4 §W4.3 — `field-sizing: content` autosize (opt-in via the Textarea
|
|
846
|
+
`autosize` prop → `[data-autosize]`). The textarea grows vertically with
|
|
847
|
+
content between a 3-line floor and a token-driven ceiling, then scrolls.
|
|
848
|
+
`field-sizing` Baseline Newly (Chromium 2024) — on a non-supporting
|
|
849
|
+
engine the declaration is ignored and the element keeps the
|
|
850
|
+
`min/max-block-size` bounds + standard scroll (no JS, no `@supports`,
|
|
851
|
+
no grid-mirror polyfill). The fixed-size path (default, `min-h-20`) is
|
|
852
|
+
the unchanged no-op fallback. */
|
|
853
|
+
.input-pill[data-autosize] {
|
|
854
|
+
field-sizing: content;
|
|
855
|
+
width: 100%;
|
|
856
|
+
min-block-size: 3lh;
|
|
857
|
+
max-block-size: var(--textarea-autosize-max, 12lh);
|
|
858
|
+
resize: vertical;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
.input-pill:disabled {
|
|
862
|
+
opacity: 0.5;
|
|
863
|
+
cursor: not-allowed;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/* AW.W13 — the Select trigger mirrors the input pill's resting border lift.
|
|
867
|
+
The reka-ui SelectTrigger renders `role="combobox"` over the `.glass-wash`
|
|
868
|
+
surface; its 8%α `--glass-border-wash` field edge vanished on cream just
|
|
869
|
+
like the input pill, so the same `--surface-tint-15` lift makes the
|
|
870
|
+
trigger read as an enclosed control at rest. Scoped to the combobox role
|
|
871
|
+
so the lift reaches ONLY the Select trigger, not every `.glass-wash`
|
|
872
|
+
surface. */
|
|
873
|
+
.glass-wash[role="combobox"] {
|
|
874
|
+
border-color: var(--surface-tint-15);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/* ── AW.W23 — chromatic edge dispersion (the polished-glass rim fringe) ──
|
|
878
|
+
An opt-in `.glass-chromatic` composes the warm/cool oklab fringe OVER the
|
|
879
|
+
W22 uniform rim (it does NOT replace it — the rim still rides each rung's
|
|
880
|
+
box-shadow). The fringe is a transparency garnish, so it composes ONLY
|
|
881
|
+
under `prefers-reduced-transparency: no-preference` and drops under
|
|
882
|
+
`reduce` (handled by the bracket below). The fringe lands as a dedicated
|
|
883
|
+
inset box-shadow pair on the material surface so it adds onto the rim. */
|
|
884
|
+
@media (prefers-reduced-transparency: no-preference) {
|
|
885
|
+
.glass-chromatic {
|
|
886
|
+
box-shadow: var(--glass-edge-dispersion), var(--glass-material-rim);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
/* ── Deck-position rail (AW.W16 · AX.W24) ───────────────────────────────────
|
|
892
|
+
A Tailwind `@utility` that RESTYLES the shipped `<Progress variant="default">`
|
|
893
|
+
(the reka-ui ProgressRoot track + its indicator child) into the thin
|
|
894
|
+
bottom-of-deck position rail. It composes the EXISTING fill — it does NOT
|
|
895
|
+
re-author a second track/range mechanic. Every visual axis is a
|
|
896
|
+
`--progress-rail-*` token, so a consumer/dark-flip re-tints with zero library
|
|
897
|
+
edit (`:root { --progress-rail-fill: … }`). The viewport-pinned chrome
|
|
898
|
+
(`position: fixed`, `env(safe-area-inset-bottom)`, the z-index) is CONSUMER-
|
|
899
|
+
supplied — this recipe owns only the LOOK.
|
|
900
|
+
|
|
901
|
+
AX.W24 — WHY `@utility`, not `@layer components`. The rail RESTYLES the default
|
|
902
|
+
Progress: it must override the `h-4` height + `rounded-pill` radius utilities the
|
|
903
|
+
variant bakes. Those utilities live in `@layer utilities`, which ALWAYS outranks
|
|
904
|
+
`@layer components` regardless of source order, so the prior `@layer components`
|
|
905
|
+
recipe's `height: 3px` was a silent no-op (the rail painted as a 16px bar). An
|
|
906
|
+
`@utility` lands the recipe IN `@layer utilities` alongside `h-4`, where a single-
|
|
907
|
+
class selector wins the geometry by source order — the idiomatic Tailwind-v4 way
|
|
908
|
+
to author a restyle-class that out-ranks base utilities (the `btn-audacious` /
|
|
909
|
+
`cartoon-surface` pattern).
|
|
910
|
+
|
|
911
|
+
The COLOUR fix is the SEPARATE token-read-at-source path (NOT a layer-juggle):
|
|
912
|
+
`ProgressDefault` reads `--progress-fill`/`--progress-track` and dropped its
|
|
913
|
+
`bg-secondary`/`bg-primary` utilities, so this recipe FEEDS those tokens rather
|
|
914
|
+
than fighting a utility colour. The `--progress-fill` set on the rail root cascades
|
|
915
|
+
to the indicator child (a CSS custom property inherits), so the indicator reads the
|
|
916
|
+
override hue. */
|
|
917
|
+
@utility glass-progress-rail {
|
|
918
|
+
/* The track hairline + zero radius — the rail reads as a thin square sliver, not
|
|
919
|
+
the variant's 16px `rounded-pill` bar. A single-class @utility ties `h-4`/
|
|
920
|
+
`rounded-pill` on SPECIFICITY (both 0,1,0) and Tailwind's utility sort can emit
|
|
921
|
+
`h-4` later, so the bare tie loses. The doubled-class self-reference (`&&` →
|
|
922
|
+
`.glass-progress-rail.glass-progress-rail`, specificity 0,2,0) wins the geometry
|
|
923
|
+
DETERMINISTICALLY over the base utilities regardless of emit order — the precise,
|
|
924
|
+
contained way to out-rank a base utility without `!important`. */
|
|
925
|
+
&& {
|
|
926
|
+
height: var(--progress-rail-h, 3px);
|
|
927
|
+
border-radius: 0;
|
|
928
|
+
}
|
|
929
|
+
/* Feed the token-read default variant — these route the consumer's
|
|
930
|
+
`--progress-rail-*` axes into the `--progress-fill`/`--progress-track` tokens
|
|
931
|
+
the variant reads at source (the cascade-correct colour path). */
|
|
932
|
+
--progress-track: var(
|
|
933
|
+
--progress-rail-track,
|
|
934
|
+
color-mix(in srgb, var(--foreground) 8%, transparent)
|
|
935
|
+
);
|
|
936
|
+
--progress-fill: var(--progress-rail-fill, var(--primary));
|
|
937
|
+
|
|
938
|
+
/* The fill — the shipped ProgressIndicator child. The indicator paints the
|
|
939
|
+
inherited `--progress-fill`; the leading-edge glow is re-authored (AX.W24 F2)
|
|
940
|
+
as a TRAILING right-edge INSET box-shadow that lives INSIDE the indicator's
|
|
941
|
+
own box. The prior `4px 0 8px` OUTSET rightward shadow extended PAST the fill
|
|
942
|
+
boundary into the ProgressRoot's load-bearing `overflow-hidden` clip and was
|
|
943
|
+
eaten — invisible at every value. An `inset` shadow offset leftward
|
|
944
|
+
(`inset -Npx 0 …`) brightens just inside the indicator's right (leading) edge
|
|
945
|
+
— the moving position head — and never crosses the fill boundary, so the clip
|
|
946
|
+
cannot eat it (the clip is never lifted; that would re-break the default
|
|
947
|
+
Progress look for every other consumer). `box-shadow` is a separate property
|
|
948
|
+
from `background`, so it composes cleanly over the token-read fill. */
|
|
949
|
+
&& > * {
|
|
950
|
+
border-radius: 0;
|
|
951
|
+
box-shadow:
|
|
952
|
+
inset
|
|
953
|
+
calc(-1 * var(--progress-rail-glow-x, 4px))
|
|
954
|
+
0
|
|
955
|
+
var(--progress-rail-glow-blur, 8px)
|
|
956
|
+
calc(-1 * var(--progress-rail-glow-spread, 2px))
|
|
957
|
+
color-mix(
|
|
958
|
+
in srgb,
|
|
959
|
+
hsl(0 0% 100%) var(--progress-rail-glow-strength, 55%),
|
|
960
|
+
transparent
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/* ── AX.W56 — the rounded-vs-squircle POLICY (re-homes AW.W23's squircle) ─────
|
|
966
|
+
Cards, glass buttons, and pills stay ROUND. The earlier AW.W23 block applied
|
|
967
|
+
`corner-shape: squircle` to `.glass-card`/`.glass-btn`/`.btn-pill`, which
|
|
968
|
+
inverted the design intent — the superellipse is imperceptible at a 16px card
|
|
969
|
+
radius or a stadium pill, so the squircle on those surfaces was visual cost
|
|
970
|
+
without read. The squircle is RE-HOMED onto the one surface where it reads: the
|
|
971
|
+
big-dock card shell (`dock.css` → `var(--corner-shape-bigdock)` under
|
|
972
|
+
`@supports (corner-shape: superellipse(2))`). This is a clean break — no alias,
|
|
973
|
+
no `--corner-shape-card` decl here (it resolves `round`, the default, so a
|
|
974
|
+
`corner-shape` rule would be a no-op). The `--corner-shape-*` token axis lives
|
|
975
|
+
in `theme.css`; a consumer who WANTS a squircle card opts in by re-pointing
|
|
976
|
+
`--corner-shape-card` at `superellipse(var(--corner-k-squircle))`. Do NOT
|
|
977
|
+
re-add a squircle rule to the card/button/pill surfaces — they are round by
|
|
978
|
+
policy. */
|
|
979
|
+
|
|
980
|
+
/* ── AX.W56(R1) — squircle coupling onto the overlay-band glass surfaces ───────
|
|
981
|
+
W54 makes GLASS the default for the Dialog + Sheet content surfaces; W56 couples
|
|
982
|
+
the SQUIRCLE onto them too. Both ride `.glass-floating` at the large
|
|
983
|
+
`--radius-dialog` (16px) modal radius where the superellipse READS — the same
|
|
984
|
+
iOS-26 idiom the big-dock card shell carries. The compound selectors scope the
|
|
985
|
+
shape to the dialog/sheet content ONLY (the `.rounded-dialog` class the
|
|
986
|
+
DialogContent surface composes, the `.sheet-animate` class the SheetContent
|
|
987
|
+
surface composes via `sheetVariants`) so it never leaks onto a bare
|
|
988
|
+
`.glass-floating` panel. Mirrors the big-dock idiom EXACTLY: an `@supports`-
|
|
989
|
+
gated ENHANCEMENT (Chrome 139+, no FF/Safari 2026) over the `border-radius`
|
|
990
|
+
round CONTRACT — the squircle is the better tier, NOT a degraded fallback. The
|
|
991
|
+
shape is the tokenized `var(--corner-shape-{dialog,sheet})` (theme.css) so it is
|
|
992
|
+
as overridable as the radius; a consumer dials either back to round by
|
|
993
|
+
re-pointing the alias. The `@supports` condition tests the LITERAL
|
|
994
|
+
`superellipse(2)` (a `var()` is not evaluable in an `@supports` query) — the same
|
|
995
|
+
Chrome-139 feature the token resolves to. */
|
|
996
|
+
@layer components {
|
|
997
|
+
@supports (corner-shape: superellipse(2)) {
|
|
998
|
+
.glass-floating.rounded-dialog {
|
|
999
|
+
corner-shape: var(--corner-shape-dialog);
|
|
1000
|
+
}
|
|
1001
|
+
.glass-floating.sheet-animate {
|
|
1002
|
+
corner-shape: var(--corner-shape-sheet);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
/* ── Accessibility: reduced transparency ──────────
|
|
1008
|
+
AX.W54 — the gestalt collapse: ONE `--glass-level: 0` replaces the ten per-rung
|
|
1009
|
+
opacity+blur clobbers. The level-0 path resolves solid `--card` + `blur(0)`
|
|
1010
|
+
through the SAME machinery (the opacity seam → 1, the blur seam → 0), so every
|
|
1011
|
+
glass surface — current and future — flattens with no per-rung enumeration. The
|
|
1012
|
+
grain + highlight kills stay (they sit off the level axis). */
|
|
1013
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
1014
|
+
:root {
|
|
1015
|
+
--glass-level: 0;
|
|
1016
|
+
--glass-grain-opacity: 0;
|
|
1017
|
+
--glass-highlight: 0 0 0 0 transparent;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
/* ── Accessibility: high contrast ─────────────────
|
|
1022
|
+
AX.W54 — a single bounded `--glass-level: 0.3` (mostly-solid, not fully flat)
|
|
1023
|
+
replaces the five per-rung opacity clobbers: every rung firms up toward solid
|
|
1024
|
+
for AA while keeping a hint of the glass register. ONE knob, every rung.
|
|
1025
|
+
|
|
1026
|
+
AX.W55 — the Clear↔Tinted user-escape (Apple's iOS-26.2 analog) COORDINATES the
|
|
1027
|
+
two axes here: the OPACITY axis rides W54's `--glass-level` (firm up toward solid)
|
|
1028
|
+
AND the TINT axis biases toward ink — `--glass-tint-source` → the warm-ink +
|
|
1029
|
+
`--glass-tint-strength` → the AA floor, the same darken the automatic bright
|
|
1030
|
+
bucket applies, now reached by the explicit "more contrast" user request. The two
|
|
1031
|
+
axes stay DISTINCT (level = opacity+blur, tint = darken) but share the bracket so
|
|
1032
|
+
a high-contrast user gets BOTH a firmer plate and the ink-tint legibility lift —
|
|
1033
|
+
one coordinated escape, not two parallel forks. */
|
|
1034
|
+
@media (prefers-contrast: more) {
|
|
1035
|
+
:root {
|
|
1036
|
+
--glass-level: 0.3;
|
|
1037
|
+
--glass-tint-source: var(--glass-tint-ink);
|
|
1038
|
+
--glass-tint-strength: var(--glass-tint-strength-aa);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
/* ── Accessibility: forced-colors (Windows High Contrast) glass-language skin ──
|
|
1043
|
+
AX.W36 — when the OS forces a palette (Windows High Contrast), the platform
|
|
1044
|
+
strips the whole decorative glass register: `backdrop-filter` is dropped, the
|
|
1045
|
+
`--glass-*` inset/box-shadow rungs vanish, and every meaning-bearing chroma
|
|
1046
|
+
flattens to one system color. A `.glass-floating` Dialog over a `.glass-resting`
|
|
1047
|
+
Card would read as two borderless transparent rectangles with no edge, and a
|
|
1048
|
+
green-vs-red StatusDot would resolve to one indistinguishable dot. This skin
|
|
1049
|
+
restores the STRUCTURE the glass chrome encoded — it does NOT pin glass-ui's
|
|
1050
|
+
brand colours (no `forced-color-adjust: none`); the chrome is decorative and
|
|
1051
|
+
YIELDS to the user's forced palette (css forced-colors guidance). Only the
|
|
1052
|
+
standard WHC system keywords are used (`Canvas`/`CanvasText`/`Highlight`) — no
|
|
1053
|
+
glass-ui token leaks into the WHC arm.
|
|
1054
|
+
|
|
1055
|
+
COORDINATES WITH W54 — the same `--glass-level: 0` opaque escape the reduced-
|
|
1056
|
+
transparency / high-contrast brackets ride. Setting the ONE clarity knob to 0
|
|
1057
|
+
collapses the `--glass-bg-*` opacity recipe to solid `--card` + `blur(0)` through
|
|
1058
|
+
the SAME machinery (no parallel solid recipe), so the platform sees opaque plates
|
|
1059
|
+
it maps to `Canvas`. The grain + highlight kills match the reduce bracket (they
|
|
1060
|
+
sit off the level axis). The W55 tint axis is moot under WHC (the user's palette
|
|
1061
|
+
supersedes the ink-darken) but harmless — the opaque plate is system-painted. */
|
|
1062
|
+
@media (forced-colors: active) {
|
|
1063
|
+
:root {
|
|
1064
|
+
--glass-level: 0;
|
|
1065
|
+
--glass-grain-opacity: 0;
|
|
1066
|
+
--glass-highlight: 0 0 0 0 transparent;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/* Every glass-material ladder rung relied on `backdrop-filter` + an inset
|
|
1070
|
+
`box-shadow` rim for its silhouette — both gone under WHC. Restore a real
|
|
1071
|
+
`border` keyed to the system text colour so each tier pane reads as a boxed
|
|
1072
|
+
region. The selector group MIRRORS the ladder definition above (one source,
|
|
1073
|
+
not a hand-rolled re-list) so a future rung added to the material group
|
|
1074
|
+
inherits the WHC edge for free — and a `.glass-material` div that became one
|
|
1075
|
+
via the GlassPanel retire gets the edge with no per-component arm. */
|
|
1076
|
+
.glass-material,
|
|
1077
|
+
.glass-wash,
|
|
1078
|
+
.glass-quiet,
|
|
1079
|
+
.glass-resting,
|
|
1080
|
+
.glass-floating,
|
|
1081
|
+
.glass-overlay,
|
|
1082
|
+
.glass-card,
|
|
1083
|
+
.glass-drawer {
|
|
1084
|
+
border: 1px solid CanvasText;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
/* The floating + overlay rungs (Dialog / Sheet / Popover / DropdownMenu /
|
|
1088
|
+
HoverCard compose them) + the Drawer (the overlay-band sheet, drawer.css)
|
|
1089
|
+
get the canonical `Canvas` fill so the modal reads as a DISTINCT boxed
|
|
1090
|
+
region OVER the page, not a transparent overlap — the hierarchy survives.
|
|
1091
|
+
The `CanvasText` edge above already draws the box; this opaque fill
|
|
1092
|
+
separates the floating layer from the content beneath it. */
|
|
1093
|
+
.glass-floating,
|
|
1094
|
+
.glass-overlay,
|
|
1095
|
+
.glass-drawer {
|
|
1096
|
+
background: Canvas;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/* The decorative specular catch-light (`::before`) + grain overlay (`::after`)
|
|
1100
|
+
paint nothing meaningful once chroma collapses — yield them entirely (the
|
|
1101
|
+
same decorative-chrome cede the `--glass-level: 0` opaque escape performs on
|
|
1102
|
+
the body). They are NOT part of the structure the skin restores. */
|
|
1103
|
+
.glass-material::before,
|
|
1104
|
+
.glass-wash::before,
|
|
1105
|
+
.glass-quiet::before,
|
|
1106
|
+
.glass-resting::before,
|
|
1107
|
+
.glass-floating::before,
|
|
1108
|
+
.glass-overlay::before,
|
|
1109
|
+
.glass-card::before,
|
|
1110
|
+
.glass-drawer::before,
|
|
1111
|
+
.glass-wash::after,
|
|
1112
|
+
.glass-quiet::after,
|
|
1113
|
+
.glass-resting::after,
|
|
1114
|
+
.glass-floating::after,
|
|
1115
|
+
.glass-overlay::after {
|
|
1116
|
+
display: none;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
/* Meaning-bearing hue identity → a bordered glyph that keeps ok/warn/error
|
|
1120
|
+
distinguishable by a WHC-HONORED system color, NOT a glass-ui hue (which WHC
|
|
1121
|
+
strips to one fill). StatusDot reflects its variant onto `data-status`
|
|
1122
|
+
(StatusDot.vue) and the inner dot carries `.status-dot__dot`; the
|
|
1123
|
+
`role="img"` + `aria-label` accessible name already ships, so this is the
|
|
1124
|
+
VISUAL differentiation only. A bare bordered dot + the per-status fill triplet
|
|
1125
|
+
below means a sighted WHC user can still tell the row apart.
|
|
1126
|
+
|
|
1127
|
+
`!important` IS load-bearing here: StatusDot paints the dot fill via an INLINE
|
|
1128
|
+
`:style="dotStyle"` (`background-color`, specificity 1,0,0,0), which outranks
|
|
1129
|
+
any stylesheet selector AND which forced-colors then `forced-color-adjust:
|
|
1130
|
+
auto`-remaps to a single Canvas-white — collapsing the row to one indistinct
|
|
1131
|
+
dot. The WHC arm must override that inline fill, and inline can only be beaten
|
|
1132
|
+
by `!important`; this is the documented forced-colors degradation path (the
|
|
1133
|
+
arm only activates under WHC). The component keeps its inline hue in normal
|
|
1134
|
+
mode untouched. */
|
|
1135
|
+
.status-dot__dot {
|
|
1136
|
+
border: 1px solid CanvasText;
|
|
1137
|
+
/* The bordered ring alone separates a dot from its surround; the per-status
|
|
1138
|
+
fill below adds the WHC-honored distinction. */
|
|
1139
|
+
}
|
|
1140
|
+
/* The inner painted fill (`.status-dot__fill`, `h-full w-full`) + the pulse ring
|
|
1141
|
+
carry the SAME inline glass-ui hue StatusDot paints — under WHC they cover the
|
|
1142
|
+
outer `.status-dot__dot` glyph with a `forced-color-adjust`-remapped Canvas
|
|
1143
|
+
fill (the row collapses to one white dot even with the outer system color set).
|
|
1144
|
+
Yield them so the outer bordered + system-color glyph is the sole visible dot. */
|
|
1145
|
+
.status-dot__fill,
|
|
1146
|
+
.status-dot__pulse {
|
|
1147
|
+
display: none;
|
|
1148
|
+
}
|
|
1149
|
+
/* The ok/warn/error/idle distinction rides the standard WHC keyword palette:
|
|
1150
|
+
active (ok) → Highlight (the affirmative system accent)
|
|
1151
|
+
paused (warn) → Mark (the marked/attention system color)
|
|
1152
|
+
error → CanvasText (a solid filled dot — the strongest weight)
|
|
1153
|
+
idle/custom → Canvas (a hollow dot — only the CanvasText ring reads)
|
|
1154
|
+
Each stays distinguishable by the system color the user's palette assigns,
|
|
1155
|
+
never glass-ui chroma. The `!important` beats the component's inline fill. */
|
|
1156
|
+
.status-dot__dot[data-status="active"] {
|
|
1157
|
+
background: Highlight !important;
|
|
1158
|
+
}
|
|
1159
|
+
.status-dot__dot[data-status="paused"] {
|
|
1160
|
+
background: Mark !important;
|
|
1161
|
+
}
|
|
1162
|
+
.status-dot__dot[data-status="error"] {
|
|
1163
|
+
background: CanvasText !important;
|
|
1164
|
+
}
|
|
1165
|
+
.status-dot__dot[data-status="idle"],
|
|
1166
|
+
.status-dot__dot[data-status="custom"] {
|
|
1167
|
+
background: Canvas !important;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/* ── @supports fallbacks for non-backdrop-filter browsers ── */
|
|
1172
|
+
@layer components {
|
|
1173
|
+
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
|
1174
|
+
.glass-wash {
|
|
1175
|
+
background: color-mix(in srgb, var(--card) 80%, transparent);
|
|
1176
|
+
}
|
|
1177
|
+
.glass-quiet {
|
|
1178
|
+
background: color-mix(in srgb, var(--card) 88%, transparent);
|
|
1179
|
+
}
|
|
1180
|
+
.glass-resting {
|
|
1181
|
+
background: color-mix(in srgb, var(--card) 94%, transparent);
|
|
1182
|
+
}
|
|
1183
|
+
.glass-floating {
|
|
1184
|
+
background: color-mix(in srgb, var(--card) 98%, transparent);
|
|
1185
|
+
}
|
|
1186
|
+
.glass-overlay {
|
|
1187
|
+
background: var(--card);
|
|
1188
|
+
}
|
|
1189
|
+
.glass-btn {
|
|
1190
|
+
background: color-mix(in srgb, var(--background) 92%, transparent);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
/* ── :has() focus-elevation fallback (AQ.W3 §W3.1a) ──────────────────────
|
|
1196
|
+
SOLE fallback for engines without `:has()`. `.is-focus-within` is toggled
|
|
1197
|
+
by a ≤6-LOC focusin/focusout guard in `Card.vue` that runs ONLY when
|
|
1198
|
+
`!CSS.supports('selector(:has(*))')` (modern engines run zero JS). The
|
|
1199
|
+
class redefines the SAME `--card-focus-*` rungs as the `:has()` rule, so the
|
|
1200
|
+
override values have one source. NOT a live+dead alias — `:has()` is the
|
|
1201
|
+
enhancement, this the documented degradation path. */
|
|
1202
|
+
@supports not selector(:has(*)) {
|
|
1203
|
+
.glass-card.is-focus-within {
|
|
1204
|
+
--card-focus-shadow: var(--shadow-md);
|
|
1205
|
+
--card-focus-border: var(--glass-border-floating);
|
|
1206
|
+
}
|
|
1207
|
+
}
|