@mkbabb/glass-ui 3.5.1 → 3.6.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/{DataTable-DyKc_26a.js → DataTable-BGVXx0Ww.js} +1 -1
- package/dist/{NumberFieldContent-jO-zGOHf.js → NumberFieldContent-Bhhmk95p.js} +1 -1
- package/dist/animated-digit.js +1 -1
- package/dist/api/index.d.ts +2 -1
- package/dist/api.js +1 -1
- package/dist/aurora.js +259 -81
- package/dist/{button-i8Ek5vvg.js → button-BcMzVrqs.js} +2 -2
- package/dist/button.js +1 -1
- package/dist/carousel.js +1 -1
- package/dist/color.js +1 -1
- package/dist/components/custom/aurora/composables/color.d.ts +35 -2
- package/dist/components/custom/aurora/composables/glSetup.d.ts +1 -1
- package/dist/components/custom/aurora/composables/uniformBridge.d.ts +6 -0
- package/dist/components/custom/aurora/constants/presets.d.ts +9 -0
- package/dist/components/custom/aurora/constants/shaders/composition.glsl.d.ts +1 -1
- package/dist/components/custom/aurora/index.d.ts +3 -2
- package/dist/components/custom/constellation/Constellation.vue.d.ts +45 -0
- package/dist/components/custom/constellation/constellationField.d.ts +85 -0
- package/dist/components/custom/constellation/index.d.ts +3 -0
- package/dist/components/custom/dock/GlassDock.vue.d.ts +34 -3
- package/dist/composables/glass/canvas2d/index.d.ts +2 -0
- package/dist/composables/glass/canvas2d/useCanvas2D.d.ts +48 -0
- package/dist/composables/glass/index.d.ts +2 -0
- package/dist/confirm-dialog.js +1 -1
- package/dist/constellation.d.ts +1 -0
- package/dist/constellation.js +182 -0
- package/dist/controls.js +1 -1
- package/dist/dark.js +1 -1
- package/dist/data-table.js +1 -1
- package/dist/dock.js +121 -102
- package/dist/expandable-container.js +1 -1
- package/dist/forms.js +1 -1
- package/dist/glass-ui.css +1 -1
- package/dist/glass-ui.js +79 -78
- package/dist/goo-blob.js +3 -3
- package/dist/icon-tooltip.js +1 -1
- package/dist/keyboard.js +1 -1
- package/dist/labeled-field.js +2 -2
- package/dist/motion-core.js +3 -3
- package/dist/motion.js +2 -2
- package/dist/number-field.js +1 -1
- package/dist/responsive-tabs.js +1 -1
- package/dist/search.js +1 -1
- package/dist/sortable-list.js +1 -1
- package/dist/styles/dock.css +185 -1
- package/dist/styles/glass-refract.css +60 -0
- package/dist/styles/glass-specular-track.css +43 -130
- package/dist/styles/glass.css +243 -27
- package/dist/styles/index.css +1 -0
- package/dist/styles/theme.css +6 -0
- package/dist/styles/tokens.css +33 -0
- package/dist/tabs.js +2 -2
- package/dist/{useAnimatedNumber-BF6r64lA.js → useAnimatedNumber-DfVgP4te.js} +1 -1
- package/dist/useCanvas2D-BeNJqqIS.js +95 -0
- package/dist/watercolor-dot.js +1 -1
- package/package.json +21 -1
- package/src/styles/dock.css +185 -1
- package/src/styles/glass-refract.css +60 -0
- package/src/styles/glass-specular-track.css +43 -130
- package/src/styles/glass.css +243 -27
- package/src/styles/index.css +1 -0
- package/src/styles/theme.css +6 -0
- package/src/styles/tokens.css +33 -0
- /package/dist/{IconTooltip-BTyYn4mr.js → IconTooltip-DZlCkU-0.js} +0 -0
- /package/dist/{Input-CU9CNKmo.js → Input-CRJ2owjN.js} +0 -0
- /package/dist/{UnderlineTabs-Cq_AD03t.js → UnderlineTabs-CuIK16JI.js} +0 -0
- /package/dist/{color-rkK4RMx2.js → color-DS-iLRVM.js} +0 -0
- /package/dist/{compile-DVgAxagk.js → compile-C62K_FpC.js} +0 -0
- /package/dist/{constants-DsCdlK9I.js → constants-C8NMonuo.js} +0 -0
- /package/dist/{presets-1OhFpaIC.js → presets-Dyv9Zl7W.js} +0 -0
- /package/dist/{prng-Bz_1Tydc.js → prng-CjVgzjQT.js} +0 -0
- /package/dist/{useGlobalDark-CWiaCoEw.js → useGlobalDark-D7R9OypB.js} +0 -0
- /package/dist/{useIntersectionPause-DAdVPVp4.js → useIntersectionPause-DsQYGc8V.js} +0 -0
- /package/dist/{useKeyboardShortcuts-BQfnAHHW.js → useKeyboardShortcuts-i8tHd5WO.js} +0 -0
- /package/dist/{useSortable-DnyGXKKY.js → useSortable-muI29agz.js} +0 -0
- /package/dist/{useViewTransition-CJnqg6wl.js → useViewTransition-DI0Rs2rV.js} +0 -0
package/dist/styles/dock.css
CHANGED
|
@@ -60,6 +60,19 @@
|
|
|
60
60
|
--dock-wrap-max-width: calc(100vw - var(--dock-viewport-inline-gutter, 1rem));
|
|
61
61
|
--dock-separator-height: calc(var(--dock-h, var(--size-icon-btn)) * 0.5);
|
|
62
62
|
--dock-collapsed-hover-scale: var(--scale-hover-dock);
|
|
63
|
+
/* AW.W3 — the spring-keyed child stagger knobs. `--dock-stagger-window-size`
|
|
64
|
+
is the fraction of the morph each child reveals over; `--dock-stagger-rise`
|
|
65
|
+
is the px the child rises from as it fades in. Consumers retune the
|
|
66
|
+
cascade feel per instance. */
|
|
67
|
+
--dock-stagger-window-size: 0.55;
|
|
68
|
+
--dock-stagger-rise: 4px;
|
|
69
|
+
/* AW.W3b — big-dock tokens. `--dock-card-radius` is the finite card-shell
|
|
70
|
+
radius (default --radius-dock-card = --radius-3xl, density-scaled
|
|
71
|
+
below); `--dock-tile-min` is the grid tile minimum (auto-fill track
|
|
72
|
+
floor). Both are density-scaled (compact tighter, spacious looser) — the
|
|
73
|
+
card radius + tile size ride the density cascade, never magic numbers. */
|
|
74
|
+
--dock-card-radius: var(--radius-dock-card);
|
|
75
|
+
--dock-tile-min: 4.5rem;
|
|
63
76
|
/* Individual-transform identity base (AQ.W3 §W3.2). `scale: 1` at rest
|
|
64
77
|
mints the stacking context once so a portaled popover over `--z-dock`
|
|
65
78
|
stays above the dock during a hover scale (the load-bearing
|
|
@@ -300,12 +313,28 @@
|
|
|
300
313
|
only. Width is driven by the inner `.dock-layers` FLIP via
|
|
301
314
|
`useLayerTransition`; pinning it here would race the FLIP and
|
|
302
315
|
reintroduce the `width: auto` non-interpolation diagnosed in
|
|
303
|
-
R1+R6.
|
|
316
|
+
R1+R6.
|
|
317
|
+
|
|
318
|
+
AW.W3 — the collapsed-hover `scale` (the `.collapsed:hover` rule below) +
|
|
319
|
+
the press squish (`--scale-press-dock` on the dock controls) ride the
|
|
320
|
+
INDIVIDUAL `scale` property, NOT `transform`. Adding `scale
|
|
321
|
+
var(--dock-motion-resize)` here unifies the hover scale onto the SAME dock
|
|
322
|
+
spring vocabulary as the morph, so a collapsed→hover→expand gesture reads as
|
|
323
|
+
ONE continuous spring rather than a `scale`-ease handed off to the
|
|
324
|
+
width-spring. Under PRM the global gate strips `scale` from the
|
|
325
|
+
transition-property → the hover scale snaps (0 morph frames). */
|
|
304
326
|
.glass-dock:not(.vertical) {
|
|
305
327
|
transition:
|
|
306
328
|
padding var(--dock-motion-resize),
|
|
307
329
|
box-shadow var(--dock-motion-standard),
|
|
308
330
|
transform var(--dock-motion-resize),
|
|
331
|
+
scale var(--dock-motion-resize),
|
|
332
|
+
/* AW.W3b — the pill↔card corner radius morphs on the SAME spring the
|
|
333
|
+
aperture rides (ONE free interpolation). Without this the
|
|
334
|
+
shape-card collapsed↔expanded radius snaps discretely. The list is
|
|
335
|
+
shared by ALL horizontal docks — the radius transition also glides
|
|
336
|
+
the wrap collapsed↔expanded radius swap (desirable). */
|
|
337
|
+
border-radius var(--dock-motion-resize),
|
|
309
338
|
background var(--dock-motion-standard),
|
|
310
339
|
border-color var(--dock-motion-standard);
|
|
311
340
|
}
|
|
@@ -339,6 +368,7 @@
|
|
|
339
368
|
padding var(--dock-motion-resize),
|
|
340
369
|
box-shadow var(--dock-motion-standard),
|
|
341
370
|
transform var(--dock-motion-resize),
|
|
371
|
+
scale var(--dock-motion-resize),
|
|
342
372
|
background var(--dock-motion-standard),
|
|
343
373
|
border-color var(--dock-motion-standard);
|
|
344
374
|
}
|
|
@@ -357,6 +387,38 @@
|
|
|
357
387
|
border-radius: var(--radius-xl);
|
|
358
388
|
}
|
|
359
389
|
|
|
390
|
+
/* AW.W3b — horizontal-root shape rules. The `shape-*` class previously bound
|
|
391
|
+
ONLY under `.variant-rail`; on a horizontal `variant-dock` the prop silently
|
|
392
|
+
no-op'd (the root hardwired `--radius-dock` = `--radius-pill`). These rules
|
|
393
|
+
close that documented-but-dead prop — `shape="rounded"` / `shape="card"` now
|
|
394
|
+
paint on a horizontal dock. Scoped to `variant-dock:not(.vertical)` so the
|
|
395
|
+
rail + instrument-strip variants (which own their own radius) are untouched. */
|
|
396
|
+
.glass-dock.variant-dock:not(.vertical).shape-rounded {
|
|
397
|
+
border-radius: var(--radius-xl);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/* The big-dock CARD shell — a FINITE concentric radius above 2xl, below pill.
|
|
401
|
+
It does NOT collapse to a stadium. Collapsed it returns to a pill (the morph
|
|
402
|
+
target); the pill↔card swap glides on the `--dock-motion-resize` spring
|
|
403
|
+
(`border-radius` is in the `:not(.vertical)` transition list). */
|
|
404
|
+
.glass-dock.variant-dock:not(.vertical).shape-card {
|
|
405
|
+
border-radius: var(--dock-card-radius);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.glass-dock.variant-dock:not(.vertical).shape-card.collapsed {
|
|
409
|
+
border-radius: var(--radius-pill);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/* The squircle reads markedly better at the large card radius — an
|
|
413
|
+
`@supports`-gated ENHANCEMENT only (Chrome 139+, no FF/Safari 2026). The
|
|
414
|
+
border-radius arc is the CONTRACT; the squircle is the better tier (the
|
|
415
|
+
baseline arc is the acceptable contract, NOT a degraded fallback). */
|
|
416
|
+
@supports (corner-shape: squircle) {
|
|
417
|
+
.glass-dock.variant-dock:not(.vertical).shape-card {
|
|
418
|
+
corner-shape: squircle;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
360
422
|
/* AJ-W1-δ — `instrument-strip` variant (per G-AJ-D7 Path 1).
|
|
361
423
|
*
|
|
362
424
|
* The speedtest survey cockpit's vertical rail needs chassis-strip
|
|
@@ -438,6 +500,13 @@
|
|
|
438
500
|
justify-content: center;
|
|
439
501
|
}
|
|
440
502
|
|
|
503
|
+
/* AW.W3 — the collapsed-hover scale rides the unified dock spring vocabulary.
|
|
504
|
+
The `scale var(--dock-motion-resize)` arm was added to the `.glass-dock`
|
|
505
|
+
root transition above, so this hover scale settles on the SAME spring curve
|
|
506
|
+
the expand morph uses — a collapsed→hover→expand gesture is one continuous
|
|
507
|
+
spring, not a `scale`-ease handed off to the width-spring. The hover scale
|
|
508
|
+
and the press squish (`--scale-press-dock`) are now one register on the dock
|
|
509
|
+
surface. */
|
|
441
510
|
.glass-dock.collapsed:hover {
|
|
442
511
|
background: var(--glass-bg-wash);
|
|
443
512
|
border-color: var(--glass-border-floating);
|
|
@@ -659,6 +728,72 @@
|
|
|
659
728
|
inset: 0;
|
|
660
729
|
}
|
|
661
730
|
|
|
731
|
+
/* AW.W3 — the spring-keyed child stagger. While the size spring drives the
|
|
732
|
+
morph, the driver arms the container (`.dock-layers` / `.dock-layer-stack`)
|
|
733
|
+
with `data-revealing` and writes the morph's normalized progress to
|
|
734
|
+
`--dock-morph-progress` (0 → 1) every frame. Each direct child of the
|
|
735
|
+
ACTIVE pane reveals when that progress crosses a per-child onset, so the
|
|
736
|
+
controls cascade IN ON the physical morph — a fast flick and a slow
|
|
737
|
+
hover-open both choreograph correctly, and an interrupted morph carries the
|
|
738
|
+
cascade with it (no orphaned timers). This is a per-CHILD opacity onset; the
|
|
739
|
+
active PANE itself stays statically opacity:1 (revealed by the aperture —
|
|
740
|
+
the W2 clip-reveal contract is NOT regressed).
|
|
741
|
+
|
|
742
|
+
The reveal is a pure `calc` ramp: `opacity = clamp(0, (progress − onset) /
|
|
743
|
+
window, 1)`. `--dock-stagger-onset` steps per child (nth-child); the window
|
|
744
|
+
is `--dock-stagger-window`. The children also rise a few px on a `translate`
|
|
745
|
+
that resolves on the same ramp. ONLY children inside the `[data-revealing]`
|
|
746
|
+
active pane are armed — at rest the children carry no opacity authority (the
|
|
747
|
+
default cascade leaves them at 1).
|
|
748
|
+
|
|
749
|
+
PRM: the driver never arms `data-revealing` on the synchronous PRM swap (the
|
|
750
|
+
FLIP fast-path returns before the spring; the spring's `respectReducedMotion`
|
|
751
|
+
settles instantly), so the children paint at full opacity at once. The
|
|
752
|
+
global PRM gate also strips `translate` from the transition-property — the
|
|
753
|
+
rise snaps. The cascade collapses to 0 stagger frames under PRM. */
|
|
754
|
+
.dock-layers[data-revealing] .dock-layer--full > *,
|
|
755
|
+
.dock-layer-stack[data-revealing] .dock-layer-item-host.is-active > * {
|
|
756
|
+
--dock-stagger-window: var(--dock-stagger-window-size, 0.55);
|
|
757
|
+
--dock-stagger-onset: 0;
|
|
758
|
+
opacity: clamp(
|
|
759
|
+
0,
|
|
760
|
+
calc(
|
|
761
|
+
(var(--dock-morph-progress, 1) - var(--dock-stagger-onset)) /
|
|
762
|
+
var(--dock-stagger-window)
|
|
763
|
+
),
|
|
764
|
+
1
|
|
765
|
+
);
|
|
766
|
+
translate: 0
|
|
767
|
+
calc(
|
|
768
|
+
(1 - clamp(0, calc((var(--dock-morph-progress, 1) - var(--dock-stagger-onset)) / var(--dock-stagger-window)), 1)) *
|
|
769
|
+
var(--dock-stagger-rise, 4px)
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/* Per-child onsets — each successive control starts a beat later, so the row
|
|
774
|
+
fills outer→in on expand. The onsets are fractions of the morph progress;
|
|
775
|
+
capped so even a long row finishes within the morph window. */
|
|
776
|
+
.dock-layers[data-revealing] .dock-layer--full > *:nth-child(2),
|
|
777
|
+
.dock-layer-stack[data-revealing] .dock-layer-item-host.is-active > *:nth-child(2) {
|
|
778
|
+
--dock-stagger-onset: 0.08;
|
|
779
|
+
}
|
|
780
|
+
.dock-layers[data-revealing] .dock-layer--full > *:nth-child(3),
|
|
781
|
+
.dock-layer-stack[data-revealing] .dock-layer-item-host.is-active > *:nth-child(3) {
|
|
782
|
+
--dock-stagger-onset: 0.16;
|
|
783
|
+
}
|
|
784
|
+
.dock-layers[data-revealing] .dock-layer--full > *:nth-child(4),
|
|
785
|
+
.dock-layer-stack[data-revealing] .dock-layer-item-host.is-active > *:nth-child(4) {
|
|
786
|
+
--dock-stagger-onset: 0.24;
|
|
787
|
+
}
|
|
788
|
+
.dock-layers[data-revealing] .dock-layer--full > *:nth-child(5),
|
|
789
|
+
.dock-layer-stack[data-revealing] .dock-layer-item-host.is-active > *:nth-child(5) {
|
|
790
|
+
--dock-stagger-onset: 0.32;
|
|
791
|
+
}
|
|
792
|
+
.dock-layers[data-revealing] .dock-layer--full > *:nth-child(n + 6),
|
|
793
|
+
.dock-layer-stack[data-revealing] .dock-layer-item-host.is-active > *:nth-child(n + 6) {
|
|
794
|
+
--dock-stagger-onset: 0.4;
|
|
795
|
+
}
|
|
796
|
+
|
|
662
797
|
/* AW.W2 — the at-rest clip-lift moved to `.expanded:not([data-morphing])` /
|
|
663
798
|
`.always-expanded:not([data-morphing])` (the clip-shell block at the top).
|
|
664
799
|
A plain `overflow: visible` here would lift the clip DURING the morph (the
|
|
@@ -676,6 +811,46 @@
|
|
|
676
811
|
cursor: default;
|
|
677
812
|
}
|
|
678
813
|
|
|
814
|
+
/* ── AW.W3b — `layout="grid"` the 2D auto-grid big dock ──────────────
|
|
815
|
+
The active layer becomes a self-wrapping tile grid (Launchpad / Stage-Manager
|
|
816
|
+
track symmetry — `auto-fill` columns floored at `--dock-tile-min`, explicit
|
|
817
|
+
`grid-auto-rows` so the concentric look holds). DISTINCT from
|
|
818
|
+
`overflow="wrap"`'s content-ordered flex-wrap. A `layout="grid"` dock is
|
|
819
|
+
`alwaysExpanded` by contract (GlassDock.vue), so there is NO width morph and
|
|
820
|
+
hence NO per-frame grid-column reflow. `white-space: normal` releases the
|
|
821
|
+
single-row nowrap so the tiles wrap. */
|
|
822
|
+
.glass-dock.layout-grid {
|
|
823
|
+
white-space: normal;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
.glass-dock.layout-grid .dock-layer--full {
|
|
827
|
+
display: grid;
|
|
828
|
+
grid-template-columns: repeat(
|
|
829
|
+
auto-fill,
|
|
830
|
+
minmax(var(--dock-tile-min, 4.5rem), 1fr)
|
|
831
|
+
);
|
|
832
|
+
grid-auto-rows: var(--dock-tile-min, 4.5rem);
|
|
833
|
+
gap: var(--dock-layer-gap, 0.375rem);
|
|
834
|
+
width: 100%;
|
|
835
|
+
height: auto;
|
|
836
|
+
white-space: normal;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/* A grid dock fills the inline cap; the active layer is the panel body, not a
|
|
840
|
+
shrink-to-fit row. The cap is the `--dock-max-inline-size` clamp (KEPT). */
|
|
841
|
+
.glass-dock.layout-grid .dock-layers {
|
|
842
|
+
width: 100%;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
/* CONCENTRIC inner tiles — inner radius = outer card radius − padding (the
|
|
846
|
+
universal macOS / visionOS / Raycast rule). NEVER nest two pills
|
|
847
|
+
(stadium-in-stadium); a finite outer + a stepped-down inner. */
|
|
848
|
+
.glass-dock.layout-grid.shape-card .dock-layer--full > * {
|
|
849
|
+
border-radius: calc(
|
|
850
|
+
var(--dock-card-radius) - var(--dock-padding-inline, 0.5rem)
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
|
|
679
854
|
/* ── Scroll-on-overflow opt-in (GlassDock `overflow="scroll"`) ───────
|
|
680
855
|
The default contract grows-to-fit then overflows visibly past the
|
|
681
856
|
axis cap. `overflow="scroll"` instead makes the dock the scroll port
|
|
@@ -748,6 +923,15 @@
|
|
|
748
923
|
min-height: var(--dock-wrap-layer-min-height, var(--dock-layer-height, 2rem));
|
|
749
924
|
gap: var(--dock-wrap-row-gap, 0.25rem)
|
|
750
925
|
var(--dock-wrap-column-gap, var(--dock-layer-gap, 0.375rem));
|
|
926
|
+
/* AW.W3b — the graceful wrap-morph. The wrap/unwrap row-reflow flips
|
|
927
|
+
`flex-wrap` at the `--dock-overflow-bp` `@media` boundary; without a
|
|
928
|
+
transition the rows JUMP-CUT. Sequencing the layer's `min-height`
|
|
929
|
+
through the `--dock-motion-resize` spring MORPHS the reflow. The global
|
|
930
|
+
PRM gate strips `min-height` from the transition-property under reduce →
|
|
931
|
+
the morph snaps (0 morph frames) while the reflow still completes. The
|
|
932
|
+
wrap radius swap (`.collapsed` / `@media`) glides too — `border-radius`
|
|
933
|
+
is in the shared `:not(.vertical)` transition list (AW.W3b scope 2). */
|
|
934
|
+
transition: min-height var(--dock-motion-resize);
|
|
751
935
|
}
|
|
752
936
|
|
|
753
937
|
.glass-dock.dock-overflow-wrap .dock-layer--summary {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* AW.W23 — the #glass-refract convex-lens SVG filter, shipped as a library
|
|
2
|
+
* asset so `.glass-refract` works OUT OF THE BOX (no consumer hand-mounts a
|
|
3
|
+
* filter node).
|
|
4
|
+
*
|
|
5
|
+
* The refraction recipe is the canonical 3-primitive graph (kube.io /
|
|
6
|
+
* LogRocket, 2025):
|
|
7
|
+
*
|
|
8
|
+
* feImage — a pre-baked displacement map computed from the convex-
|
|
9
|
+
* squircle surface profile y = ⁴√(1 − (1 − x)⁴) (Apple's
|
|
10
|
+
* preferred corner curve) via Snell's law (n₁ = 1 air,
|
|
11
|
+
* n₂ = 1.5 glass). The map is authored as an embedded SVG
|
|
12
|
+
* whose R channel carries the x-shift and G channel the
|
|
13
|
+
* y-shift (neutral 128 = no shift; bright edges bend light
|
|
14
|
+
* inward like a lens), expressed as two crossed linear
|
|
15
|
+
* gradients over the squircle silhouette.
|
|
16
|
+
* feDisplacementMap— displaces the backdrop by the baked map (R = x-shift,
|
|
17
|
+
* G = y-shift, `scale` = max px, neutral 128).
|
|
18
|
+
* feImage specular — a soft top-left specular highlight…
|
|
19
|
+
* feBlend screen — …blended `screen` over the displaced backdrop for the
|
|
20
|
+
* polished-glass catch.
|
|
21
|
+
*
|
|
22
|
+
* The filter ships INLINE as a data-URI in the `backdrop-filter` declaration, so
|
|
23
|
+
* the consumer gets it with zero markup. `backdrop-filter: url(#…)` is
|
|
24
|
+
* Chromium-only (WebKit bug 245510 open, Firefox not shipping), so the whole
|
|
25
|
+
* decl sits behind `@supports (backdrop-filter: url(#…))`; a non-supporting
|
|
26
|
+
* engine paints the un-gated blur base alone (no broken `url()` reference).
|
|
27
|
+
*
|
|
28
|
+
* RE-HOME (AW.W23 — decouple from the W22-folded compound). At HEAD the
|
|
29
|
+
* refraction rule was bound to `.glass-specular-track.glass-refract`. W22 folds
|
|
30
|
+
* `glass-specular-track` into `.glass-material`, retiring the compound's left
|
|
31
|
+
* operand, so the rule is re-homed onto `.glass-material.glass-refract` — the
|
|
32
|
+
* refraction garnish opts in on ANY `.glass-material` surface, no dependency on
|
|
33
|
+
* the now-aliased `glass-specular-track` class. The garnish stays PE-only over
|
|
34
|
+
* the blur base; it is NEVER promoted to the card/primitive substrate
|
|
35
|
+
* (feDisplacementMap is resize-expensive — only the `scale` attr animates
|
|
36
|
+
* without rebuilding the map).
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
@layer components {
|
|
40
|
+
/* The shipped filter graph as a data-URI SVG. A `0×0` host SVG mounted via
|
|
41
|
+
the CSS asset keeps the `#glass-refract` id resolvable in-document. */
|
|
42
|
+
.glass-refract {
|
|
43
|
+
/* The named convex-lens displacement filter, baked from the squircle
|
|
44
|
+
surface profile. Composed as a custom property so the @supports arm
|
|
45
|
+
below references one source. */
|
|
46
|
+
--glass-refract-filter: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='glass-refract' x='0' y='0' width='100%25' height='100%25'%3E%3CfeImage href='data:image/svg+xml,%253Csvg xmlns=%2522http://www.w3.org/2000/svg%2522 width=%2522100%2522 height=%2522100%2522%253E%253CradialGradient id=%2522m%2522 cx=%252250%2525%2522 cy=%252250%2525%2522 r=%252250%2525%2522%253E%253Cstop offset=%25220%2525%2522 stop-color=%2522rgb(128,128,128)%2522/%253E%253Cstop offset=%252270%2525%2522 stop-color=%2522rgb(128,128,128)%2522/%253E%253Cstop offset=%2522100%2525%2522 stop-color=%2522rgb(255,255,128)%2522/%253E%253C/radialGradient%253E%253Crect width=%2522100%2522 height=%2522100%2522 fill=%2522url(%2523m)%2522/%253E%253C/svg%253E' result='map'/%3E%3CfeDisplacementMap in='SourceGraphic' in2='map' scale='28' xChannelSelector='R' yChannelSelector='G' result='disp'/%3E%3CfeImage href='data:image/svg+xml,%253Csvg xmlns=%2522http://www.w3.org/2000/svg%2522 width=%2522100%2522 height=%2522100%2522%253E%253CradialGradient id=%2522s%2522 cx=%252230%2525%2522 cy=%252225%2525%2522 r=%252260%2525%2522%253E%253Cstop offset=%25220%2525%2522 stop-color=%2522rgba(255,255,255,0.5)%2522/%253E%253Cstop offset=%2522100%2525%2522 stop-color=%2522rgba(255,255,255,0)%2522/%253E%253C/radialGradient%253E%253Crect width=%2522100%2522 height=%2522100%2522 fill=%2522url(%2523s)%2522/%253E%253C/svg%253E' result='spec'/%3E%3CfeBlend in='disp' in2='spec' mode='screen'/%3E%3C/filter%3E%3C/svg%3E#glass-refract");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* The gated consuming selector — RE-HOMED onto `.glass-material.glass-refract`
|
|
50
|
+
(decoupled from the W22-folded `.glass-specular-track` compound). The
|
|
51
|
+
refraction composes the shipped `#glass-refract` filter OVER the host's
|
|
52
|
+
existing blur; it is additive (the blur already painted, this sharpens the
|
|
53
|
+
refraction edge where the engine supports it). */
|
|
54
|
+
@supports (backdrop-filter: url("#glass-refract")) {
|
|
55
|
+
.glass-material.glass-refract,
|
|
56
|
+
.glass-refract {
|
|
57
|
+
backdrop-filter: var(--glass-blur-resting) var(--glass-refract-filter);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -1,120 +1,45 @@
|
|
|
1
1
|
/* Pointer-anchored moving specular — the iOS-26 Liquid Glass illuminate-from-
|
|
2
|
-
* within behaviour
|
|
2
|
+
* within behaviour.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* over a `mask-image` so the glow stays clipped to the glass plate and travels
|
|
10
|
-
* with the cursor — "the light travels around the material / illuminate under
|
|
11
|
-
* your fingertip".
|
|
4
|
+
* AW.W22 — the moving-specular `::before` body (the masked radial catch-light,
|
|
5
|
+
* the rest/hover/active intensity steps, the var() fallback) was FOLDED into the
|
|
6
|
+
* single `.glass-material` mixin in glass.css, where the five ladder rungs +
|
|
7
|
+
* `.glass-card` + the `.dock-icon-button` control + `.glass-specular-track`
|
|
8
|
+
* compose it from ONE source. There is NO duplicate `::before` body here.
|
|
12
9
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* light
|
|
17
|
-
*
|
|
10
|
+
* `.glass-specular-track` is now a thin ALIAS — it is listed in the
|
|
11
|
+
* `.glass-material` selector GROUP in glass.css, so a `.glass-specular-track`
|
|
12
|
+
* element gets the unified material (the `::before` catch-light, the rim, the
|
|
13
|
+
* interaction-light) with zero duplicate paint. This keeps the historical
|
|
14
|
+
* opt-in call sites (Card.vue, any consumer that still reaches for the class)
|
|
15
|
+
* painting the catch-light without a legacy shim — the alias is a live
|
|
16
|
+
* composition onto the unified mixin, not a dead orphan.
|
|
18
17
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
18
|
+
* This file carries ONLY:
|
|
19
|
+
* 1. The reduced-motion + reduced-transparency a11y brackets, pointed at the
|
|
20
|
+
* `.glass-material` group (the canonical specular owner) so the guards
|
|
21
|
+
* cover every band surface, not just the opt-in class.
|
|
22
|
+
* 2. The `@supports`-gated `#glass-refract` refraction garnish — RE-HOMED at
|
|
23
|
+
* AW.W23 off the (now-folded) `.glass-specular-track.glass-refract`
|
|
24
|
+
* compound onto `.glass-material.glass-refract` (see glass-refract.css for
|
|
25
|
+
* the shipped filter asset + the re-homed consuming selector).
|
|
23
26
|
*/
|
|
24
27
|
|
|
25
|
-
@layer components {
|
|
26
|
-
/* The opt-in class. A `::before` pseudo carries the moving catch-light so it
|
|
27
|
-
composes over the host's own glass background/blur without a second
|
|
28
|
-
backdrop-filter (no glass-on-glass — the specular is a LIGHT layer, not a
|
|
29
|
-
second plate). The host need not be positioned-relative by us; the five
|
|
30
|
-
glass tiers + the dock + Button already mint `position: relative`. */
|
|
31
|
-
.glass-specular-track::before {
|
|
32
|
-
content: "";
|
|
33
|
-
position: absolute;
|
|
34
|
-
inset: 0;
|
|
35
|
-
border-radius: inherit;
|
|
36
|
-
pointer-events: none;
|
|
37
|
-
z-index: 1;
|
|
38
|
-
/* Map the consumer's pointer write onto the typed properties, ON THE
|
|
39
|
-
PSEUDO. The `--mouse-x/--mouse-y` write lands on the HOST (inline
|
|
40
|
-
style); those are UNREGISTERED custom properties, so they inherit
|
|
41
|
-
down to this `::before` naturally. The registered `--specular-*`
|
|
42
|
-
props are `inherits: false` (per-surface ownership), which means a
|
|
43
|
-
`::before` does NOT see a value set on its originating element — so
|
|
44
|
-
the mapping must be declared HERE, on the pseudo, where it owns its
|
|
45
|
-
own typed value. The `var(--mouse-x, 50%)` floor is the centred
|
|
46
|
-
fallback: an engine that never receives a pointer write paints a
|
|
47
|
-
centred catch-light, never a broken/absent one. */
|
|
48
|
-
--specular-x: var(--mouse-x, 50%);
|
|
49
|
-
--specular-y: var(--mouse-y, 50%);
|
|
50
|
-
/* The catch-light intensity floor at rest; hover/active lift it (the
|
|
51
|
-
pseudo-targeting rules below). Registered `<number>`, so the layer
|
|
52
|
-
opacity it drives interpolates smoothly. */
|
|
53
|
-
--specular-intensity: 0.35;
|
|
54
|
-
/* The travelling catch-light. A soft circular glow centred on the
|
|
55
|
-
tracked pointer position; the warm-white tint keeps the warm-cream
|
|
56
|
-
identity (NOT iOS blue). The gradient carries FIXED stop alphas — the
|
|
57
|
-
`--specular-intensity` multiplier drives the layer `opacity` below
|
|
58
|
-
(NOT a `calc()` in the stop alpha: a registered-`@property` `var()`
|
|
59
|
-
nested in `calc()` inside an `hsl()` alpha inside a gradient does not
|
|
60
|
-
substitute in Chromium — it computes to 0. Driving the whole layer's
|
|
61
|
-
`opacity` is both correct AND the cleaner model — intensity IS a
|
|
62
|
-
layer brightness, not a per-stop axis). */
|
|
63
|
-
background: radial-gradient(
|
|
64
|
-
circle at var(--specular-x, 50%) var(--specular-y, 50%),
|
|
65
|
-
hsl(40 30% 100% / 0.55) 0%,
|
|
66
|
-
hsl(40 30% 100% / 0.22) 22%,
|
|
67
|
-
transparent 55%
|
|
68
|
-
);
|
|
69
|
-
opacity: var(--specular-intensity, 0.35);
|
|
70
|
-
/* Clip the glow to the plate so it rides the surface (the "light travels
|
|
71
|
-
AROUND the material" read). `mask-image` Baseline 2023-12-07; a
|
|
72
|
-
non-supporting engine ignores it and the `inset: 0` + radius-inherit
|
|
73
|
-
already bound the glow to the host rectangle. */
|
|
74
|
-
mask-image: radial-gradient(
|
|
75
|
-
circle at var(--specular-x, 50%) var(--specular-y, 50%),
|
|
76
|
-
black 0%,
|
|
77
|
-
black 55%,
|
|
78
|
-
transparent 75%
|
|
79
|
-
);
|
|
80
|
-
mix-blend-mode: screen;
|
|
81
|
-
/* Smooth the position + intensity between pointer samples. The typed
|
|
82
|
-
`@property` registrations make the percentage stops interpolable, so
|
|
83
|
-
the catch-light position SETTLES rather than snapping frame-to-frame;
|
|
84
|
-
the intensity rides the layer `opacity` transition. */
|
|
85
|
-
transition:
|
|
86
|
-
--specular-x var(--duration-fast, 150ms) var(--ease-standard, ease-out),
|
|
87
|
-
--specular-y var(--duration-fast, 150ms) var(--ease-standard, ease-out),
|
|
88
|
-
opacity var(--duration-normal, 240ms) var(--ease-standard, ease-out);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.dark .glass-specular-track::before {
|
|
92
|
-
/* Softer over the deep canvas — the screen blend lifts harder on dark. */
|
|
93
|
-
--specular-intensity: 0.22;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/* Hover/active lift the intensity — the "illuminate under your fingertip"
|
|
97
|
-
brighten on engagement; settles back on leave via the opacity transition
|
|
98
|
-
above. The rules target the `::before` (the intensity lives on the pseudo
|
|
99
|
-
— see the mapping note above). */
|
|
100
|
-
.glass-specular-track:hover::before {
|
|
101
|
-
--specular-intensity: 0.6;
|
|
102
|
-
}
|
|
103
|
-
.glass-specular-track:active::before {
|
|
104
|
-
--specular-intensity: 0.85;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
28
|
/* ── Reduced-motion: pin the specular STATIC ───────────────────────────────
|
|
109
29
|
The MANDATORY guard. Under `prefers-reduced-motion: reduce` the catch-light
|
|
110
|
-
paints static at rest position — no pointer-tracking
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
(where the typed `--specular-*` props are owned), overriding the pointer-map
|
|
115
|
-
above by order — the `var(--mouse-x)` write is ignored, the catch-light pins
|
|
116
|
-
centred. */
|
|
30
|
+
paints static at rest position — no pointer-tracking, no transition. The pin
|
|
31
|
+
covers the unified `.glass-material` group AND the `.glass-specular-track`
|
|
32
|
+
alias so EVERY band surface (not just the opt-in class) freezes the position
|
|
33
|
+
while keeping the centred catch-light + the warm-cream tint. */
|
|
117
34
|
@media (prefers-reduced-motion: reduce) {
|
|
35
|
+
.glass-material::before,
|
|
36
|
+
.glass-wash::before,
|
|
37
|
+
.glass-quiet::before,
|
|
38
|
+
.glass-resting::before,
|
|
39
|
+
.glass-floating::before,
|
|
40
|
+
.glass-overlay::before,
|
|
41
|
+
.glass-card::before,
|
|
42
|
+
.dock-icon-button::before,
|
|
118
43
|
.glass-specular-track::before {
|
|
119
44
|
--specular-x: 50%;
|
|
120
45
|
--specular-y: 50%;
|
|
@@ -125,30 +50,18 @@
|
|
|
125
50
|
/* ── Reduced-transparency: drop the specular ───────────────────────────────
|
|
126
51
|
When the user requests reduced transparency the surface goes opaque (the
|
|
127
52
|
glass.css bracket maps the blurs to `none`); a travelling catch-light on a
|
|
128
|
-
flat opaque surface is decoration without substrate, so it drops too
|
|
53
|
+
flat opaque surface is decoration without substrate, so it drops too — across
|
|
54
|
+
the whole material group. */
|
|
129
55
|
@media (prefers-reduced-transparency: reduce) {
|
|
56
|
+
.glass-material::before,
|
|
57
|
+
.glass-wash::before,
|
|
58
|
+
.glass-quiet::before,
|
|
59
|
+
.glass-resting::before,
|
|
60
|
+
.glass-floating::before,
|
|
61
|
+
.glass-overlay::before,
|
|
62
|
+
.glass-card::before,
|
|
63
|
+
.dock-icon-button::before,
|
|
130
64
|
.glass-specular-track::before {
|
|
131
65
|
display: none;
|
|
132
66
|
}
|
|
133
67
|
}
|
|
134
|
-
|
|
135
|
-
/* ── feDisplacementMap refraction garnish — PE-only, @supports-gated ────────
|
|
136
|
-
True SVG `feDisplacementMap` lensing (`backdrop-filter: url(#…)`) is
|
|
137
|
-
Chromium-only (WebKit bug #245510, Firefox not shipping) and resize-
|
|
138
|
-
expensive. It lands ONLY as a progressive-enhancement garnish layered OVER
|
|
139
|
-
the blur base, NEVER the substrate — gated `@supports (backdrop-filter:
|
|
140
|
-
url(#…))` so a non-supporting engine paints the blur base alone (no broken
|
|
141
|
-
`url(#…)` reference). A consumer opts a glass surface into the refraction by
|
|
142
|
-
adding `.glass-refract` AND mounting the named SVG filter; the gate asserts
|
|
143
|
-
the garnish never reaches the substrate. The blur base paints WITHOUT it. */
|
|
144
|
-
@supports (backdrop-filter: url("#glass-refract")) {
|
|
145
|
-
@layer components {
|
|
146
|
-
.glass-specular-track.glass-refract {
|
|
147
|
-
/* The garnish composes the consumer-mounted `#glass-refract` SVG
|
|
148
|
-
filter OVER the host's existing blur. It is additive — the host's
|
|
149
|
-
`backdrop-filter` blur already painted; this only sharpens the
|
|
150
|
-
refraction edge where the engine supports it. */
|
|
151
|
-
backdrop-filter: var(--glass-blur-resting) url("#glass-refract");
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|