@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/utilities.css
CHANGED
|
@@ -1,19 +1,1291 @@
|
|
|
1
|
-
/* Shared utility classes
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
/*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
/* Shared utility classes */
|
|
2
|
+
|
|
3
|
+
/* ── Popover/dropdown shared animation ──
|
|
4
|
+
`@utility` form (V.W3.T8 / A5 §5.9 standardisation): data-state animation
|
|
5
|
+
pair for popover-class containers (Popover, DropdownMenu, ContextMenu,
|
|
6
|
+
Tooltip, HoverCard, Select). Composed alongside `.slide-in-from-side`
|
|
7
|
+
for direction-keyed entries. */
|
|
8
|
+
@utility popover-animate {
|
|
9
|
+
@apply data-[state=open]:animate-in data-[state=closed]:animate-out
|
|
10
|
+
data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0
|
|
11
|
+
data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95;
|
|
12
|
+
|
|
13
|
+
/* ── 3.11 §2i-bis — the popover rides the SPRING grammar, not the generic
|
|
14
|
+
tw-animate tween. Per the tokens.css EASING DOCTRINE: enter = spring
|
|
15
|
+
(`--spring-snappy`, the crisp floating-surface entry), exit = bezier
|
|
16
|
+
(`--ease-out`, an exit must never overshoot past gone). The four custom
|
|
17
|
+
properties are the consumer seam — override per host, never fork the
|
|
18
|
+
utility. PRM snaps both directions to a single frame. */
|
|
19
|
+
&[data-state="open"] {
|
|
20
|
+
animation-duration: var(--popover-enter-duration, 200ms);
|
|
21
|
+
animation-timing-function: var(--popover-enter-ease, var(--spring-snappy));
|
|
22
|
+
}
|
|
23
|
+
&[data-state="closed"] {
|
|
24
|
+
animation-duration: var(--popover-exit-duration, 100ms);
|
|
25
|
+
animation-timing-function: var(--popover-exit-ease, var(--ease-out));
|
|
26
|
+
}
|
|
27
|
+
@media (prefers-reduced-motion: reduce) {
|
|
28
|
+
&[data-state="open"],
|
|
29
|
+
&[data-state="closed"] {
|
|
30
|
+
animation-duration: 1ms;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* ── Slide-in entry by side data-attr ──
|
|
36
|
+
`@utility` form (V.W3.T8). Side-keyed slide entry that pairs with
|
|
37
|
+
reka-ui's `data-side` (`top|right|bottom|left`). The animation
|
|
38
|
+
direction inverts each side so panels enter from outside the
|
|
39
|
+
anchor's edge. */
|
|
40
|
+
@utility slide-in-from-side {
|
|
41
|
+
@apply data-[side=bottom]:slide-in-from-top-2
|
|
42
|
+
data-[side=left]:slide-in-from-right-2
|
|
43
|
+
data-[side=right]:slide-in-from-left-2
|
|
44
|
+
data-[side=top]:slide-in-from-bottom-2;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@layer components {
|
|
48
|
+
/* ── Section description (V.W3.T7) ──────────────────────────────
|
|
49
|
+
Canonical recipe for the `<Section>` primitive's description
|
|
50
|
+
paragraph. Composes the body type + muted-foreground color in
|
|
51
|
+
one declaration so consumers don't fight twMerge's
|
|
52
|
+
typography-class collapse. */
|
|
53
|
+
.section-description {
|
|
54
|
+
font-size: var(--type-body);
|
|
55
|
+
color: var(--muted-foreground);
|
|
56
|
+
line-height: 1.5;
|
|
57
|
+
text-wrap: pretty; /* AQ.W3 §W3.3 — prose-register orphan/river fix */
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* ── Labeled-field label (V.W3.T5 / A5 §5.5) ────────────────────
|
|
61
|
+
Canonical recipe for `<LabeledField>`'s tooltip-wrapped label
|
|
62
|
+
layer. Four sibling wrappers (LabeledInput, LabeledSelect,
|
|
63
|
+
LabeledSlider, LabeledSwitch) used to repeat this literal across
|
|
64
|
+
their templates; promoting it to a utility lets the parent SFC
|
|
65
|
+
own one canonical declaration. */
|
|
66
|
+
.labeled-field-label {
|
|
67
|
+
font-family: var(--font-display);
|
|
68
|
+
font-size: var(--type-body);
|
|
69
|
+
/* AQ.W4 §W3.1c — the label color reads the group-state variable so the
|
|
70
|
+
`:has(:user-invalid)` rule below reddens it (one source of the
|
|
71
|
+
override value, shared with the `.has-error` fallback class). */
|
|
72
|
+
color: var(--field-label-color, var(--muted-foreground));
|
|
73
|
+
cursor: help;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* AQ.W4 §W3.1c — LabeledField group error highlight. When any descendant
|
|
77
|
+
control is `:user-invalid`, the whole field group marks (the label
|
|
78
|
+
reddens via `--field-label-color`, the error region reveals). Scoped to
|
|
79
|
+
`.labeled-field` — never `body`, never a bare element. `:has()` Baseline
|
|
80
|
+
Widely; the `.has-error` / `.has-valid` fallback classes (toggled by the
|
|
81
|
+
`useUserInvalidAria` fallback path) are the SOLE degradation for engines
|
|
82
|
+
without `:has()`/`:user-invalid` (not a live+dead alias). */
|
|
83
|
+
.labeled-field {
|
|
84
|
+
--field-label-color: var(--muted-foreground);
|
|
85
|
+
}
|
|
86
|
+
.labeled-field:has(:user-invalid),
|
|
87
|
+
.labeled-field.has-error {
|
|
88
|
+
--field-label-color: var(--destructive);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* The error region: hidden at rest, revealed once a descendant is
|
|
92
|
+
`:user-invalid` (or the `.has-error` fallback class is set). Non-color
|
|
93
|
+
reinforcement for the validity tint — the error TEXT is the primary
|
|
94
|
+
cue, the border/bg shift the supplement (forms guide: never color
|
|
95
|
+
alone). */
|
|
96
|
+
.labeled-field-error {
|
|
97
|
+
display: none;
|
|
98
|
+
font-size: var(--type-small);
|
|
99
|
+
color: var(--destructive);
|
|
100
|
+
margin-block-start: 0.25rem;
|
|
101
|
+
}
|
|
102
|
+
.labeled-field:has(:user-invalid) .labeled-field-error,
|
|
103
|
+
.labeled-field.has-error .labeled-field-error {
|
|
104
|
+
display: block;
|
|
105
|
+
}
|
|
106
|
+
/* Hide the error text once the field reads valid (the user corrected it). */
|
|
107
|
+
.labeled-field:has(:user-valid) .labeled-field-error,
|
|
108
|
+
.labeled-field.has-valid .labeled-field-error {
|
|
109
|
+
display: none;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* ── Popover-content recipe (V.W3.T3 / A5 §3.2) ─────────────────
|
|
113
|
+
Canonical recipe for floating-tier portal containers
|
|
114
|
+
(PopoverContent, DropdownMenuContent, SelectContent, etc.).
|
|
115
|
+
Covers radius + outline + popover-foreground; consumers compose
|
|
116
|
+
this with `.glass-floating` (bg + blur + border + shadow), with
|
|
117
|
+
`.popover-animate` (data-state animation), and with the per-host
|
|
118
|
+
z-rung. The floating-tier shadow comes from
|
|
119
|
+
`--glass-shadow-floating` via `.glass-floating`, replacing the
|
|
120
|
+
`shadow-md`/`shadow-lg` literals that bypassed the canonical
|
|
121
|
+
cascade. */
|
|
122
|
+
.popover-content {
|
|
123
|
+
border-radius: var(--radius-panel);
|
|
124
|
+
color: var(--popover-foreground);
|
|
125
|
+
outline: none;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ── Scrollbar ── */
|
|
129
|
+
.scrollbar-hidden {
|
|
130
|
+
scrollbar-width: none;
|
|
131
|
+
}
|
|
132
|
+
.scrollbar-hidden::-webkit-scrollbar {
|
|
133
|
+
display: none;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* AQ.W3 §W3.4 — tokenized thin scrollbar. Standard `scrollbar-width` +
|
|
137
|
+
`scrollbar-color` (Baseline Widely, Chromium 121+) is the primary path,
|
|
138
|
+
reading `--scrollbar-{thumb,track}` (auto-dark via `--muted-foreground`).
|
|
139
|
+
The legacy `::-webkit-scrollbar` family is the SOLE fallback for older
|
|
140
|
+
WebKit/Blink, gated on `@supports not (scrollbar-color: auto)` so modern
|
|
141
|
+
engines never paint it — not a live+dead alias. */
|
|
142
|
+
.scrollbar-thin {
|
|
143
|
+
scrollbar-width: thin;
|
|
144
|
+
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
|
|
145
|
+
}
|
|
146
|
+
@supports not (scrollbar-color: auto) {
|
|
147
|
+
.scrollbar-thin::-webkit-scrollbar {
|
|
148
|
+
width: 8px;
|
|
149
|
+
height: 8px;
|
|
150
|
+
}
|
|
151
|
+
.scrollbar-thin::-webkit-scrollbar-track {
|
|
152
|
+
background: var(--scrollbar-track);
|
|
153
|
+
}
|
|
154
|
+
.scrollbar-thin::-webkit-scrollbar-thumb {
|
|
155
|
+
background: var(--scrollbar-thumb);
|
|
156
|
+
border-radius: var(--radius-pill);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* ── Focus ring — auto-activates on :focus-visible ── */
|
|
161
|
+
.focus-ring:focus-visible {
|
|
162
|
+
outline: none;
|
|
163
|
+
border-radius: var(--radius-pill);
|
|
164
|
+
box-shadow: var(--focus-ring-shadow);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* D6.b — `focus-ring-gold`: the opt-in gilded focus ring. Overrides ONLY
|
|
168
|
+
`--focus-ring-color` (tokens.css) → the shared `--focus-ring-shadow`
|
|
169
|
+
re-resolves to gold for THIS element; the default ring elsewhere is
|
|
170
|
+
unchanged. Gold-as-state is the "you are here / this is live" mark — a
|
|
171
|
+
transient highlight (appears on focus-visible, gone on blur), so gold is
|
|
172
|
+
lawful here where a permanent gold furniture would not be. Mirror of the
|
|
173
|
+
`focus-ring` custom-property pattern: a consumer composes
|
|
174
|
+
`class="focus-ring focus-ring-gold"` to gild the ring on the active rung;
|
|
175
|
+
the resting mark keeps its structural color (the four-voice law: red is
|
|
176
|
+
identity, gold is distinction). */
|
|
177
|
+
.focus-ring-gold {
|
|
178
|
+
--focus-ring-color: var(--gold);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* ── Hairline-accent — 0.5px catch-light + under-shadow pair ──
|
|
182
|
+
Composes the canonical `--border-hairline` token (tokens.css §
|
|
183
|
+
Glass-decorative). Adopt at any surface that wants the canonical
|
|
184
|
+
2-stop hairline (dock surfaces, instrument-chassis). Auto-mirrors
|
|
185
|
+
in dark via the .dark token override. */
|
|
186
|
+
.hairline-accent {
|
|
187
|
+
box-shadow: var(--border-hairline);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* ── Interactive item (hover bg + focus ring + active scale) ── */
|
|
191
|
+
.interactive-item {
|
|
192
|
+
/* AQ.W3 §W3.2 identity base */
|
|
193
|
+
scale: 1;
|
|
194
|
+
border-radius: var(--radius-lg);
|
|
195
|
+
user-select: none;
|
|
196
|
+
transition:
|
|
197
|
+
background-color var(--duration-fast) var(--ease-standard),
|
|
198
|
+
color var(--duration-fast) var(--ease-standard),
|
|
199
|
+
border-color var(--duration-fast) var(--ease-standard),
|
|
200
|
+
box-shadow var(--duration-fast) var(--ease-standard),
|
|
201
|
+
scale var(--duration-fast) var(--ease-standard);
|
|
202
|
+
}
|
|
203
|
+
.interactive-item:hover {
|
|
204
|
+
background-color: color-mix(in srgb, var(--accent) 50%, transparent);
|
|
205
|
+
}
|
|
206
|
+
.interactive-item:focus-visible {
|
|
207
|
+
outline: none;
|
|
208
|
+
box-shadow: var(--focus-ring-shadow);
|
|
209
|
+
}
|
|
210
|
+
.interactive-item:active {
|
|
211
|
+
/* AW.W31.a — fold the residual literal press scale onto the canonical
|
|
212
|
+
`--scale-press*` cohort (the prior `0.98` was the sole per-atom press
|
|
213
|
+
literal surviving the W2/W3/W25 sweeps). `--scale-press-sm` (0.97) is
|
|
214
|
+
the soft press rung the button + slider recipes consume — ONE press
|
|
215
|
+
vocabulary, no per-atom literal. The 0.98→0.97 delta is sub-perceptual. */
|
|
216
|
+
scale: var(--scale-press-sm);
|
|
217
|
+
}
|
|
218
|
+
.interactive-item:disabled,
|
|
219
|
+
.interactive-item[data-disabled] {
|
|
220
|
+
pointer-events: none;
|
|
221
|
+
opacity: var(--opacity-disabled);
|
|
222
|
+
cursor: not-allowed;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* ── Tap-squish — universal press-state choreography ──
|
|
226
|
+
AL-W10 SLIM (X6 §M-1 + Y7 §3 + DESIGN.md §L3). I compose this
|
|
227
|
+
class onto every interactive surface that wants the iOS tap-
|
|
228
|
+
squish idiom; the press scales to `--scale-press` (0.96, the
|
|
229
|
+
canonical iOS `.regular` control rung). I keep this as a single
|
|
230
|
+
canonical rung — NOT a 3-rung ladder. The one outlier I leave
|
|
231
|
+
alone is the button `0.97` (`--scale-press-btn`), which consumes
|
|
232
|
+
its own slightly-softer token directly. The dock control now
|
|
233
|
+
tracks the canonical `--scale-press` (0.96) too (AT.W7-dock-b
|
|
234
|
+
press canon — the prior deeper `0.92` was retired to ONE source).
|
|
235
|
+
|
|
236
|
+
AX.W52 §6 doctrine — the `scale` register is reconciled onto
|
|
237
|
+
`--spring-smooth` (was `--spring-snappy`), the ONE button/interactive
|
|
238
|
+
scale register the §6 easing doctrine records and the gate asserts
|
|
239
|
+
across `.btn-pill`/`.tap-squish`/`.glass-btn`/`btn-interactive`. At
|
|
240
|
+
ζ=0.86 `--spring-smooth` settles a press crisply with a sub-perceptual
|
|
241
|
+
(~0.5%) peak — no visible bounce — so press still reads alive while
|
|
242
|
+
hover reads as a smooth glide on the SAME spring.
|
|
243
|
+
|
|
244
|
+
The transition lists the SURFACE legs (bg/border/box-shadow/color/
|
|
245
|
+
opacity) on the bezier `--ease-standard` alongside the spring `scale`
|
|
246
|
+
leg — the SAME coherent set `.btn-pill` carries. This is load-bearing:
|
|
247
|
+
`.tap-squish` and `.btn-pill` are co-composed on the button base and
|
|
248
|
+
this `transition` shorthand would otherwise CLOBBER `.btn-pill`'s
|
|
249
|
+
surface legs down to a scale-only animation (the cascade bug that left
|
|
250
|
+
every button transitioning ONLY `scale` — the live-readback RED). With
|
|
251
|
+
both classes carrying the full set, whichever wins the surface cross-
|
|
252
|
+
fade survives. On a non-button press surface the extra legs are inert
|
|
253
|
+
unless that prop changes, in which case a smooth cross-fade is the
|
|
254
|
+
desired interactive read anyway.
|
|
255
|
+
|
|
256
|
+
Under PRM I retire the press transform entirely and lean on the
|
|
257
|
+
consumer's own hover/active bg-tint cascade to acknowledge the
|
|
258
|
+
press. */
|
|
259
|
+
.tap-squish {
|
|
260
|
+
/* AQ.W3 §W3.2 identity base */
|
|
261
|
+
scale: 1;
|
|
262
|
+
transition:
|
|
263
|
+
background-color var(--duration-fast) var(--ease-standard),
|
|
264
|
+
border-color var(--duration-fast) var(--ease-standard),
|
|
265
|
+
box-shadow var(--duration-fast) var(--ease-standard),
|
|
266
|
+
color var(--duration-fast) var(--ease-standard),
|
|
267
|
+
opacity var(--duration-fast) var(--ease-standard),
|
|
268
|
+
scale var(--duration-fast) var(--spring-smooth);
|
|
269
|
+
transform-origin: center center;
|
|
270
|
+
}
|
|
271
|
+
.tap-squish:active {
|
|
272
|
+
scale: var(--scale-press);
|
|
273
|
+
}
|
|
274
|
+
@media (prefers-reduced-motion: reduce) {
|
|
275
|
+
.tap-squish:active {
|
|
276
|
+
/* Reset to the longhand identity (not `transform: none`) so the
|
|
277
|
+
press scale is neutralized under PRM. */
|
|
278
|
+
scale: 1;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/* ── Status dot indicator ── */
|
|
283
|
+
.status-dot {
|
|
284
|
+
display: inline-block;
|
|
285
|
+
width: 0.5rem;
|
|
286
|
+
height: 0.5rem;
|
|
287
|
+
border-radius: var(--radius-pill);
|
|
288
|
+
flex-shrink: 0;
|
|
289
|
+
transition:
|
|
290
|
+
color var(--duration-fast) var(--ease-standard),
|
|
291
|
+
background var(--duration-fast) var(--ease-standard),
|
|
292
|
+
box-shadow var(--duration-fast) var(--ease-standard);
|
|
293
|
+
}
|
|
294
|
+
/* ── Depth text (layered offset stamp) ──
|
|
295
|
+
AW.W26 — composes the `--text-shadow-depth` @theme identity token (the
|
|
296
|
+
hard offset stack + soft ambient tail) instead of a hand-rolled multi-stop
|
|
297
|
+
literal. The token holds the stack inside the `@theme` defs (gate 4) and
|
|
298
|
+
re-tints under `.dark` via `--shadow`/`--shadow-color` BY CONSTRUCTION. */
|
|
299
|
+
.depth-text {
|
|
300
|
+
--depth-color-primary: var(--primary);
|
|
301
|
+
|
|
302
|
+
color: var(--depth-color-primary);
|
|
303
|
+
text-shadow: var(--text-shadow-depth);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* ── 3D preserve ── */
|
|
307
|
+
.preserve-3d {
|
|
308
|
+
transform-style: preserve-3d;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* ── iOS input font-size fix (prevent auto-zoom) ── */
|
|
312
|
+
.ios input,
|
|
313
|
+
.ios select,
|
|
314
|
+
.ios textarea,
|
|
315
|
+
.ios [contenteditable] {
|
|
316
|
+
font-size: max(1rem, 1em);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* ── Scroll fade masks ──
|
|
320
|
+
Single-source `mask-image` (AV.W16 TW7): the `-webkit-` companion is
|
|
321
|
+
dropped — Tailwind v4's Lightning CSS build pipeline auto-prefixes per
|
|
322
|
+
the browserslist targets, mirroring the glass ladder's single-source
|
|
323
|
+
`backdrop-filter` (glass.css). NOTE: this is the consistency cut; it
|
|
324
|
+
does NOT conflict with W15's `@supports`-gated older-Safari mask
|
|
325
|
+
fallback (that fallback is the distinct case where the companion is
|
|
326
|
+
genuinely needed and W15 gates it). */
|
|
327
|
+
.scroll-fade-mask {
|
|
328
|
+
mask-image: linear-gradient(to right, transparent, black var(--mask-fade-width), black calc(100% - var(--mask-fade-width)), transparent);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.scroll-fade-top {
|
|
332
|
+
mask-image: linear-gradient(to bottom, transparent, black var(--mask-fade-width));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.scroll-fade-bottom {
|
|
336
|
+
mask-image: linear-gradient(to bottom, black calc(100% - var(--mask-fade-width)), transparent);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.scroll-fade-y {
|
|
340
|
+
mask-image: linear-gradient(to bottom, transparent, black var(--mask-fade-width), black calc(100% - var(--mask-fade-width)), transparent);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/* ── Deferred section — render-skip an offscreen subtree (AQ.W3 §5) ──────
|
|
344
|
+
`content-visibility: auto` skips layout/paint/style of an offscreen
|
|
345
|
+
subtree — the INP-under-load lever (fourier γ, muster, speedtest, words).
|
|
346
|
+
`contain-intrinsic-size: auto <estimate>` is measurement-safe: the `auto`
|
|
347
|
+
prefix makes the engine REMEMBER the last-rendered size (no scroll-jump on
|
|
348
|
+
re-entry); plain `<estimate>` freezes the section + thrashes the scrollbar
|
|
349
|
+
— the trap this closes. Estimate is token-driven (`--deferred-section-
|
|
350
|
+
size`, default 30rem). `.deferred-section--cached` →
|
|
351
|
+
`content-visibility: hidden` (always skipped, state CACHED — cheaper to
|
|
352
|
+
re-show than `display:none`). Baseline Widely; unsupporting engines render
|
|
353
|
+
normally. JS HOOK: the engine fires `contentvisibilityautostatechange`
|
|
354
|
+
{ skipped } at the render-skip boundary — pair with `useRAFLoop`
|
|
355
|
+
pause/resume: `el.addEventListener('contentvisibilityautostatechange',
|
|
356
|
+
e => e.skipped ? loop.pause() : loop.resume())`. */
|
|
357
|
+
.deferred-section {
|
|
358
|
+
content-visibility: auto;
|
|
359
|
+
contain-intrinsic-size: auto var(--deferred-section-size, 30rem);
|
|
360
|
+
}
|
|
361
|
+
.deferred-section--cached {
|
|
362
|
+
content-visibility: hidden;
|
|
363
|
+
contain-intrinsic-size: auto var(--deferred-section-size, 30rem);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/* ── Card scroll-timeline host (AI.W1-α) ───────────────────────────────
|
|
367
|
+
Canonical scroll-overflow host that emits the `--card-scroll` named
|
|
368
|
+
scroll-timeline consumed by `<CardHeader shrink>`. Apply to the
|
|
369
|
+
scroll-overflow ancestor (typically the `<Card>` itself OR a wrapper
|
|
370
|
+
inside it that carries `overflow-y: auto`).
|
|
371
|
+
|
|
372
|
+
`contain: layout style paint` isolates the named timeline so child
|
|
373
|
+
animations respond to THIS host's scroll only — not to portal-
|
|
374
|
+
triggered layout shifts in ancestor containers (the load-bearing
|
|
375
|
+
isolation guarantee the value.js `PaneHeader.vue:18-29` comment
|
|
376
|
+
called out and the AI.W1 spec §1.2 ratified as canon).
|
|
377
|
+
|
|
378
|
+
Disambiguated from `.scroll-fade-*` (mask-image fade gradient at
|
|
379
|
+
the container's leading/trailing edge) — `.card-scroll-host` does
|
|
380
|
+
NOT paint any visual; it only emits the timeline. */
|
|
381
|
+
.card-scroll-host {
|
|
382
|
+
contain: layout style paint;
|
|
383
|
+
scroll-timeline: --card-scroll block;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/* ── Shimmer text effects ──
|
|
387
|
+
The text-fill + background-clip composition is load-bearing visual
|
|
388
|
+
(the gold gradient itself); only the `animation:` line is wrapped in
|
|
389
|
+
the PRM no-preference bracket so PRM users see the static gradient
|
|
390
|
+
without the slide animation. */
|
|
391
|
+
.gold-shimmer {
|
|
392
|
+
background: linear-gradient(90deg, var(--color-gold-dark), var(--color-gold-light), var(--color-gold), var(--color-gold-light), var(--color-gold-dark));
|
|
393
|
+
background-size: 250% 100%;
|
|
394
|
+
background-clip: text;
|
|
395
|
+
-webkit-background-clip: text;
|
|
396
|
+
color: transparent;
|
|
397
|
+
}
|
|
398
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
399
|
+
.gold-shimmer {
|
|
400
|
+
animation: gold-shimmer-slide var(--duration-shimmer) linear infinite;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* E7c — `.text-gilt`: the brand-polish ONE-SHOT seal, a FULL to-AND-fro round
|
|
405
|
+
trip. The gold gradient sweeps across ONCE (the press), catches the light,
|
|
406
|
+
and RECEDES to the figure's own resting ink — a pressed seal, not a marquee.
|
|
407
|
+
|
|
408
|
+
The defect this corrects: `.text-gilt` formerly set `color: transparent`
|
|
409
|
+
PERMANENTLY and ran the infinite `gold-shimmer-slide` `1 forwards`, so it
|
|
410
|
+
held its gilt terminal frame forever; the consumer then RIPPED the class off
|
|
411
|
+
on a `setTimeout`. But the gold gradient lives in `background-image`, which no
|
|
412
|
+
transition covers — so the gilt SNAPPED off in one frame (the broken to-and-fro
|
|
413
|
+
the user named). The recede was never authored at the library; it lived as a
|
|
414
|
+
JS class-yank in the consumer.
|
|
415
|
+
|
|
416
|
+
The fix is library-rooted: the RESTING state is the figure's OWN ink — `.text-gilt`
|
|
417
|
+
does NOT permanently null the color; only the `gold-shimmer-pulse` animation
|
|
418
|
+
drives `color`/`-webkit-text-fill-color` transparent (revealing the gradient
|
|
419
|
+
through `background-clip:text`) and BACK, over its own `--duration-seal`
|
|
420
|
+
(≈1.5s, NOT the 5s loop token), `ease-in-out`. The final keyframe IS rest, so
|
|
421
|
+
NO `forwards` and NO class-yank: the animation owns the whole envelope and the
|
|
422
|
+
gradient never snaps in a single frame. PRM users see no pass — the figure
|
|
423
|
+
just keeps its resting ink (no gilt sweep). */
|
|
424
|
+
.text-gilt {
|
|
425
|
+
background: linear-gradient(90deg, var(--color-gold-dark), var(--color-gold-light), var(--color-gold), var(--color-gold-light), var(--color-gold-dark));
|
|
426
|
+
background-size: 250% 100%;
|
|
427
|
+
background-clip: text;
|
|
428
|
+
-webkit-background-clip: text;
|
|
429
|
+
/* resting = the figure's own ink (currentColor); the animation, not this
|
|
430
|
+
rule, nulls it to reveal the gradient — so there is nothing to yank. */
|
|
431
|
+
}
|
|
432
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
433
|
+
.text-gilt {
|
|
434
|
+
animation: gold-shimmer-pulse var(--duration-seal) var(--ease-standard) 1;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* ── Keyboard key ── */
|
|
439
|
+
.kbd {
|
|
440
|
+
display: inline-flex;
|
|
441
|
+
align-items: center;
|
|
442
|
+
justify-content: center;
|
|
443
|
+
min-height: 1.5rem;
|
|
444
|
+
min-width: 1.5rem;
|
|
445
|
+
padding: 0 0.375rem;
|
|
446
|
+
font-family: var(--font-mono);
|
|
447
|
+
font-size: var(--type-micro);
|
|
448
|
+
background: var(--muted);
|
|
449
|
+
border: 1px solid var(--border);
|
|
450
|
+
border-radius: var(--radius-sm);
|
|
451
|
+
box-shadow: 0 1px 0 1px color-mix(in srgb, var(--border) 30%, transparent);
|
|
452
|
+
line-height: 1;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/* ── Metric badge pill — compact glass label for numeric telemetry ── */
|
|
456
|
+
.metric-badge {
|
|
457
|
+
display: inline-flex;
|
|
458
|
+
align-items: center;
|
|
459
|
+
justify-content: center;
|
|
460
|
+
text-align: center;
|
|
461
|
+
/* AQ.W3 §W3.2 identity base */
|
|
462
|
+
scale: 1;
|
|
463
|
+
gap: var(--metric-badge-gap, 0.125rem);
|
|
464
|
+
max-width: var(--metric-badge-max-width, 8rem);
|
|
465
|
+
min-height: var(--metric-badge-min-height, 1.5rem);
|
|
466
|
+
padding:
|
|
467
|
+
var(--metric-badge-padding-block, 0.125rem)
|
|
468
|
+
var(--metric-badge-padding-inline, 0.5rem);
|
|
469
|
+
overflow: hidden;
|
|
470
|
+
flex-shrink: 0;
|
|
471
|
+
line-height: 1;
|
|
472
|
+
/* Canonical 5-tier glass surface: the `quiet` rung paints the badge
|
|
473
|
+
plate at the `--glass-bg-quiet` α (0.50) with `--glass-blur-quiet`.
|
|
474
|
+
Reduced-transparency / no-backdrop-filter fallbacks propagate
|
|
475
|
+
through the lazy custom-property resolution that `glass.css`
|
|
476
|
+
sets up — the badge no longer bypasses the canon.
|
|
477
|
+
(Per audit U.W0.C-c §7.2.) */
|
|
478
|
+
border: 1px solid var(--metric-badge-border, var(--glass-border-quiet));
|
|
479
|
+
border-radius: var(--metric-badge-radius, var(--radius-badge, var(--radius-pill)));
|
|
480
|
+
background: var(--metric-badge-bg, var(--glass-bg-quiet));
|
|
481
|
+
backdrop-filter: var(--metric-badge-blur, var(--glass-blur-quiet));
|
|
482
|
+
box-shadow: var(--metric-badge-shadow, 0 1px 6px color-mix(in srgb, var(--shadow-color) 8%, transparent), var(--glass-highlight));
|
|
483
|
+
transition:
|
|
484
|
+
background var(--duration-fast) var(--ease-standard),
|
|
485
|
+
border-color var(--duration-fast) var(--ease-standard),
|
|
486
|
+
box-shadow var(--duration-fast) var(--ease-standard),
|
|
487
|
+
scale var(--duration-fast) var(--ease-standard);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.metric-badge > span {
|
|
491
|
+
line-height: 1;
|
|
492
|
+
/* AW.W26 — `wrap-anywhere` (overflow-wrap: anywhere) on the value slot:
|
|
493
|
+
a long metric value breaks WITHIN the badge rather than being clipped
|
|
494
|
+
by the host `overflow: hidden`. v4.1 `wrap-anywhere` / mwg css §7.
|
|
495
|
+
Scoped to the value span, NOT a universal selector (mwg perf caveat). */
|
|
496
|
+
overflow-wrap: anywhere;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.metric-badge:hover {
|
|
500
|
+
/* Hover lifts to the `resting` rung — one step up the canonical
|
|
501
|
+
ladder (audit U.W0.C-c §7.2). */
|
|
502
|
+
border-color: var(--metric-badge-hover-border, var(--glass-border-resting));
|
|
503
|
+
background: var(--metric-badge-hover-bg, var(--glass-bg-resting));
|
|
504
|
+
box-shadow: var(--metric-badge-hover-shadow, 0 2px 10px color-mix(in srgb, var(--shadow-color) 12%, transparent), var(--glass-highlight));
|
|
505
|
+
scale: var(--metric-badge-hover-scale, 1.02);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.metric-badge:active {
|
|
509
|
+
scale: var(--metric-badge-press-scale, 0.96);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.metric-badge:focus-visible {
|
|
513
|
+
outline: none;
|
|
514
|
+
box-shadow: var(--focus-ring-shadow);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/* Phase-finish catch-light — the quieter cousin of --glass-specular.
|
|
518
|
+
Speedtest toggles `data-just-resolved="true"` on a metric pill once
|
|
519
|
+
its phase completes; the badge briefly carries a 1px catch-light
|
|
520
|
+
streak at the top edge. Reads as the pill catching club lighting at
|
|
521
|
+
the moment its result resolves. */
|
|
522
|
+
.metric-badge[data-just-resolved="true"] {
|
|
523
|
+
box-shadow:
|
|
524
|
+
inset 0 1px 0 0 rgb(255 255 255 / 0.18),
|
|
525
|
+
var(--metric-badge-shadow,
|
|
526
|
+
0 1px 6px color-mix(in srgb, var(--shadow-color) 8%, transparent),
|
|
527
|
+
var(--glass-highlight));
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/* ── Label-bearing variants ───────────────────────────────────────────
|
|
531
|
+
When the badge carries a `labelPosition`, the prose-style annotation
|
|
532
|
+
slot precedes the amount. Inline keeps the row baseline-aligned;
|
|
533
|
+
stacked breaks to a column with items flush-left. The label slot
|
|
534
|
+
carries its own tracked uppercase rules; the badge's max-width
|
|
535
|
+
relaxes so the wider register fits. */
|
|
536
|
+
.metric-badge--label-inline {
|
|
537
|
+
flex-direction: row;
|
|
538
|
+
align-items: baseline;
|
|
539
|
+
justify-content: flex-start;
|
|
540
|
+
gap: var(--metric-badge-label-gap, 0.375rem);
|
|
541
|
+
max-width: var(--metric-badge-label-max-width, none);
|
|
542
|
+
text-align: left;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.metric-badge--label-stacked {
|
|
546
|
+
flex-direction: column;
|
|
547
|
+
align-items: flex-start;
|
|
548
|
+
justify-content: center;
|
|
549
|
+
gap: var(--metric-badge-label-gap-stacked, 0.0625rem);
|
|
550
|
+
max-width: var(--metric-badge-label-max-width, none);
|
|
551
|
+
text-align: left;
|
|
552
|
+
min-height: var(--metric-badge-min-height-stacked, 2.625rem);
|
|
553
|
+
padding-block: var(--metric-badge-padding-block-stacked, 0.375rem);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/* T.W2.T2 — stacked variant pairs amount + unit on a single row (row 2)
|
|
557
|
+
below the label (row 1). The `.metric-badge__row` wrapper baseline-
|
|
558
|
+
aligns the value and unit so the quantity reads as one phrase, not as
|
|
559
|
+
three siblings split across three rows (the pre-T behaviour the
|
|
560
|
+
audit B-spec §2.1 flagged). */
|
|
561
|
+
.metric-badge--label-stacked .metric-badge__row {
|
|
562
|
+
display: inline-flex;
|
|
563
|
+
align-items: baseline;
|
|
564
|
+
gap: var(--metric-badge-row-gap-stacked, 0.25rem);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/* T.W2.T3 — MetricPill density modifiers. The pill is a thin composition
|
|
568
|
+
over MetricBadge with `labelPosition="stacked"` defaults; the density
|
|
569
|
+
knob adjusts block padding + horizontal padding so the pill reads at
|
|
570
|
+
the right register for its dock host. `spacious` is the default
|
|
571
|
+
(chassis-strip rhythm), `comfortable` keeps the tighter compact
|
|
572
|
+
register for densely-packed dock clusters. The selectors compose with
|
|
573
|
+
`.metric-badge--label-stacked` so the inline ladder remains untouched
|
|
574
|
+
— these are stacked-only refinements. */
|
|
575
|
+
.metric-pill[data-density="spacious"].metric-badge--label-stacked {
|
|
576
|
+
--metric-badge-padding-block-stacked: 0.5rem;
|
|
577
|
+
--metric-badge-padding-inline: 0.75rem;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.metric-pill[data-density="comfortable"].metric-badge--label-stacked {
|
|
581
|
+
--metric-badge-padding-block-stacked: 0.25rem;
|
|
582
|
+
--metric-badge-padding-inline: 0.5rem;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/* AS.W4 — @container style(--density) companion. A pill nested under a host
|
|
586
|
+
that sets `--density: comfortable | spacious` on a wrapping element picks
|
|
587
|
+
up the matching padding register with no `data-density` markup contract;
|
|
588
|
+
the attribute rules above stay the SOLE fallback (inv 47).
|
|
589
|
+
The inner `.metric-pill.metric-badge--label-stacked` selector (0,2,0) sits
|
|
590
|
+
just BELOW the `.metric-pill[data-density]…` attribute rules (0,3,0) — so a
|
|
591
|
+
pill carrying BOTH the attribute and a `--density` ancestor lands on the
|
|
592
|
+
attribute rule (same tokens, identical paint), while a pill with only the
|
|
593
|
+
ancestor token still resolves the right register (no competing rule sets
|
|
594
|
+
these padding custom props at higher weight in that case).
|
|
595
|
+
|
|
596
|
+
No `@supports` wrapper: unlike the sibling scroll-state recipe (which
|
|
597
|
+
probes `@supports (container-type: scroll-state)` — a probeable
|
|
598
|
+
container-type VALUE), style queries introduce no new `container-type`
|
|
599
|
+
value (every element is a style container by default), so there is no
|
|
600
|
+
clean declaration test for style-query support. This relies on
|
|
601
|
+
`@container style()`'s own graceful degradation: an engine without
|
|
602
|
+
style-query support parses the unknown at-rule as invalid, drops the
|
|
603
|
+
block, and keeps the `[data-density]` attribute base. (The earlier
|
|
604
|
+
`@supports (container-type: inline-size)` wrapper was wrong — it probed
|
|
605
|
+
SIZE-query support, a distinct feature with a distinct support timeline.) */
|
|
606
|
+
@container style(--density: spacious) {
|
|
607
|
+
.metric-pill.metric-badge--label-stacked {
|
|
608
|
+
--metric-badge-padding-block-stacked: 0.5rem;
|
|
609
|
+
--metric-badge-padding-inline: 0.75rem;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
@container style(--density: comfortable) {
|
|
613
|
+
.metric-pill.metric-badge--label-stacked {
|
|
614
|
+
--metric-badge-padding-block-stacked: 0.25rem;
|
|
615
|
+
--metric-badge-padding-inline: 0.5rem;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.metric-badge__label {
|
|
620
|
+
letter-spacing: 0.18em;
|
|
621
|
+
line-height: 1;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/* Dual-slot default visibility (R2-spec):
|
|
625
|
+
The library renders both `--full` and `--abbr` siblings whenever both
|
|
626
|
+
props are passed. Default identity is the full label; the abbreviation
|
|
627
|
+
sits hidden until consumer container-query CSS flips it. The adjacent-
|
|
628
|
+
sibling selector preserves single-slot back-compat: when a consumer
|
|
629
|
+
passes only `abbreviation` (no `--full` sibling rendered), the abbr
|
|
630
|
+
reads at default visibility. */
|
|
631
|
+
.metric-badge__label--full + .metric-badge__label--abbr {
|
|
632
|
+
display: none;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/* sm-tier label tracking — tighter than the md/lg/xl 0.18em rung so the
|
|
636
|
+
compact register reads as a tighter annotation against the smaller
|
|
637
|
+
glyph rise. R2-spec D-Rec-2 (audit-D). */
|
|
638
|
+
.metric-badge[data-size="sm"] .metric-badge__label {
|
|
639
|
+
letter-spacing: 0.1em;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/* D6.b — `MetricBadge variant="record"`: the gilded record/peak value.
|
|
643
|
+
The highest-leverage gold moment on a DATA atlas — a tiny gilt pill on the
|
|
644
|
+
single record / peak / featured figure (the all-time-high, the top
|
|
645
|
+
district). Gold marks the SUPERLATIVE, exactly one per view: a gilt pill on
|
|
646
|
+
a single figure reads as a medal — civic and earned, not decorative.
|
|
647
|
+
|
|
648
|
+
The recipe composes ON the base `.metric-badge` glass plate (it is a
|
|
649
|
+
MODIFIER, never a replacement): a 1px `--gold-rim` tooled edge + `--gold-ink`
|
|
650
|
+
text + a FAINT gold wash (≤8% alpha — never an opaque gold fill, which would
|
|
651
|
+
read as a fourth DATA category and break one-color-one-meaning). The wash
|
|
652
|
+
sits over the badge's own `--glass-bg-quiet` plate via `color-mix`, so the
|
|
653
|
+
cream glass still reads through; the gold is the leaf, not the area.
|
|
654
|
+
|
|
655
|
+
`--gold-ink` clears cream-law ② (APCA |Lc| ≥ 45) on `--card` in both arms
|
|
656
|
+
(proof:gold-ink-contrast), and the faint wash only lightens the plate it
|
|
657
|
+
sits on, so the ink stays legible. */
|
|
658
|
+
.metric-badge--record {
|
|
659
|
+
border-color: var(--gold-rim);
|
|
660
|
+
background:
|
|
661
|
+
linear-gradient(
|
|
662
|
+
0deg,
|
|
663
|
+
color-mix(in oklab, var(--gold) 8%, transparent),
|
|
664
|
+
color-mix(in oklab, var(--gold) 8%, transparent)
|
|
665
|
+
),
|
|
666
|
+
var(--metric-badge-bg, var(--glass-bg-quiet));
|
|
667
|
+
box-shadow:
|
|
668
|
+
inset 0 0 0 1px var(--gold-rim),
|
|
669
|
+
inset 0 0.5px 0 0
|
|
670
|
+
color-mix(in oklab, var(--gold-light) 30%, transparent),
|
|
671
|
+
0 1px 6px color-mix(in srgb, var(--shadow-color) 8%, transparent);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/* The record value reads in legible gold ink (the medal numeral). Scoped to
|
|
675
|
+
the amount slot so the unit/label keep their muted register — the gold is
|
|
676
|
+
on the figure, not the furniture. */
|
|
677
|
+
.metric-badge--record .metric-badge__amount {
|
|
678
|
+
color: var(--gold-ink);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
.metric-badge--record:hover {
|
|
682
|
+
/* Hover deepens the rim to full `--gold` and lifts the wash one rung —
|
|
683
|
+
the gilt edge catches a touch more light, never a fill. */
|
|
684
|
+
border-color: var(--gold);
|
|
685
|
+
box-shadow:
|
|
686
|
+
inset 0 0 0 1px var(--gold),
|
|
687
|
+
inset 0 0.5px 0 0
|
|
688
|
+
color-mix(in oklab, var(--gold-light) 40%, transparent),
|
|
689
|
+
0 2px 10px color-mix(in srgb, var(--shadow-color) 12%, transparent);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/* Q.W3 Lane A — the `.glass-dock[data-density="…"]` density rungs that
|
|
693
|
+
set `--dock-tab-h` + `--dock-label-size` were consolidated into
|
|
694
|
+
`dock.css` (their canonical dock-styles home). They formerly lived
|
|
695
|
+
here and resolved only by the index.css import order (utilities.css
|
|
696
|
+
after dock.css); the cascade-order dependency is now gone. */
|
|
697
|
+
|
|
698
|
+
/* ── Input bar — glassmorphic inline input container ──
|
|
699
|
+
Composes the canonical `floating` glass rung (--glass-bg-floating
|
|
700
|
+
≈ 80% α, --glass-blur-floating ≈ 24px) — same surface read as the
|
|
701
|
+
prior raw composition but routed through the canonical 5-tier
|
|
702
|
+
ladder so PRT / no-backdrop-filter fallbacks propagate cleanly.
|
|
703
|
+
(Per audit U.W0.C-c §7.2.) */
|
|
704
|
+
.input-bar {
|
|
705
|
+
display: flex;
|
|
706
|
+
align-items: center;
|
|
707
|
+
gap: 0.5rem;
|
|
708
|
+
border-radius: var(--radius-2xl);
|
|
709
|
+
border: 1px solid var(--glass-border-floating);
|
|
710
|
+
background: var(--glass-bg-floating);
|
|
711
|
+
backdrop-filter: var(--glass-blur-floating);
|
|
712
|
+
|
|
713
|
+
padding: 0 0.75rem;
|
|
714
|
+
height: 2.25rem;
|
|
715
|
+
max-width: var(--max-width-input);
|
|
716
|
+
width: 100%;
|
|
717
|
+
transition:
|
|
718
|
+
box-shadow var(--duration-fast) var(--ease-standard),
|
|
719
|
+
border-color var(--duration-fast) var(--ease-standard);
|
|
720
|
+
}
|
|
721
|
+
.input-bar:focus-within {
|
|
722
|
+
box-shadow: var(--focus-ring-shadow);
|
|
723
|
+
border-color: var(--border);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.input-bar-field {
|
|
727
|
+
font-family: var(--font-mono, monospace);
|
|
728
|
+
font-size: var(--type-small);
|
|
729
|
+
background: transparent;
|
|
730
|
+
border: none;
|
|
731
|
+
outline: none;
|
|
732
|
+
flex: 1;
|
|
733
|
+
min-width: 0;
|
|
734
|
+
}
|
|
735
|
+
.input-bar-field::placeholder {
|
|
736
|
+
color: color-mix(in srgb, var(--muted-foreground) 50%, transparent);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/* ── Hover lift — translate-y + shadow on hover (3 levels) ──
|
|
740
|
+
The shared transition is expressed once over the family; the three
|
|
741
|
+
:hover rules carry the per-level translate + shadow. */
|
|
742
|
+
:where(.hover-lift, .hover-lift-md, .hover-lift-lg) {
|
|
743
|
+
/* Individual-transform identity base (AQ.W3 §W3.2) — `translate: 0` at
|
|
744
|
+
rest mints the stacking context once so the hover lift does not
|
|
745
|
+
flicker it into being. */
|
|
746
|
+
translate: 0;
|
|
747
|
+
transition-property: translate, box-shadow;
|
|
748
|
+
transition-duration: var(--duration-normal);
|
|
749
|
+
transition-timing-function: var(--ease-standard);
|
|
750
|
+
}
|
|
751
|
+
.hover-lift:hover:not(:disabled):not([aria-disabled='true']) {
|
|
752
|
+
translate: 0 var(--lift-sm);
|
|
753
|
+
box-shadow: var(--shadow-md);
|
|
754
|
+
}
|
|
755
|
+
.hover-lift-md:hover:not(:disabled):not([aria-disabled='true']) {
|
|
756
|
+
translate: 0 var(--lift-md);
|
|
757
|
+
box-shadow: var(--shadow-md);
|
|
758
|
+
}
|
|
759
|
+
.hover-lift-lg:hover:not(:disabled):not([aria-disabled='true']) {
|
|
760
|
+
translate: 0 var(--lift-lg);
|
|
761
|
+
box-shadow: var(--shadow-lg);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/* ── Cartoon shadows — token-driven box-shadow + bezel border + lift ──
|
|
765
|
+
The box-shadow stack reads from the canonical
|
|
766
|
+
`--shadow-cartoon-{sm,md,lg}` rungs declared at tokens.css:304-312.
|
|
767
|
+
Those rungs use `color-mix(in srgb, var(--shadow-color) N%, transparent)`
|
|
768
|
+
so dark-mode flips the cast through `--shadow-color` automatically.
|
|
769
|
+
The 2px bezel border + translateY are the cartoon-stamp geometry
|
|
770
|
+
atop the canonical depth — kept here so a single class delivers
|
|
771
|
+
the whole cartoon read.
|
|
772
|
+
(Per audit U.W0.C-c §1.3 / Union 1 — collapses the dual-system
|
|
773
|
+
drift onto the token-driven shadow.) */
|
|
774
|
+
:where(.shadow-cartoon-sm, .shadow-cartoon-md, .shadow-cartoon-lg) {
|
|
775
|
+
border: 2px solid var(--border);
|
|
776
|
+
}
|
|
777
|
+
/* Static cartoon-stamp lift (AQ.W3 §W3.2). NOT state-driven, so the
|
|
778
|
+
identity base is not strictly required, but the `translate:` longhand
|
|
779
|
+
keeps the family uniform with the state-driven sites above. */
|
|
780
|
+
.shadow-cartoon-sm {
|
|
781
|
+
box-shadow: var(--shadow-cartoon-sm);
|
|
782
|
+
translate: 0 -1px;
|
|
783
|
+
}
|
|
784
|
+
.shadow-cartoon-md {
|
|
785
|
+
box-shadow: var(--shadow-cartoon-md);
|
|
786
|
+
translate: 0 -1px;
|
|
787
|
+
}
|
|
788
|
+
.shadow-cartoon-lg {
|
|
789
|
+
box-shadow: var(--shadow-cartoon-lg);
|
|
790
|
+
translate: 0 -2px;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/* ── Divider utilities ── */
|
|
794
|
+
.divider-h {
|
|
795
|
+
height: 2px;
|
|
796
|
+
background: linear-gradient(
|
|
797
|
+
to right,
|
|
798
|
+
transparent 1%,
|
|
799
|
+
var(--border) 8%,
|
|
800
|
+
var(--border) 92%,
|
|
801
|
+
transparent 99%
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
.divider-h-tapered {
|
|
806
|
+
height: 2px;
|
|
807
|
+
background: linear-gradient(
|
|
808
|
+
to right,
|
|
809
|
+
transparent 3%,
|
|
810
|
+
var(--border) 15%,
|
|
811
|
+
var(--border) 85%,
|
|
812
|
+
transparent 97%
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/* ── Hover scale (O.W6 Lane C) ──
|
|
819
|
+
Canonical hover-transform utility consuming the `--scale-hover` token
|
|
820
|
+
(tokens.css §11). Promoted from consumer-side `hover:scale-105`
|
|
821
|
+
recipe — 13 sites in keyframes.js demo + 9 sites in words/frontend
|
|
822
|
+
per O11/d L3 + O11/a I4. Single-token recipe (no ladder); the dual
|
|
823
|
+
`--scale-press-{xs..lg}` ladder discussion is the press-axis cohort
|
|
824
|
+
(O-N-7), not hover. Consumers reach the utility via
|
|
825
|
+
`class="scale-on-hover"`; transition + easing bind to the canonical
|
|
826
|
+
`--duration-fast` + `--ease-standard` tokens. */
|
|
827
|
+
@utility scale-on-hover {
|
|
828
|
+
/* Individual-transform identity base (AQ.W3 §W3.2). Transition the `scale`
|
|
829
|
+
longhand (not `transform`) so the migrated hover scale animates.
|
|
830
|
+
AX.W52 §6 doctrine — the `scale` transform leg rides `--spring-smooth`
|
|
831
|
+
(the settle register), NOT the bezier `--ease-standard` (a transform on a
|
|
832
|
+
bezier reads mechanical; springs read alive on transform). */
|
|
833
|
+
scale: 1;
|
|
834
|
+
transition: scale var(--duration-fast) var(--spring-smooth);
|
|
835
|
+
|
|
836
|
+
&:hover {
|
|
837
|
+
scale: var(--scale-hover);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/* ── Twin-line divider — the chassis catch-light / under-shadow groove ───────
|
|
842
|
+
AW.W15 DRY extraction. The twin-line idiom (a 1px white catch-light over a
|
|
843
|
+
1px black under-shadow reading as one engraved hairline groove) is the
|
|
844
|
+
chassis-divider signature. The exact light/dark α pair was DUPLICATED in
|
|
845
|
+
`instrument-chassis.css` (`.bezel-line.top`/`.bottom`) and `instrument-rail.css`
|
|
846
|
+
(the sibling `::before`/`::after`). This utility is the SINGLE source of that
|
|
847
|
+
α pair: it publishes `--twin-line-catch` (the top catch-light) and
|
|
848
|
+
`--twin-line-shadow` (the bottom under-shadow) as cascade tokens, dark-adaptive
|
|
849
|
+
by construction (the `.dark &` arm re-resolves them). Both consumers read the
|
|
850
|
+
tokens; a retint touches one rule. Real DRY, ≥2 consumers. */
|
|
851
|
+
@utility twin-line-divider {
|
|
852
|
+
--twin-line-catch: rgb(255 255 255 / 0.10);
|
|
853
|
+
--twin-line-shadow: rgb(0 0 0 / 0.12);
|
|
854
|
+
|
|
855
|
+
:where(.dark) & {
|
|
856
|
+
--twin-line-catch: rgb(255 255 255 / 0.06);
|
|
857
|
+
--twin-line-shadow: rgb(0 0 0 / 0.18);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/* ── Control transition recipe (AU.W8b.4) ───────────────────────────────
|
|
862
|
+
Shared compound transition for interactive controls that animate their
|
|
863
|
+
surface (background-color/color/box-shadow/border-color) on the four-state
|
|
864
|
+
contract. Replaces the per-site
|
|
865
|
+
`transition-[background-color,color,box-shadow,border-color] duration-[var(--duration-fast)] ease-[var(--ease-standard)]`
|
|
866
|
+
arbitrary wraps at ≥2 sites (TabsTrigger, SelectTrigger, Toggle `card`
|
|
867
|
+
variant). Binds the canonical --duration-fast + --ease-standard tokens. */
|
|
868
|
+
@utility transition-control {
|
|
869
|
+
transition-property: background-color, color, box-shadow, border-color;
|
|
870
|
+
transition-duration: var(--duration-fast);
|
|
871
|
+
transition-timing-function: var(--ease-standard);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/* ── Collapse transition recipe (AU.W8b.4) ──────────────────────────────
|
|
875
|
+
Shared height+opacity transition for reka-ui collapse surfaces (Accordion
|
|
876
|
+
content, Collapsible content). Replaces the per-site `transition-[height,opacity]`
|
|
877
|
+
wrap at ≥2 sites. The duration/easing rides the data-state
|
|
878
|
+
`animate-accordion-*`/`animate-collapsible-*` keyframes (this only declares
|
|
879
|
+
WHICH properties transition; the keyframes own the timing). */
|
|
880
|
+
@utility transition-collapse {
|
|
881
|
+
transition-property: height, opacity;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
/* ── Sheet/drawer slide-out animation grammar ──
|
|
885
|
+
Mirror of `.popover-animate` for sheet/drawer panels. Pairs with
|
|
886
|
+
`slide-in-from-{top,right,bottom,left}-N` to compose side-keyed entries.
|
|
887
|
+
The open path uses --duration-panel (slow, deliberate); the close path
|
|
888
|
+
uses --duration-fast so dismiss feels snappy. R5 vocab.γ gap row 6;
|
|
889
|
+
consumed by W2.A sheet/drawer migration. */
|
|
890
|
+
@utility sheet-animate {
|
|
891
|
+
@apply data-[state=open]:animate-in data-[state=closed]:animate-out
|
|
892
|
+
data-[state=open]:fade-in data-[state=closed]:fade-out
|
|
893
|
+
data-[state=open]:duration-[var(--duration-panel)]
|
|
894
|
+
data-[state=closed]:duration-[var(--duration-fast)];
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/* ── Audacious primary CTA recipe ────────────────────────────────────────
|
|
898
|
+
Composes three at-rest marks (top-highlight via --glass-highlight, bottom
|
|
899
|
+
under-shadow via --border-hairline, baseline glass) with three disco accents
|
|
900
|
+
on hover (specular swap via --glass-specular, sparkle sweep via the ::after
|
|
901
|
+
star, disco-grain via --paper-clean-texture blended with a --primary radial).
|
|
902
|
+
Disco fires on hover only — at rest the button reads as a restrained glass
|
|
903
|
+
surface. Pair with `bg-primary text-primary-foreground`; the utility owns
|
|
904
|
+
the texture/highlight/sparkle composition only. */
|
|
905
|
+
@utility btn-audacious {
|
|
906
|
+
position: relative;
|
|
907
|
+
isolation: isolate;
|
|
908
|
+
overflow: hidden;
|
|
909
|
+
border: 1px solid var(--glass-border-quiet);
|
|
910
|
+
box-shadow:
|
|
911
|
+
var(--border-hairline),
|
|
912
|
+
var(--glass-highlight);
|
|
913
|
+
transition:
|
|
914
|
+
background var(--duration-fast) var(--ease-standard),
|
|
915
|
+
color var(--duration-fast) var(--ease-standard),
|
|
916
|
+
transform var(--duration-fast) var(--ease-standard),
|
|
917
|
+
box-shadow var(--duration-fast) var(--ease-standard),
|
|
918
|
+
--ripple-radius var(--motion-duration-ripple, 340ms) var(--ease-out-expo);
|
|
919
|
+
|
|
920
|
+
/* Hover composition: paper-grain texture overlaid on a soft
|
|
921
|
+
--primary radial, with the specular catch-light replacing the
|
|
922
|
+
resting hairline highlight. The radial uses --primary at 18%
|
|
923
|
+
— same alpha rung the dock primary tier consumes for phase
|
|
924
|
+
tinting, just bound to the canonical CTA color instead. */
|
|
925
|
+
&:hover:not(:disabled) {
|
|
926
|
+
background-image:
|
|
927
|
+
var(--paper-clean-texture),
|
|
928
|
+
radial-gradient(
|
|
929
|
+
ellipse at 30% 30%,
|
|
930
|
+
color-mix(in srgb, var(--primary) 18%, transparent),
|
|
931
|
+
transparent 70%
|
|
932
|
+
);
|
|
933
|
+
background-blend-mode: overlay, normal;
|
|
934
|
+
background-size: var(--paper-texture-size), auto;
|
|
935
|
+
box-shadow:
|
|
936
|
+
var(--glass-specular),
|
|
937
|
+
0 0.5px 0 0 rgb(0 0 0 / 0.06);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/* AI.W4-M.2 (Q5 — G-AI-D27 ratified) — press-ripple via a typed
|
|
941
|
+
`--ripple-radius` length custom property. The `::before`
|
|
942
|
+
element paints a radial gradient whose stop radius equals the
|
|
943
|
+
typed length; the `:active` rule sets the length to the cap
|
|
944
|
+
(`--ripple-radius-max`, 12rem), and the host's transition
|
|
945
|
+
(above) interpolates the radius via the @property registration
|
|
946
|
+
(tokens.css §18). Release returns the length to its 0px initial
|
|
947
|
+
value and the ripple retires. The ripple paints at z-index 0
|
|
948
|
+
(above the host's background, below the slot content which sits
|
|
949
|
+
in the default 0 stacking context but with text inheriting the
|
|
950
|
+
slot's foreground colour). */
|
|
951
|
+
&::before {
|
|
952
|
+
content: "";
|
|
953
|
+
position: absolute;
|
|
954
|
+
inset: 0;
|
|
955
|
+
pointer-events: none;
|
|
956
|
+
z-index: 0;
|
|
957
|
+
border-radius: inherit;
|
|
958
|
+
background: radial-gradient(
|
|
959
|
+
circle at center,
|
|
960
|
+
color-mix(in srgb, var(--primary-foreground, hsl(0 0% 100%)) 35%, transparent) 0,
|
|
961
|
+
color-mix(in srgb, var(--primary-foreground, hsl(0 0% 100%)) 18%, transparent)
|
|
962
|
+
calc(var(--ripple-radius, 0px) * 0.55),
|
|
963
|
+
transparent var(--ripple-radius, 0px)
|
|
964
|
+
);
|
|
965
|
+
transition: inherit;
|
|
966
|
+
will-change: background;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
&:active:not(:disabled) {
|
|
970
|
+
--ripple-radius: var(--ripple-radius-max, 12rem);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/* D6.b — the disco-star EXCISED. The `::after { content:"✦" }` glyph and
|
|
974
|
+
its `&:hover::after { animation: sparkle-sweep … }` trigger are gone (the
|
|
975
|
+
user's named removal: "remove the disco star from all glass-ui elements").
|
|
976
|
+
A primary action now announces itself by SETTLING into a richer surface,
|
|
977
|
+
not by emitting a sparkle: the whole hover affirmation rides the existing
|
|
978
|
+
`&:hover` specular catch-light box-shadow (`--glass-specular`, above) —
|
|
979
|
+
honest physics, not casino-skeuomorphism. Gold CTAs add the
|
|
980
|
+
`btn-gold-bg-sweep` metal-catch (a metal catching light is honest; a star
|
|
981
|
+
is not). One garish gesture out, zero garish gestures in. The
|
|
982
|
+
`sparkle-sweep` keyframe is TOMBSTONED in animations.css (empty body,
|
|
983
|
+
full delete in 3.11); `--duration-sparkle` is `@deprecated` in tokens.css. */
|
|
984
|
+
|
|
985
|
+
/* PRM bracket — the press-ripple retires; the press still reads
|
|
986
|
+
via the audacious recipe's transform/scale beat (the ripple is
|
|
987
|
+
affirmation atop affirmation). */
|
|
988
|
+
@media (prefers-reduced-motion: reduce) {
|
|
989
|
+
transition:
|
|
990
|
+
background var(--duration-fast) var(--ease-standard),
|
|
991
|
+
color var(--duration-fast) var(--ease-standard),
|
|
992
|
+
transform var(--duration-fast) var(--ease-standard),
|
|
993
|
+
box-shadow var(--duration-fast) var(--ease-standard);
|
|
994
|
+
|
|
995
|
+
&:active:not(:disabled) {
|
|
996
|
+
--ripple-radius: 0px;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/* ── Gold CTA shimmer modifier — AN.R0 D9 ───────────────────────────────
|
|
1002
|
+
`btn-audacious-gold` extends `btn-audacious` with a gold sweep shimmer
|
|
1003
|
+
on hover. Pair as `btn-audacious btn-audacious-gold` (or use the Button
|
|
1004
|
+
`variant="gold-audacious"` which composes both). The hover shimmer is a
|
|
1005
|
+
translucent linear-gradient sweep (low alpha — tasteful, not garish)
|
|
1006
|
+
that slides left-to-right via the existing `gold-shimmer-slide` keyframe
|
|
1007
|
+
(animations.css). The gold gradient uses the canonical `--color-gold-*`
|
|
1008
|
+
token triad (tokens.css §13).
|
|
1009
|
+
|
|
1010
|
+
Design register:
|
|
1011
|
+
• PRM no-preference: the gold sweep IS the gold-CTA affirmation — a metal
|
|
1012
|
+
catching light is honest physics (the disco-star is gone, D6.b).
|
|
1013
|
+
• PRM reduce: static gold tint shows on hover; no animation.
|
|
1014
|
+
• The gold shimmer is a background-image layer ABOVE the existing
|
|
1015
|
+
btn-audacious paper-grain + primary-radial layers, blended with
|
|
1016
|
+
`overlay` so the glass substrate reads through at partial opacity.
|
|
1017
|
+
• The `::before` ripple from btn-audacious is unchanged; the `::after`
|
|
1018
|
+
sparkle is EXCISED (D6.b) — this modifier only retunes the hover layers.
|
|
1019
|
+
• Consumer applies: `<Button variant="gold-audacious">Next →</Button>` */
|
|
1020
|
+
@utility btn-audacious-gold {
|
|
1021
|
+
/* At rest: a faint warm gold tint over the glass surface reads as "warm
|
|
1022
|
+
glass" without announcing the hover state prematurely. */
|
|
1023
|
+
background-image:
|
|
1024
|
+
linear-gradient(
|
|
1025
|
+
135deg,
|
|
1026
|
+
color-mix(in srgb, var(--color-gold) 8%, transparent),
|
|
1027
|
+
color-mix(in srgb, var(--color-gold-light) 5%, transparent) 50%,
|
|
1028
|
+
color-mix(in srgb, var(--color-gold) 8%, transparent)
|
|
1029
|
+
);
|
|
1030
|
+
|
|
1031
|
+
/* Hover: deepen the plate to a SATURATED gold the white label clears (AW.W13
|
|
1032
|
+
names a "saturated gold backplate"; the D1 fix). The bottom layer is the
|
|
1033
|
+
OPAQUE fixed deep gold (`--color-gold-deep`, mode-INVARIANT) — a solid plate
|
|
1034
|
+
at ~6.5:1 (light) / ~5.5:1 (dark) against white, well below the ~0.18
|
|
1035
|
+
luminance floor white text needs for ≥4.5:1 (was a translucent 22-30% wash
|
|
1036
|
+
over near-cream glass → 1.29:1 white-on-pale-gold). The plate is FIXED-dark
|
|
1037
|
+
in both modes — deepening toward the mode-flipping `--foreground` brightened
|
|
1038
|
+
the dark plate to a cream-gold; `--color-gold-deep` does not flip. The
|
|
1039
|
+
shimmer sweep rides ABOVE as a LOW-alpha gold gradient (normal blend, ≤18% —
|
|
1040
|
+
it textures the plate without brightening it past the floor), and the
|
|
1041
|
+
paper-grain overlays for the disco facet. */
|
|
1042
|
+
&:hover:not(:disabled) {
|
|
1043
|
+
background-image:
|
|
1044
|
+
var(--paper-clean-texture),
|
|
1045
|
+
linear-gradient(
|
|
1046
|
+
90deg,
|
|
1047
|
+
transparent 0%,
|
|
1048
|
+
color-mix(in srgb, var(--color-gold) 14%, transparent) 25%,
|
|
1049
|
+
color-mix(in srgb, var(--color-gold) 18%, transparent) 50%,
|
|
1050
|
+
color-mix(in srgb, var(--color-gold) 14%, transparent) 75%,
|
|
1051
|
+
transparent 100%
|
|
1052
|
+
),
|
|
1053
|
+
linear-gradient(
|
|
1054
|
+
135deg,
|
|
1055
|
+
var(--color-gold-deep),
|
|
1056
|
+
color-mix(in srgb, var(--color-gold-deep), var(--color-gold-dark) 18%) 50%,
|
|
1057
|
+
var(--color-gold-deep)
|
|
1058
|
+
);
|
|
1059
|
+
background-blend-mode: overlay, normal, normal;
|
|
1060
|
+
background-size: var(--paper-texture-size), 250% 100%, 100% 100%;
|
|
1061
|
+
background-position: 0 0, 100% 0, 0 0;
|
|
1062
|
+
box-shadow:
|
|
1063
|
+
var(--glass-specular),
|
|
1064
|
+
0 0 0 1px color-mix(in srgb, var(--color-gold-dark) 40%, transparent),
|
|
1065
|
+
0 0.5px 0 0 rgb(0 0 0 / 0.06);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1069
|
+
&:hover:not(:disabled) {
|
|
1070
|
+
animation: btn-gold-bg-sweep var(--duration-shimmer) linear infinite;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/* Background-position sweep for the gold CTA hover shimmer.
|
|
1076
|
+
Distinct from `gold-shimmer-slide` (which animates background-position for
|
|
1077
|
+
a text-clip gradient). This variant sweeps the `background-position` of the
|
|
1078
|
+
250% wide gradient across the button's bounding box. */
|
|
1079
|
+
@keyframes btn-gold-bg-sweep {
|
|
1080
|
+
0% { background-position: 0 0, 100% 0, 0 0; }
|
|
1081
|
+
100% { background-position: 0 0, -100% 0, 0 0; }
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/* ── Table density utilities — AN.R0 ─────────────────────────────────────
|
|
1085
|
+
`table-cell` and `table-head` replace the former Tailwind `p-4` / `h-12`
|
|
1086
|
+
literals in TableCell.vue / TableHead.vue. Both read `--table-density`
|
|
1087
|
+
(tokens.css §19) so a single CSS-var assignment on a wrapping element
|
|
1088
|
+
scales all row padding + header height simultaneously. Default density=1
|
|
1089
|
+
resolves to the identical geometry as the previous Tailwind literals. */
|
|
1090
|
+
@utility table-cell {
|
|
1091
|
+
padding-inline: var(--table-cell-px, 1rem);
|
|
1092
|
+
padding-block: var(--table-cell-py, 1rem);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
@utility table-head {
|
|
1096
|
+
height: var(--table-head-h, 3rem);
|
|
1097
|
+
padding-inline: var(--table-head-px, 1rem);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
/* ── Rainbow gradient backgrounds ───────────────────────────────────────
|
|
1101
|
+
`.rainbow-vivid` + `.rainbow-pastel` paint a left-to-right 7-stop spectrum
|
|
1102
|
+
from the canonical `--rainbow-*` / `--rainbow-pastel-*` token families
|
|
1103
|
+
(tokens.css §14). Consumed by keyframes.js's `AnimationMenuBar.vue` round
|
|
1104
|
+
play button — `rainbow-vivid` (playing) / `rainbow-pastel` (idle). */
|
|
1105
|
+
@utility rainbow-vivid {
|
|
1106
|
+
background: linear-gradient(
|
|
1107
|
+
to right,
|
|
1108
|
+
var(--rainbow-red),
|
|
1109
|
+
var(--rainbow-orange),
|
|
1110
|
+
var(--rainbow-yellow),
|
|
1111
|
+
var(--rainbow-green),
|
|
1112
|
+
var(--rainbow-blue),
|
|
1113
|
+
var(--rainbow-indigo),
|
|
1114
|
+
var(--rainbow-violet)
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
@utility rainbow-pastel {
|
|
1119
|
+
background: linear-gradient(
|
|
1120
|
+
to right,
|
|
1121
|
+
var(--rainbow-pastel-red),
|
|
1122
|
+
var(--rainbow-pastel-orange),
|
|
1123
|
+
var(--rainbow-pastel-yellow),
|
|
1124
|
+
var(--rainbow-pastel-green),
|
|
1125
|
+
var(--rainbow-pastel-blue),
|
|
1126
|
+
var(--rainbow-pastel-indigo),
|
|
1127
|
+
var(--rainbow-pastel-violet)
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/* ── Text-highlight paint (CSS Custom Highlight API) ─────────────────────
|
|
1132
|
+
The default `::highlight()` paint for the `useTextHighlight` composable
|
|
1133
|
+
(composables/motion; the `/motion-core` subpath, AX.W37). A named `Highlight`
|
|
1134
|
+
registered in `CSS.highlights`
|
|
1135
|
+
under one of these keys paints its `Range` set without a `<mark>` wrapper.
|
|
1136
|
+
Token-bridged: the search mark reads the canonical pastel-yellow rainbow
|
|
1137
|
+
token at a soft alpha; the equation/variable mark reads `--primary`. Both
|
|
1138
|
+
axes are overridable via the `--text-highlight-*` custom properties. */
|
|
1139
|
+
::highlight(glass-search-mark) {
|
|
1140
|
+
background-color: var(
|
|
1141
|
+
--text-highlight-search-bg,
|
|
1142
|
+
color-mix(in srgb, var(--rainbow-pastel-yellow) 55%, transparent)
|
|
1143
|
+
);
|
|
1144
|
+
color: var(--text-highlight-search-fg, inherit);
|
|
1145
|
+
}
|
|
1146
|
+
::highlight(glass-mark) {
|
|
1147
|
+
background-color: var(
|
|
1148
|
+
--text-highlight-bg,
|
|
1149
|
+
color-mix(in srgb, var(--primary) 18%, transparent)
|
|
1150
|
+
);
|
|
1151
|
+
color: var(--text-highlight-fg, inherit);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/* ── Interactive-button four-state recipe ────────────────────────────────
|
|
1155
|
+
`.btn-interactive` carries the canonical "Button-with-spring-hover"
|
|
1156
|
+
four-state contract — transition + hover-scale + active-press + disabled-
|
|
1157
|
+
opacity + focus-ring fade-in. The Button primitive's variants emit the four
|
|
1158
|
+
states except `hover:scale-*`; this is the canonical opt-in that adds the
|
|
1159
|
+
spring-hover lift. Consumed by keyframes.js at 7 sites.
|
|
1160
|
+
|
|
1161
|
+
AX.W52 §6 — the hover lift is the restrained `--scale-hover-btn` (1.05, NOT
|
|
1162
|
+
the emphatic 1.08 `--scale-hover`) routed through `--spring-smooth` — the ONE
|
|
1163
|
+
button scale register the §6 doctrine records and the gate asserts across
|
|
1164
|
+
`.btn-pill`/`.tap-squish`/`.glass-btn`/`btn-interactive`. The surface legs
|
|
1165
|
+
stay on the bezier `--ease-standard` (a colour cross-fade reads as a wobble
|
|
1166
|
+
on a spring); only the `scale` transform leg springs. */
|
|
1167
|
+
@utility btn-interactive {
|
|
1168
|
+
/* AQ.W3 §W3.2 identity base */
|
|
1169
|
+
scale: 1;
|
|
1170
|
+
transition:
|
|
1171
|
+
background-color var(--duration-fast) var(--ease-standard),
|
|
1172
|
+
color var(--duration-fast) var(--ease-standard),
|
|
1173
|
+
scale var(--duration-fast) var(--spring-smooth),
|
|
1174
|
+
opacity var(--duration-fast) var(--ease-standard);
|
|
1175
|
+
|
|
1176
|
+
&:hover {
|
|
1177
|
+
scale: var(--scale-hover-btn);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
&:active {
|
|
1181
|
+
scale: var(--scale-press);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
&:disabled {
|
|
1185
|
+
opacity: var(--opacity-disabled);
|
|
1186
|
+
pointer-events: none;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
&:focus-visible {
|
|
1190
|
+
box-shadow: var(--focus-ring-shadow);
|
|
1191
|
+
outline: none;
|
|
1192
|
+
/* Fade in; base state has no box-shadow transition → instant
|
|
1193
|
+
removal. The `scale` leg keeps the `--spring-smooth` register. */
|
|
1194
|
+
transition:
|
|
1195
|
+
background-color var(--duration-fast) var(--ease-standard),
|
|
1196
|
+
color var(--duration-fast) var(--ease-standard),
|
|
1197
|
+
scale var(--duration-fast) var(--spring-smooth),
|
|
1198
|
+
opacity var(--duration-fast) var(--ease-standard),
|
|
1199
|
+
box-shadow var(--duration-fast) var(--ease-standard);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
/* ── Reduced motion: nuanced — preserve opacity, remove spatial motion ── */
|
|
1204
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1205
|
+
*:not([data-allow-motion]) {
|
|
1206
|
+
animation-duration: 0.01ms !important;
|
|
1207
|
+
animation-iteration-count: 1 !important;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
/* Keep opacity transitions (safe, non-spatial) but shorten */
|
|
1211
|
+
*:not([data-allow-motion]) {
|
|
1212
|
+
transition-duration: 0.1s !important;
|
|
1213
|
+
transition-property: opacity, color, background-color, border-color, box-shadow !important;
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
/* ── No-transition class (for theme switches, etc.) ── */
|
|
1218
|
+
html.no-transition,
|
|
1219
|
+
html.no-transition *,
|
|
1220
|
+
html.no-transition *::before,
|
|
1221
|
+
html.no-transition *::after {
|
|
1222
|
+
transition-duration: 0s !important;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
/* ── Forced-colors focus + silhouette rung (AQ.W2 §5 · generalized AX.W36) ────
|
|
1226
|
+
Windows High Contrast / forced-colors mode STRIPS `box-shadow` — so the
|
|
1227
|
+
glass focus rings (box-shadow + `outline: none`) vanish, leaving keyboard
|
|
1228
|
+
users with no visible focus. Restore a real `outline` keyed to the system
|
|
1229
|
+
`Highlight` color (outline survives forced-colors; box-shadow does not). The
|
|
1230
|
+
box-shadow ring stays the primary in normal mode (it carries the glass
|
|
1231
|
+
aesthetic) — these two are mutually exclusive by media query, NOT a parallel
|
|
1232
|
+
path.
|
|
1233
|
+
|
|
1234
|
+
AX.W36 — the focus-survival selector set is GENERALIZED to the broader
|
|
1235
|
+
interactive surface: the dock CONTROLS (`.dock-icon-button` / `.dock-tab-button`
|
|
1236
|
+
/ `.dock-select-trigger` / `.dock-dropdown-trigger`) + the pill button base
|
|
1237
|
+
(`.btn-pill`) all carry a box-shadow focus ring (dock.css / utilities.css) that
|
|
1238
|
+
vanishes under WHC just like the original five. This is the SAME `Highlight`
|
|
1239
|
+
outline rung extended to cover them — ONE block, ONE path (the glass-language
|
|
1240
|
+
structure-survival skin for the tier panes / StatusDot / floating surfaces lives
|
|
1241
|
+
co-located with the ladder it skins, in glass.css).
|
|
1242
|
+
|
|
1243
|
+
`forced-color-adjust: none` is deliberately NOT used — glass-ui's glassmorphic
|
|
1244
|
+
chrome is decorative and yields to the user's forced palette (css forced-colors
|
|
1245
|
+
guidance). Baseline Widely Available; this IS the fallback mechanism (it only
|
|
1246
|
+
activates under forced-colors), so no nested guard. */
|
|
1247
|
+
@media (forced-colors: active) {
|
|
1248
|
+
.focus-ring:focus-visible,
|
|
1249
|
+
.glass-btn:focus-visible,
|
|
1250
|
+
.interactive-item:focus-visible,
|
|
1251
|
+
.btn-pill:focus-visible,
|
|
1252
|
+
.dock-icon-button:focus-visible,
|
|
1253
|
+
.dock-tab-button:focus-visible,
|
|
1254
|
+
.dock-select-trigger:focus-visible,
|
|
1255
|
+
.dock-dropdown-trigger:focus-visible,
|
|
1256
|
+
.input-pill:focus,
|
|
1257
|
+
.input-pill:focus-visible {
|
|
1258
|
+
outline: 2px solid Highlight;
|
|
1259
|
+
outline-offset: 2px;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/* Structural edges borne ONLY by box-shadow (the hairline catch-light + the
|
|
1263
|
+
dock SHELL pill edge) also vanish — restore a real border so the silhouette
|
|
1264
|
+
survives. CanvasText is the system text/border color. The `.glass-dock`
|
|
1265
|
+
shell is OUT of the glass-material group BY DESIGN (it hand-rolls a parallel
|
|
1266
|
+
surface — see glass.css), so it keeps its WHC edge HERE; the five-rung ladder
|
|
1267
|
+
+ `.glass-card` get their WHC border from the co-located glass-language skin
|
|
1268
|
+
in glass.css (one source per surface family, no double-declaration). */
|
|
1269
|
+
.hairline-accent,
|
|
1270
|
+
.glass-dock {
|
|
1271
|
+
border: 1px solid CanvasText;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
/* ── Coarse-pointer touch-target floor (AQ.W3 §7) ───────────────────────────
|
|
1276
|
+
General-surface companion to the dock-scoped R0G-6 floor (dock.css). On
|
|
1277
|
+
coarse pointers the small icon-button/trigger surfaces OUTSIDE the dock lift
|
|
1278
|
+
to the WCAG 2.5.5 (44px) minimum — ONE amendment retiring the per-consumer
|
|
1279
|
+
`min-h-[44px]` one-liners. Token-driven (`--touch-target`); fine-pointer
|
|
1280
|
+
byte-identical. Targets `Button size="icon"` (via the `data-size="icon"`
|
|
1281
|
+
attr Button.vue reflects), ExpandableContainer + SegmentedTabs (responsive
|
|
1282
|
+
mobile Select) triggers.
|
|
1283
|
+
`min-block/inline-size` only RAISES — a larger explicit size is never lost. */
|
|
1284
|
+
@media (pointer: coarse) {
|
|
1285
|
+
[data-size="icon"],
|
|
1286
|
+
.expandable-container__trigger,
|
|
1287
|
+
.segmented-tabs__trigger {
|
|
1288
|
+
min-block-size: var(--touch-target, 2.75rem);
|
|
1289
|
+
min-inline-size: var(--touch-target, 2.75rem);
|
|
1290
|
+
}
|
|
1291
|
+
}
|