@viraui/react 2026.1.10 → 2026.2.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/catalog.json +6 -0
- package/dist/components/beam/beam-pulse-driver.d.ts +25 -0
- package/dist/components/beam/beam-pulse-driver.js +239 -0
- package/dist/components/beam/beam.css +1 -0
- package/dist/components/beam/beam.d.ts +93 -0
- package/dist/components/beam/beam.guide.json +123 -0
- package/dist/components/beam/beam.js +173 -0
- package/dist/components/beam/beam.module.js +19 -0
- package/dist/components/beam/index.d.ts +2 -0
- package/dist/components/glow/glow-tracker.js +1 -5
- package/dist/components/glow/glow.css +1 -1
- package/dist/components/glow/glow.d.ts +0 -14
- package/dist/components/glow/glow.guide.json +9 -20
- package/dist/components/glow/glow.js +3 -9
- package/dist/components/glow/glow.module.js +1 -4
- package/dist/components/glow/index.d.ts +0 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/index.js +2 -2
- package/package.json +2 -2
package/dist/catalog.json
CHANGED
|
@@ -38,6 +38,12 @@
|
|
|
38
38
|
"summary": "Decorative status-dot overlay that wraps a child, with optional radial mask cutout on the child around the corner dot.",
|
|
39
39
|
"guidePath": "./components/badge/badge.guide.json"
|
|
40
40
|
},
|
|
41
|
+
{
|
|
42
|
+
"id": "beam",
|
|
43
|
+
"name": "Beam",
|
|
44
|
+
"summary": "Decorative animated border, line, or pulse glow on a transparent Surface shell (radius only). Put the visual card as children. CSS-module geometry plus shared ~30fps pulse rAF for pulse variants. Memory/CPU cost — use sparingly.",
|
|
45
|
+
"guidePath": "./components/beam/beam.guide.json"
|
|
46
|
+
},
|
|
41
47
|
{
|
|
42
48
|
"id": "bleed",
|
|
43
49
|
"name": "Bleed",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type BeamPulseOscillator = {
|
|
2
|
+
prop: string;
|
|
3
|
+
a: number;
|
|
4
|
+
b: number;
|
|
5
|
+
period: number;
|
|
6
|
+
delay: number;
|
|
7
|
+
unit: '' | 'px';
|
|
8
|
+
};
|
|
9
|
+
export type BeamPulseDriverConfig = {
|
|
10
|
+
oscillators: BeamPulseOscillator[];
|
|
11
|
+
hue: {
|
|
12
|
+
prop: string;
|
|
13
|
+
range: number;
|
|
14
|
+
period: number;
|
|
15
|
+
continuous: boolean;
|
|
16
|
+
} | null;
|
|
17
|
+
};
|
|
18
|
+
/** Cosine ease-in-out factor in [0, 1]: 0 at phase 0/1, 1 at phase 0.5. */
|
|
19
|
+
export declare const beamPulsePingPong: (phase: number) => number;
|
|
20
|
+
export declare const getBeamPulseDriverConfig: (variant: "pulse-inner" | "pulse-outside", durationSec: number, staticColors: boolean) => BeamPulseDriverConfig;
|
|
21
|
+
export declare const registerBeamPulse: (el: HTMLElement, config: BeamPulseDriverConfig) => (() => void);
|
|
22
|
+
/** Story / test helper. */
|
|
23
|
+
export declare const getBeamPulseRegistrySize: () => number;
|
|
24
|
+
/** Story / test helper — forces a paint tick using `performance.now()` baseline. */
|
|
25
|
+
export declare const flushBeamPulseFrameForTests: () => void;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
//#region src/components/beam/beam-pulse-driver.ts
|
|
2
|
+
var instances = /* @__PURE__ */ new Set();
|
|
3
|
+
var rafId = null;
|
|
4
|
+
var lastFrame = 0;
|
|
5
|
+
/** ~30 fps; small slack so early frames still paint. */
|
|
6
|
+
var FRAME_INTERVAL = 1e3 / 30 - 2;
|
|
7
|
+
var TWO_PI = Math.PI * 2;
|
|
8
|
+
var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
9
|
+
/** Cosine ease-in-out factor in [0, 1]: 0 at phase 0/1, 1 at phase 0.5. */
|
|
10
|
+
var beamPulsePingPong = (phase) => (1 - Math.cos(TWO_PI * phase)) / 2;
|
|
11
|
+
var prefersReducedMotion = () => typeof window !== "undefined" && typeof matchMedia === "function" && matchMedia(REDUCED_MOTION_QUERY).matches;
|
|
12
|
+
/** Theme-agnostic defaults (upstream dark presets). */
|
|
13
|
+
var pulseParams = (variant, durationSec) => {
|
|
14
|
+
const durScale = durationSec / 2.3;
|
|
15
|
+
if (variant === "pulse-inner") return {
|
|
16
|
+
sp: .28,
|
|
17
|
+
dr: 33,
|
|
18
|
+
op: .48,
|
|
19
|
+
gh: .34,
|
|
20
|
+
bs: 1.9 * durScale,
|
|
21
|
+
ss: 2.6 * durScale,
|
|
22
|
+
ghs: 2.4 * durScale,
|
|
23
|
+
huePeriod: 16
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
sp: .28,
|
|
27
|
+
dr: 14,
|
|
28
|
+
op: .46,
|
|
29
|
+
gh: .16,
|
|
30
|
+
bs: 2.3 * durScale,
|
|
31
|
+
ss: 6.4 * durScale,
|
|
32
|
+
ghs: 2.4 * durScale,
|
|
33
|
+
huePeriod: 14
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
var oscillatorDefs = (p) => {
|
|
37
|
+
const { sp, dr, op, gh, bs, ss, ghs } = p;
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
prop: "--__beam-bw1",
|
|
41
|
+
a: 1 - sp,
|
|
42
|
+
b: 1 + sp * 1.1,
|
|
43
|
+
period: ss * .9,
|
|
44
|
+
delay: 0,
|
|
45
|
+
unit: ""
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
prop: "--__beam-bh1",
|
|
49
|
+
a: 1 + sp * .9,
|
|
50
|
+
b: 1 - sp * .85,
|
|
51
|
+
period: ss * 1.26,
|
|
52
|
+
delay: 0,
|
|
53
|
+
unit: ""
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
prop: "--__beam-bx1",
|
|
57
|
+
a: -dr,
|
|
58
|
+
b: dr * .9,
|
|
59
|
+
period: bs * 1.6,
|
|
60
|
+
delay: 0,
|
|
61
|
+
unit: "px"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
prop: "--__beam-by1",
|
|
65
|
+
a: dr * .55,
|
|
66
|
+
b: -dr * .7,
|
|
67
|
+
period: bs * 1.6,
|
|
68
|
+
delay: 0,
|
|
69
|
+
unit: "px"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
prop: "--__beam-bw2",
|
|
73
|
+
a: 1 + sp,
|
|
74
|
+
b: 1 - sp * .85,
|
|
75
|
+
period: ss * 1.1,
|
|
76
|
+
delay: 0,
|
|
77
|
+
unit: ""
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
prop: "--__beam-bh2",
|
|
81
|
+
a: 1 - sp * .8,
|
|
82
|
+
b: 1 + sp * 1.05,
|
|
83
|
+
period: ss * .81,
|
|
84
|
+
delay: 0,
|
|
85
|
+
unit: ""
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
prop: "--__beam-bx2",
|
|
89
|
+
a: dr * .8,
|
|
90
|
+
b: -dr * .9,
|
|
91
|
+
period: bs * 1.88,
|
|
92
|
+
delay: 0,
|
|
93
|
+
unit: "px"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
prop: "--__beam-by2",
|
|
97
|
+
a: -dr,
|
|
98
|
+
b: dr * .65,
|
|
99
|
+
period: bs * 1.88,
|
|
100
|
+
delay: 0,
|
|
101
|
+
unit: "px"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
prop: "--__beam-bw3",
|
|
105
|
+
a: 1 - sp * .6,
|
|
106
|
+
b: 1 + sp * 1.15,
|
|
107
|
+
period: ss * .98,
|
|
108
|
+
delay: 0,
|
|
109
|
+
unit: ""
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
prop: "--__beam-bh3",
|
|
113
|
+
a: 1 + sp * .75,
|
|
114
|
+
b: 1 - sp,
|
|
115
|
+
period: ss * 1.4,
|
|
116
|
+
delay: 0,
|
|
117
|
+
unit: ""
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
prop: "--__beam-bx3",
|
|
121
|
+
a: -dr * .6,
|
|
122
|
+
b: dr,
|
|
123
|
+
period: bs * 1.45,
|
|
124
|
+
delay: 0,
|
|
125
|
+
unit: "px"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
prop: "--__beam-by3",
|
|
129
|
+
a: -dr * .85,
|
|
130
|
+
b: dr * .45,
|
|
131
|
+
period: bs * 1.45,
|
|
132
|
+
delay: 0,
|
|
133
|
+
unit: "px"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
prop: "--__beam-bgh",
|
|
137
|
+
a: 1 - gh,
|
|
138
|
+
b: 1 + gh,
|
|
139
|
+
period: ghs,
|
|
140
|
+
delay: 0,
|
|
141
|
+
unit: ""
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
prop: "--__beam-bop-tl",
|
|
145
|
+
a: 1 - op,
|
|
146
|
+
b: 1,
|
|
147
|
+
period: bs,
|
|
148
|
+
delay: 0,
|
|
149
|
+
unit: ""
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
prop: "--__beam-bop-tr",
|
|
153
|
+
a: 1 - op,
|
|
154
|
+
b: 1,
|
|
155
|
+
period: bs * 1.32,
|
|
156
|
+
delay: bs * .28,
|
|
157
|
+
unit: ""
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
prop: "--__beam-bop-bl",
|
|
161
|
+
a: 1 - op,
|
|
162
|
+
b: 1,
|
|
163
|
+
period: bs * .84,
|
|
164
|
+
delay: bs * .55,
|
|
165
|
+
unit: ""
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
prop: "--__beam-bop-br",
|
|
169
|
+
a: 1 - op,
|
|
170
|
+
b: 1,
|
|
171
|
+
period: bs * 1.58,
|
|
172
|
+
delay: bs * .83,
|
|
173
|
+
unit: ""
|
|
174
|
+
}
|
|
175
|
+
];
|
|
176
|
+
};
|
|
177
|
+
var getBeamPulseDriverConfig = (variant, durationSec, staticColors) => {
|
|
178
|
+
const p = pulseParams(variant, durationSec);
|
|
179
|
+
return {
|
|
180
|
+
oscillators: oscillatorDefs(p),
|
|
181
|
+
hue: staticColors ? null : {
|
|
182
|
+
prop: "--vui-beam-hue",
|
|
183
|
+
range: 360,
|
|
184
|
+
period: p.huePeriod,
|
|
185
|
+
continuous: true
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
var frame = (ts) => {
|
|
190
|
+
rafId = requestAnimationFrame(frame);
|
|
191
|
+
if (typeof document !== "undefined" && document.hidden) return;
|
|
192
|
+
if (ts - lastFrame < FRAME_INTERVAL) return;
|
|
193
|
+
lastFrame = ts;
|
|
194
|
+
const tSec = ts / 1e3;
|
|
195
|
+
for (const { el, config } of instances) {
|
|
196
|
+
for (const osc of config.oscillators) {
|
|
197
|
+
const phase = (tSec - osc.delay) / osc.period;
|
|
198
|
+
const value = osc.a + (osc.b - osc.a) * beamPulsePingPong(phase);
|
|
199
|
+
el.style.setProperty(osc.prop, osc.unit === "px" ? `${value.toFixed(2)}px` : value.toFixed(4));
|
|
200
|
+
}
|
|
201
|
+
if (config.hue) {
|
|
202
|
+
const { prop, range, period, continuous } = config.hue;
|
|
203
|
+
const value = continuous ? tSec / period % 1 * range : -range + 2 * range * beamPulsePingPong(tSec / period);
|
|
204
|
+
el.style.setProperty(prop, `${value.toFixed(2)}deg`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
var startLoop = () => {
|
|
209
|
+
if (rafId == null) {
|
|
210
|
+
lastFrame = 0;
|
|
211
|
+
rafId = requestAnimationFrame(frame);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
var stopLoopIfIdle = () => {
|
|
215
|
+
if (instances.size === 0 && rafId != null) {
|
|
216
|
+
cancelAnimationFrame(rafId);
|
|
217
|
+
rafId = null;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* Register an element on the shared pulse loop.
|
|
222
|
+
* @returns cleanup that unregisters (and stops the loop when empty).
|
|
223
|
+
*/
|
|
224
|
+
var noopCleanup = () => void 0;
|
|
225
|
+
var registerBeamPulse = (el, config) => {
|
|
226
|
+
if (prefersReducedMotion()) return noopCleanup;
|
|
227
|
+
const instance = {
|
|
228
|
+
el,
|
|
229
|
+
config
|
|
230
|
+
};
|
|
231
|
+
instances.add(instance);
|
|
232
|
+
startLoop();
|
|
233
|
+
return () => {
|
|
234
|
+
instances.delete(instance);
|
|
235
|
+
stopLoopIfIdle();
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
//#endregion
|
|
239
|
+
export { beamPulsePingPong, getBeamPulseDriverConfig, registerBeamPulse };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-beam-angle{syntax:"<angle>";inherits:true;initial-value:0deg}@property --vui-beam-opacity{syntax:"<number>";inherits:true;initial-value:0}@property --vui-beam-hue{syntax:"<angle>";inherits:true;initial-value:0deg}@property --vui-beam-hue-base{syntax:"<angle>";inherits:true;initial-value:0deg}@property --vui-beam-hue-range{syntax:"<angle>";inherits:true;initial-value:30deg}@property --vui-beam-duration{syntax:"<time>";inherits:true;initial-value:1.96s}@property --vui-beam-strength{syntax:"<number>";inherits:true;initial-value:1}@property --vui-beam-brightness{syntax:"<number>";inherits:true;initial-value:1.3}@property --vui-beam-saturation{syntax:"<number>";inherits:true;initial-value:1.2}@property --vui-beam-color{syntax:"<color>";inherits:true;initial-value:#0000}@property --vui-beam-stroke-opacity{syntax:"<number>";inherits:true;initial-value:.26}@property --vui-beam-inner-opacity{syntax:"<number>";inherits:true;initial-value:.42}@property --vui-beam-bloom-opacity{syntax:"<number>";inherits:true;initial-value:.24}@property --vui-beam-core-blur{syntax:"<length>";inherits:true;initial-value:3px}@property --vui-beam-bloom-blur{syntax:"<length>";inherits:true;initial-value:22.5px}@property --vui-beam-pulse-glow-sx{syntax:"<number>";inherits:true;initial-value:1}@property --vui-beam-pulse-glow-sy{syntax:"<number>";inherits:true;initial-value:1}@property --vui-beam-pulse-glow-boost{syntax:"<number>";inherits:true;initial-value:1}@property --vui-beam-x{syntax:"<number>";inherits:true;initial-value:0}@property --vui-beam-w{syntax:"<number>";inherits:true;initial-value:1}@property --vui-beam-h{syntax:"<number>";inherits:true;initial-value:1}@property --vui-beam-spike{syntax:"<number>";inherits:true;initial-value:1}@property --vui-beam-spike2{syntax:"<number>";inherits:true;initial-value:1}@property --vui-beam-edge{syntax:"<number>";inherits:true;initial-value:1}@property --__beam-bw1{syntax:"<number>";inherits:true;initial-value:1}@property --__beam-bh1{syntax:"<number>";inherits:true;initial-value:1}@property --__beam-bw2{syntax:"<number>";inherits:true;initial-value:1}@property --__beam-bh2{syntax:"<number>";inherits:true;initial-value:1}@property --__beam-bw3{syntax:"<number>";inherits:true;initial-value:1}@property --__beam-bh3{syntax:"<number>";inherits:true;initial-value:1}@property --__beam-bgh{syntax:"<number>";inherits:true;initial-value:1}@property --__beam-bop-tl{syntax:"<number>";inherits:true;initial-value:1}@property --__beam-bop-tr{syntax:"<number>";inherits:true;initial-value:1}@property --__beam-bop-bl{syntax:"<number>";inherits:true;initial-value:1}@property --__beam-bop-br{syntax:"<number>";inherits:true;initial-value:1}@property --__beam-bx1{syntax:"<length>";inherits:true;initial-value:0}@property --__beam-by1{syntax:"<length>";inherits:true;initial-value:0}@property --__beam-bx2{syntax:"<length>";inherits:true;initial-value:0}@property --__beam-by2{syntax:"<length>";inherits:true;initial-value:0}@property --__beam-bx3{syntax:"<length>";inherits:true;initial-value:0}@property --__beam-by3{syntax:"<length>";inherits:true;initial-value:0}@property --__beam-radius-tl{syntax:"<length-percentage>";inherits:true;initial-value:0}@property --__beam-radius-tr{syntax:"<length-percentage>";inherits:true;initial-value:0}@property --__beam-radius-br{syntax:"<length-percentage>";inherits:true;initial-value:0}@property --__beam-radius-bl{syntax:"<length-percentage>";inherits:true;initial-value:0}@property --__beam-c1{syntax:"<color>";inherits:true;initial-value:#0000}@property --__beam-c2{syntax:"<color>";inherits:true;initial-value:#0000}@property --__beam-c3{syntax:"<color>";inherits:true;initial-value:#0000}@property --__beam-c4{syntax:"<color>";inherits:true;initial-value:#0000}@property --__beam-c5{syntax:"<color>";inherits:true;initial-value:#0000}@property --__beam-c6{syntax:"<color>";inherits:true;initial-value:#0000}@property --__beam-c7{syntax:"<color>";inherits:true;initial-value:#0000}@property --__beam-c8{syntax:"<color>";inherits:true;initial-value:#0000}@property --__beam-c9{syntax:"<color>";inherits:true;initial-value:#0000}@property --__beam-c10{syntax:"<color>";inherits:true;initial-value:#0000}@property --__beam-c11{syntax:"<color>";inherits:true;initial-value:#0000}@property --__beam-c12{syntax:"<color>";inherits:true;initial-value:#0000}.viraui__beam-module__Beam_xT5Pt{--__beam-radius-tl:var(--vui-surface-border-radius-top-left);--__beam-radius-tr:var(--vui-surface-border-radius-top-right);--__beam-radius-br:var(--vui-surface-border-radius-bottom-right);--__beam-radius-bl:var(--vui-surface-border-radius-bottom-left);--vui-beam-color:var(--global-primary);--__beam-c1:var(--vui-beam-color);--__beam-c2:var(--vui-beam-color);--__beam-c3:var(--vui-beam-color);--__beam-c4:var(--vui-beam-color);--__beam-c5:var(--vui-beam-color);--__beam-c6:var(--vui-beam-color);--__beam-c7:var(--vui-beam-color);--__beam-c8:var(--vui-beam-color);--__beam-c9:var(--vui-beam-color);--__beam-c10:var(--vui-beam-color);--__beam-c11:var(--vui-beam-color);--__beam-c12:var(--vui-beam-color);position:relative;z-index:0;&[data-paused],&[data-paused] [data-beam-bloom],&[data-paused]:after,&[data-paused]:before{animation-play-state:paused!important}&[data-beam-fit-content=true]{max-width:fit-content}&[data-beam-color=colorful]{--__beam-c1:#ff3264;--__beam-c2:#288cff;--__beam-c3:#32c850;--__beam-c4:#1eb9aa;--__beam-c5:#6446ff;--__beam-c6:#ff7828;--__beam-c7:#f032b4;--__beam-c8:#b428f0;--__beam-c9:#ff3c50;--__beam-c10:#ff3c50;--__beam-c11:#28beb4;--__beam-c12:#28beb4fa}&[data-beam-static-colors=true]{--vui-beam-hue-range:0deg}&[data-beam-static-colors=true] [data-beam-bloom],&[data-beam-static-colors=true]:after,&[data-beam-static-colors=true]:before{animation-name:none;filter:brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}@media (prefers-reduced-motion:reduce){&[data-animate],&[data-fading]{animation:none!important}& [data-beam-bloom],&:after,&:before{animation:none!important;filter:none!important}&[data-animate]:after,&[data-animate]:before{opacity:calc(.35 * var(--vui-beam-strength))}}&[data-beam-variant=border]{&[data-beam-size=medium]{border-radius:var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl);overflow:hidden;&[data-animate]{animation:viraui__beam-module__vui-beam-spin_PCRBV var(--vui-beam-duration) linear infinite,viraui__beam-module__vui-beam-fade-in_-pZOw .6s ease forwards}&[data-fading]{animation:viraui__beam-module__vui-beam-spin_PCRBV var(--vui-beam-duration) linear infinite,viraui__beam-module__vui-beam-fade-out_ITNKo .5s ease forwards}[data-beam-bloom]{display:none;position:absolute;inset:0;border-radius:max(0px,calc(var(--__beam-radius-tl) - 1px)) max(0px,calc(var(--__beam-radius-tr) - 1px)) max(0px,calc(var(--__beam-radius-br) - 1px)) max(0px,calc(var(--__beam-radius-bl) - 1px));clip-path:inset(0 round var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl));background:conic-gradient(from var(--vui-beam-angle),#0000 0,#0000 58%,#ffffff08 62%,#ffffff14 65%,#fff3 67%,#ffffff73 69%,#ffffffd9 70%,#ffffffd9 70.5%,#ffffff73 71.5%,#fff3 73%,#ffffff14 75%,#ffffff08 78%,#0000 82%);mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask-composite:exclude;padding:1px;filter:blur(8px) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation));pointer-events:none;z-index:3;opacity:0}&:is([data-animate],[data-fading]){&:after{border-radius:max(0px,calc(var(--__beam-radius-tl) - 1px)) max(0px,calc(var(--__beam-radius-tr) - 1px)) max(0px,calc(var(--__beam-radius-br) - 1px)) max(0px,calc(var(--__beam-radius-bl) - 1px));padding:1px;background:conic-gradient(from var(--vui-beam-angle),#0000 0,#0000 54%,#ffffff1a 57%,#ffffff4d 60%,#fff9 63%,#ffffffbf 66%,#fff9 69%,#ffffff4d 72%,#ffffff1a 75%,#0000 78%,#0000 100%),radial-gradient(ellipse 70px 40px at 33% -7.4%,var(--__beam-c1),#0000),radial-gradient(ellipse 60px 35px at 12% -5%,var(--__beam-c2),#0000),radial-gradient(ellipse 40px 70px at 2.1% 68.3%,var(--__beam-c3),#0000),radial-gradient(ellipse 20px 35px at 2.1% 68.3%,var(--__beam-c4),#0000),radial-gradient(ellipse 180px 32px at 74.4% 100%,var(--__beam-c5),#0000),radial-gradient(ellipse 85px 26px at 55% 100%,var(--__beam-c2),#0000),radial-gradient(ellipse 74px 32px at 93.9% 0,var(--__beam-c6),#0000),radial-gradient(ellipse 26px 42px at 100% 27.1%,var(--__beam-c7),#0000),radial-gradient(ellipse 52px 48px at 100% 27.1%,var(--__beam-c8),#0000);mask:conic-gradient(from var(--vui-beam-angle),#0000 0,#0000 30%,#ffffff1a 36%,#ffffff59 44%,#fff 52%,#fff 80%,#ffffff59 86%,#ffffff1a 92%,#0000 95%,#0000 100%),linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask-composite:intersect,exclude;z-index:2;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-stroke-opacity) * var(--vui-beam-strength))}&:after,&:before{content:"";position:absolute;inset:0;clip-path:inset(0 round var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl));pointer-events:none;animation:viraui__beam-module__vui-beam-hue-shift_9CzG5 12s ease-in-out infinite}&:before{border-radius:var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl);background:radial-gradient(ellipse 63px 36px at 33% -7.4%,oklab(from var(--__beam-c1) l a b/.45),#0000),radial-gradient(ellipse 54px 32px at 12% -5%,oklab(from var(--__beam-c2) l a b/.45),#0000),radial-gradient(ellipse 36px 63px at 2.1% 68.3%,oklab(from var(--__beam-c3) l a b/.45),#0000),radial-gradient(ellipse 18px 32px at 2.1% 68.3%,oklab(from var(--__beam-c4) l a b/.45),#0000),radial-gradient(ellipse 162px 29px at 74.4% 100%,oklab(from var(--__beam-c5) l a b/.45),#0000),radial-gradient(ellipse 77px 23px at 55% 100%,oklab(from var(--__beam-c2) l a b/.45),#0000),radial-gradient(ellipse 67px 29px at 93.9% 0,oklab(from var(--__beam-c6) l a b/.45),#0000),radial-gradient(ellipse 23px 38px at 100% 27.1%,oklab(from var(--__beam-c7) l a b/.45),#0000),radial-gradient(ellipse 47px 43px at 100% 27.1%,oklab(from var(--__beam-c8) l a b/.45),#0000);box-shadow:inset 0 0 9px 1px #ffffff45;mask-image:conic-gradient(from var(--vui-beam-angle),#0000 0,#0000 30%,#ffffff1a 36%,#ffffff59 44%,#fff 52%,#fff 80%,#ffffff59 86%,#ffffff1a 92%,#0000 95%,#0000 100%),linear-gradient(#fff,#0000 28px,#0000 calc(100% - 28px),#fff),linear-gradient(to right,#fff,#0000 28px,#0000 calc(100% - 28px),#fff);mask-composite:intersect,add;z-index:1;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-inner-opacity) * var(--vui-beam-strength))}[data-beam-bloom]{display:block;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-bloom-opacity) * var(--vui-beam-strength))}}}&[data-beam-size=small]{--vui-beam-stroke-opacity:0.46;--vui-beam-inner-opacity:0.24;--vui-beam-bloom-opacity:0.38;border-radius:var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl);overflow:hidden;&[data-animate]{animation:viraui__beam-module__vui-beam-spin_PCRBV var(--vui-beam-duration) linear infinite,viraui__beam-module__vui-beam-fade-in_-pZOw .6s ease forwards}&[data-fading]{animation:viraui__beam-module__vui-beam-spin_PCRBV var(--vui-beam-duration) linear infinite,viraui__beam-module__vui-beam-fade-out_ITNKo .5s ease forwards}[data-beam-bloom]{display:none;position:absolute;inset:0;border-radius:max(0px,calc(var(--__beam-radius-tl) - 1px)) max(0px,calc(var(--__beam-radius-tr) - 1px)) max(0px,calc(var(--__beam-radius-br) - 1px)) max(0px,calc(var(--__beam-radius-bl) - 1px));clip-path:inset(0 round var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl));background:conic-gradient(from var(--vui-beam-angle),#0000 0,#0000 58%,#ffffff08 62%,#ffffff14 65%,#fff3 67%,#ffffff73 69%,#ffffffd9 70%,#ffffffd9 70.5%,#ffffff73 71.5%,#fff3 73%,#ffffff14 75%,#ffffff08 78%,#0000 82%);mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask-composite:exclude;padding:1px;filter:blur(8px) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation));pointer-events:none;z-index:3;opacity:0}&:is([data-animate],[data-fading]){&:after{border-radius:max(0px,calc(var(--__beam-radius-tl) - 1px)) max(0px,calc(var(--__beam-radius-tr) - 1px)) max(0px,calc(var(--__beam-radius-br) - 1px)) max(0px,calc(var(--__beam-radius-bl) - 1px));padding:1px;background:conic-gradient(from var(--vui-beam-angle),#0000 0,#0000 54%,#ffffff1a 57%,#ffffff4d 60%,#fff9 63%,#ffffffbf 66%,#fff9 69%,#ffffff4d 72%,#ffffff1a 75%,#0000 78%,#0000 100%),radial-gradient(ellipse 9px 18px at 2% 68%,var(--__beam-c3),#0000),radial-gradient(ellipse 4px 8px at 2% 68%,var(--__beam-c4),#0000),radial-gradient(ellipse 59px 9px at 72% -3%,var(--__beam-c6),#0000),radial-gradient(ellipse 42px 7px at 74% 100%,var(--__beam-c5),#0000),radial-gradient(ellipse 10px 17px at 100% 27%,var(--__beam-c7),#0000),radial-gradient(ellipse 10px 18px at 100% 27%,var(--__beam-c8),#0000),radial-gradient(ellipse 5px 10px at 100% 27%,var(--__beam-c2),#0000),radial-gradient(ellipse 11px 12px at 100% 27%,var(--__beam-c1),#0000);mask:conic-gradient(from var(--vui-beam-angle),#0000 0,#0000 30%,#ffffff1a 36%,#ffffff59 44%,#fff 52%,#fff 80%,#ffffff59 86%,#ffffff1a 92%,#0000 95%,#0000 100%),linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask-composite:intersect,exclude;z-index:2;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-stroke-opacity) * var(--vui-beam-strength))}&:after,&:before{content:"";position:absolute;inset:0;clip-path:inset(0 round var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl));pointer-events:none;animation:viraui__beam-module__vui-beam-hue-shift_9CzG5 12s ease-in-out infinite}&:before{border-radius:var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl);background:radial-gradient(ellipse 9px 18px at 2% 68%,oklab(from var(--__beam-c3) l a b/.5),#0000),radial-gradient(ellipse 4px 8px at 2% 68%,oklab(from var(--__beam-c4) l a b/.45),#0000),radial-gradient(ellipse 59px 9px at 72% -3%,oklab(from var(--__beam-c6) l a b/.35),#0000),radial-gradient(ellipse 42px 7px at 74% 100%,oklab(from var(--__beam-c5) l a b/.35),#0000),radial-gradient(ellipse 10px 17px at 100% 27%,oklab(from var(--__beam-c7) l a b/.3),#0000),radial-gradient(ellipse 10px 18px at 100% 27%,oklab(from var(--__beam-c8) l a b/.4),#0000),radial-gradient(ellipse 5px 10px at 100% 27%,oklab(from var(--__beam-c2) l a b/.3),#0000),radial-gradient(ellipse 11px 12px at 100% 27%,oklab(from var(--__beam-c1) l a b/.3),#0000);box-shadow:inset 0 0 5px 1px #ffffff45;mask-image:conic-gradient(from var(--vui-beam-angle),#0000 0,#0000 22%,#ffffff1f 28%,#fff6 36%,#fff 46%,#fff 82%,#fff6 88%,#ffffff1f 94%,#0000 97%,#0000 100%);mask-composite:add;z-index:1;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-inner-opacity) * var(--vui-beam-strength))}[data-beam-bloom]{display:block;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-bloom-opacity) * var(--vui-beam-strength))}}}}&[data-beam-variant=line]{--vui-beam-stroke-opacity:1.14;--vui-beam-inner-opacity:0.7;--vui-beam-bloom-opacity:0.8;border-radius:var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl);overflow:hidden;&[data-animate]{animation:viraui__beam-module__beam-travel_FuPkI var(--vui-beam-duration) linear infinite,viraui__beam-module__beam-edge-fade_PXYJA var(--vui-beam-duration) linear infinite,viraui__beam-module__beam-breathe_3BBMd 4s ease-in-out infinite,viraui__beam-module__beam-spike_1iIfV 4.1s ease-in-out infinite,viraui__beam-module__beam-spike2_d1gJy 5.3s ease-in-out infinite,viraui__beam-module__vui-beam-fade-in_-pZOw .6s ease forwards}&[data-fading]{animation:viraui__beam-module__beam-travel_FuPkI var(--vui-beam-duration) linear infinite,viraui__beam-module__beam-edge-fade_PXYJA var(--vui-beam-duration) linear infinite,viraui__beam-module__beam-breathe_3BBMd 4s ease-in-out infinite,viraui__beam-module__beam-spike_1iIfV 4.1s ease-in-out infinite,viraui__beam-module__beam-spike2_d1gJy 5.3s ease-in-out infinite,viraui__beam-module__vui-beam-fade-out_ITNKo .5s ease forwards}[data-beam-bloom]{display:none;position:absolute;inset:0;border-radius:max(0px,calc(var(--__beam-radius-tl) - 1px)) max(0px,calc(var(--__beam-radius-tr) - 1px)) max(0px,calc(var(--__beam-radius-br) - 1px)) max(0px,calc(var(--__beam-radius-bl) - 1px));clip-path:inset(0 round var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl));padding:0;mask:radial-gradient(ellipse calc(84px * var(--vui-beam-w)) calc(110px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100%) 100%,#fff 0,#ffffff80 35%,#0000 100%);mask-composite:add;background:radial-gradient(ellipse calc(.8px * var(--vui-beam-spike)) calc(92px * var(--vui-beam-h)) at 8% calc(100% - 2px),var(--__beam-c9),var(--__beam-c9) 30%,#0000 88%),radial-gradient(ellipse calc(10px * var(--vui-beam-spike2)) calc(35px * var(--vui-beam-h)) at 22% calc(100% - 4px),color-mix(in srgb,var(--__beam-c12) 98%,#0000),color-mix(in srgb,var(--__beam-c12) 49%,#0000) 50%,#0000 95%),radial-gradient(ellipse calc(2px * (2 - var(--vui-beam-spike))) calc(72px * var(--vui-beam-h)) at 36% calc(100% - 3px),var(--__beam-c5),color-mix(in srgb,var(--__beam-c5) 100%,#0000) 40%,#0000 90%),radial-gradient(ellipse calc(14px * var(--vui-beam-spike2)) calc(28px * var(--vui-beam-h)) at 50% calc(100% - 2px),#ffaa2896,#ffaa284a 55%,#0000 96%),radial-gradient(ellipse calc(1.2px * (2 - var(--vui-beam-spike2))) calc(85px * var(--vui-beam-h)) at 64% calc(100% - 4px),var(--__beam-c3),color-mix(in srgb,var(--__beam-c3) 100%,#0000) 35%,#0000 89%),radial-gradient(ellipse calc(7px * var(--vui-beam-spike)) calc(45px * var(--vui-beam-h)) at 78% calc(100% - 2px),color-mix(in srgb,var(--__beam-c8) 91%,#0000),color-mix(in srgb,var(--__beam-c8) 45%,#0000) 48%,#0000 94%),radial-gradient(ellipse calc(.6px * (2 - var(--vui-beam-spike))) calc(60px * var(--vui-beam-h)) at 92% calc(100% - 3px),var(--__beam-c2),color-mix(in srgb,var(--__beam-c2) 100%,#0000) 42%,#0000 91%),radial-gradient(ellipse calc(21px * var(--vui-beam-spike)) calc(15px * var(--vui-beam-spike2)) at calc(var(--vui-beam-x) * 100%) calc(100% + 1px),#fff 0,#ffffffe6 20%,#ffffff80 50%,#0000 100%),radial-gradient(ellipse calc(42px * var(--vui-beam-w)) calc(40px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100%) 100%,#ffffff4d 0,#ffffff1f 25%,#ffffff08 55%,#0000 80%);pointer-events:none;z-index:3;opacity:0}&:is([data-animate],[data-fading]){&:after{border-radius:max(0px,calc(var(--__beam-radius-tl) - 1px)) max(0px,calc(var(--__beam-radius-tr) - 1px)) max(0px,calc(var(--__beam-radius-br) - 1px)) max(0px,calc(var(--__beam-radius-bl) - 1px));padding:1px;background:radial-gradient(ellipse calc(24px * var(--vui-beam-w)) calc(28px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100%) calc(100% + 2px),#ffffff61 0,#ffffff1f 30%,#0000 65%),radial-gradient(ellipse calc(36px * var(--vui-beam-w)) calc(36px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100%) calc(100% + 2px),var(--__beam-c1),#0000),radial-gradient(ellipse calc(30px * var(--vui-beam-w)) calc(32px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% + 39px) calc(100%),var(--__beam-c4),#0000),radial-gradient(ellipse calc(33px * var(--vui-beam-w)) calc(28px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% - 36px) calc(100% + 2px),var(--__beam-c3),#0000),radial-gradient(ellipse calc(29px * var(--vui-beam-w)) calc(34px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% - 54px) calc(100%),var(--__beam-c8),#0000),radial-gradient(ellipse calc(27px * var(--vui-beam-w)) calc(30px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% + 51px) calc(100% - 1px),var(--__beam-c6),#0000),radial-gradient(ellipse calc(36px * var(--vui-beam-w)) calc(24px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% + 21px) calc(100% + 1px),var(--__beam-c5),#0000),radial-gradient(ellipse calc(30px * var(--vui-beam-w)) calc(22px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% - 21px) calc(100%),var(--__beam-c2),#0000),radial-gradient(ellipse calc(25px * var(--vui-beam-w)) calc(28px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% + 66px) calc(100% + 1px),var(--__beam-c7),#0000),radial-gradient(ellipse calc(23px * var(--vui-beam-w)) calc(30px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% - 66px) calc(100% - 1px),var(--__beam-c4),#0000);mask:radial-gradient(ellipse calc(78px * var(--vui-beam-w)) calc(60px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100%) 100%,#fff 0,#ffffff80 45%,#0000 100%),linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask-composite:intersect,exclude;z-index:2;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-edge) * var(--vui-beam-stroke-opacity) * var(--vui-beam-strength))}&:after,&:before{content:"";position:absolute;inset:0;clip-path:inset(0 round var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl));pointer-events:none;animation:viraui__beam-module__vui-beam-hue-shift_9CzG5 12s ease-in-out infinite}&:before{border-radius:var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl);background:radial-gradient(ellipse calc(33px * var(--vui-beam-w)) calc(30px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100%) calc(100%),color-mix(in srgb,var(--__beam-c1) 48%,#0000),#0000),radial-gradient(ellipse calc(24px * var(--vui-beam-w)) calc(26px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% + 39px) calc(100% - 3px),color-mix(in srgb,var(--__beam-c4) 42%,#0000),#0000),radial-gradient(ellipse calc(27px * var(--vui-beam-w)) calc(24px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% - 36px) calc(100%),color-mix(in srgb,var(--__beam-c3) 48%,#0000),#0000),radial-gradient(ellipse calc(23px * var(--vui-beam-w)) calc(28px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% - 54px) calc(100% - 2px),color-mix(in srgb,var(--__beam-c8) 42%,#0000),#0000),radial-gradient(ellipse calc(24px * var(--vui-beam-w)) calc(24px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% + 51px) calc(100% - 1px),color-mix(in srgb,var(--__beam-c6) 50%,#0000),#0000),radial-gradient(ellipse calc(30px * var(--vui-beam-w)) calc(20px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% + 21px) calc(100%),color-mix(in srgb,var(--__beam-c5) 45%,#0000),#0000),radial-gradient(ellipse calc(25px * var(--vui-beam-w)) calc(18px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% - 21px) calc(100% - 2px),color-mix(in srgb,var(--__beam-c2) 40%,#0000),#0000),radial-gradient(ellipse calc(21px * var(--vui-beam-w)) calc(24px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% + 66px) calc(100%),color-mix(in srgb,var(--__beam-c7) 45%,#0000),#0000),radial-gradient(ellipse calc(18px * var(--vui-beam-w)) calc(26px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100% - 66px) calc(100% - 1px),color-mix(in srgb,var(--__beam-c4) 52%,#0000),#0000);box-shadow:inset 0 0 9px 1px #ffffff45;mask-image:radial-gradient(ellipse calc(78px * var(--vui-beam-w)) calc(60px * var(--vui-beam-h)) at calc(var(--vui-beam-x) * 100%) 100%,#fff 0,#ffffff80 45%,#0000 100%),linear-gradient(#fff,#0000 28px,#0000 calc(100% - 28px),#fff),linear-gradient(to right,#fff,#0000 28px,#0000 calc(100% - 28px),#fff);mask-composite:intersect,add;z-index:1;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-edge) * var(--vui-beam-inner-opacity) * var(--vui-beam-strength))}[data-beam-bloom]{display:block;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-edge) * var(--vui-beam-bloom-opacity) * var(--vui-beam-strength));animation:viraui__beam-module__beam-hue-shift-bloom_Onfv- 8s ease-in-out infinite}}}&[data-beam-variant=pulse-inner]{--vui-beam-stroke-opacity:1.54;--vui-beam-inner-opacity:0.44;--vui-beam-bloom-opacity:0.66;border-radius:var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl);overflow:hidden;isolation:isolate;&[data-animate]{animation:viraui__beam-module__vui-beam-fade-in_-pZOw .6s ease forwards}&[data-fading]{animation:viraui__beam-module__vui-beam-fade-out_ITNKo .5s ease forwards}[data-beam-bloom]{display:none;position:absolute;inset:0;border-radius:var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl);clip-path:inset(0 round var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl));background:radial-gradient(ellipse 84px 48px at 33% -7.4%,oklab(from var(--__beam-c1) l a b/.76),#0000),radial-gradient(ellipse 72px 42px at 12% -5%,oklab(from var(--__beam-c2) l a b/.76),#0000),radial-gradient(ellipse 48px 84px at 2.1% 68.3%,oklab(from var(--__beam-c3) l a b/.76),#0000),radial-gradient(ellipse 216px 38px at 74.4% 100%,oklab(from var(--__beam-c5) l a b/.76),#0000),radial-gradient(ellipse 102px 31px at 55% 100%,oklab(from var(--__beam-c2) l a b/.76),#0000),radial-gradient(ellipse 89px 38px at 93.9% 0,oklab(from var(--__beam-c6) l a b/.76),#0000),radial-gradient(ellipse 62px 58px at 100% 27.1%,oklab(from var(--__beam-c8) l a b/.76),#0000);mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask-composite:exclude;padding:1px;pointer-events:none;z-index:3;opacity:0}&:is([data-animate],[data-fading]){&:after{padding:1px;background:radial-gradient(ellipse calc(70px * var(--__beam-bw1)) calc(40px * var(--__beam-bh1) * var(--__beam-bgh)) at calc(33% + var(--__beam-bx1)) calc(-7.4% + var(--__beam-by1)),oklab(from var(--__beam-c1) l a b/var(--__beam-bop-tl)),#0000),radial-gradient(ellipse calc(60px * var(--__beam-bw2)) calc(35px * var(--__beam-bh2) * var(--__beam-bgh)) at calc(12% + var(--__beam-bx2)) calc(-5% + var(--__beam-by2)),oklab(from var(--__beam-c2) l a b/var(--__beam-bop-tl)),#0000),radial-gradient(ellipse calc(40px * var(--__beam-bw3)) calc(70px * var(--__beam-bh3) * var(--__beam-bgh)) at calc(2.1% + var(--__beam-bx3)) calc(68.3% + var(--__beam-by3)),oklab(from var(--__beam-c3) l a b/var(--__beam-bop-bl)),#0000),radial-gradient(ellipse calc(20px * var(--__beam-bw1)) calc(35px * var(--__beam-bh1) * var(--__beam-bgh)) at calc(2.1% + var(--__beam-bx1)) calc(68.3% + var(--__beam-by1)),oklab(from var(--__beam-c4) l a b/var(--__beam-bop-bl)),#0000),radial-gradient(ellipse calc(180px * var(--__beam-bw2)) calc(32px * var(--__beam-bh2) * var(--__beam-bgh)) at calc(74.4% + var(--__beam-bx2)) calc(100% + var(--__beam-by2)),oklab(from var(--__beam-c5) l a b/var(--__beam-bop-br)),#0000),radial-gradient(ellipse calc(85px * var(--__beam-bw3)) calc(26px * var(--__beam-bh3) * var(--__beam-bgh)) at calc(55% + var(--__beam-bx3)) calc(100% + var(--__beam-by3)),oklab(from var(--__beam-c2) l a b/var(--__beam-bop-br)),#0000),radial-gradient(ellipse calc(74px * var(--__beam-bw1)) calc(32px * var(--__beam-bh1) * var(--__beam-bgh)) at calc(93.9% + var(--__beam-bx1)) calc(0% + var(--__beam-by1)),oklab(from var(--__beam-c6) l a b/var(--__beam-bop-tr)),#0000),radial-gradient(ellipse calc(26px * var(--__beam-bw2)) calc(42px * var(--__beam-bh2) * var(--__beam-bgh)) at calc(100% + var(--__beam-bx2)) calc(27.1% + var(--__beam-by2)),oklab(from var(--__beam-c7) l a b/var(--__beam-bop-tr)),#0000),radial-gradient(ellipse calc(52px * var(--__beam-bw3)) calc(48px * var(--__beam-bh3) * var(--__beam-bgh)) at calc(100% + var(--__beam-bx3)) calc(27.1% + var(--__beam-by3)),oklab(from var(--__beam-c8) l a b/var(--__beam-bop-tr)),#0000);mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask-composite:exclude;z-index:2;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-stroke-opacity) * var(--vui-beam-strength))}&:after,&:before{content:"";position:absolute;inset:0;border-radius:var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl);clip-path:inset(0 round var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl));pointer-events:none;filter:hue-rotate(calc(var(--vui-beam-hue-base, 0deg) + var(--vui-beam-hue))) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}&:before{background:radial-gradient(ellipse calc(65px * var(--__beam-bw1)) calc(35px * var(--__beam-bh1) * var(--__beam-bgh)) at calc(33% + var(--__beam-bx1)) calc(-7.4% + var(--__beam-by1)),oklab(from var(--__beam-c1) l a b/var(--__beam-bop-tl)),#0000),radial-gradient(ellipse calc(55px * var(--__beam-bw2)) calc(30px * var(--__beam-bh2) * var(--__beam-bgh)) at calc(12% + var(--__beam-bx2)) calc(-5% + var(--__beam-by2)),oklab(from var(--__beam-c2) l a b/var(--__beam-bop-tl)),#0000),radial-gradient(ellipse calc(35px * var(--__beam-bw3)) calc(65px * var(--__beam-bh3) * var(--__beam-bgh)) at calc(2.1% + var(--__beam-bx3)) calc(68.3% + var(--__beam-by3)),oklab(from var(--__beam-c3) l a b/var(--__beam-bop-bl)),#0000),radial-gradient(ellipse calc(15px * var(--__beam-bw1)) calc(30px * var(--__beam-bh1) * var(--__beam-bgh)) at calc(2.1% + var(--__beam-bx1)) calc(68.3% + var(--__beam-by1)),oklab(from var(--__beam-c4) l a b/var(--__beam-bop-bl)),#0000),radial-gradient(ellipse calc(173px * var(--__beam-bw2)) calc(28px * var(--__beam-bh2) * var(--__beam-bgh)) at calc(74.4% + var(--__beam-bx2)) calc(100% + var(--__beam-by2)),oklab(from var(--__beam-c5) l a b/var(--__beam-bop-br)),#0000),radial-gradient(ellipse calc(80px * var(--__beam-bw3)) calc(22px * var(--__beam-bh3) * var(--__beam-bgh)) at calc(55% + var(--__beam-bx3)) calc(100% + var(--__beam-by3)),oklab(from var(--__beam-c2) l a b/var(--__beam-bop-br)),#0000),radial-gradient(ellipse calc(69px * var(--__beam-bw1)) calc(28px * var(--__beam-bh1) * var(--__beam-bgh)) at calc(93.9% + var(--__beam-bx1)) calc(0% + var(--__beam-by1)),oklab(from var(--__beam-c6) l a b/var(--__beam-bop-tr)),#0000),radial-gradient(ellipse calc(22px * var(--__beam-bw2)) calc(38px * var(--__beam-bh2) * var(--__beam-bgh)) at calc(100% + var(--__beam-bx2)) calc(27.1% + var(--__beam-by2)),oklab(from var(--__beam-c7) l a b/var(--__beam-bop-tr)),#0000),radial-gradient(ellipse calc(47px * var(--__beam-bw3)) calc(44px * var(--__beam-bh3) * var(--__beam-bgh)) at calc(100% + var(--__beam-bx3)) calc(27.1% + var(--__beam-by3)),oklab(from var(--__beam-c8) l a b/var(--__beam-bop-tr)),#0000),radial-gradient(ellipse 60px 60px at 0 0,rgba(255,255,255,calc(.18 * var(--__beam-bop-tl))),#0000 70%),radial-gradient(ellipse 60px 60px at 100% 0,rgba(255,255,255,calc(.18 * var(--__beam-bop-tr))),#0000 70%),radial-gradient(ellipse 60px 60px at 0 100%,rgba(255,255,255,calc(.18 * var(--__beam-bop-bl))),#0000 70%),radial-gradient(ellipse 60px 60px at 100% 100%,rgba(255,255,255,calc(.18 * var(--__beam-bop-br))),#0000 70%);mask-image:linear-gradient(#fff,#0000 28px,#0000 calc(100% - 28px),#fff),linear-gradient(90deg,#fff,#0000 28px,#0000 calc(100% - 28px),#fff);mask-composite:add;z-index:1;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-inner-opacity) * var(--vui-beam-strength))}[data-beam-bloom]{display:block;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-bloom-opacity) * var(--vui-beam-strength));filter:blur(8px) hue-rotate(calc(var(--vui-beam-hue-base, 0deg) + var(--vui-beam-hue))) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}}@media (prefers-reduced-motion:reduce){&:is([data-animate],[data-fading]),&:is([data-animate],[data-fading]) [data-beam-bloom],&:is([data-animate],[data-fading]):after,&:is([data-animate],[data-fading]):before{animation:none!important}}}&[data-beam-variant=pulse-outside]{--vui-beam-stroke-opacity:0.94;--vui-beam-inner-opacity:0.34;--vui-beam-bloom-opacity:0.3;border-radius:var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl);overflow:visible;isolation:isolate;&>:not([data-beam-bloom]){position:relative;z-index:1}&[data-animate]{animation:viraui__beam-module__vui-beam-fade-in_-pZOw .6s ease forwards}&[data-fading]{animation:viraui__beam-module__vui-beam-fade-out_ITNKo .5s ease forwards}[data-beam-bloom]{display:none;position:absolute;inset:-30px;z-index:-1;border-radius:calc(var(--__beam-radius-tl) + 30px) calc(var(--__beam-radius-tr) + 30px) calc(var(--__beam-radius-br) + 30px) calc(var(--__beam-radius-bl) + 30px);background:radial-gradient(ellipse calc(110px * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(30px * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at 27% 3%,color-mix(in srgb,var(--__beam-c1) 77%,#0000),#0000),radial-gradient(ellipse calc(100px * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(20px * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at 73% 1%,color-mix(in srgb,var(--__beam-c6) 77%,#0000),#0000),radial-gradient(ellipse calc(26px * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(62px * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at 100% 33%,color-mix(in srgb,var(--__beam-c7) 77%,#0000),#0000),radial-gradient(ellipse calc(30px * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(56px * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at 101% 72%,color-mix(in srgb,var(--__beam-c8) 77%,#0000),#0000),radial-gradient(ellipse calc(120px * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(22px * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at 67% 99%,color-mix(in srgb,var(--__beam-c5) 77%,#0000),#0000),radial-gradient(ellipse calc(88px * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(32px * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at 24% 99%,color-mix(in srgb,var(--__beam-c2) 77%,#0000),#0000),radial-gradient(ellipse calc(28px * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(58px * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at 0 60%,color-mix(in srgb,var(--__beam-c3) 77%,#0000),#0000);transform:scale(.95,.9);pointer-events:none;opacity:0}&:is([data-animate],[data-fading]){&:after{inset:0;border-radius:var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl);padding:1px;clip-path:inset(0 round var(--__beam-radius-tl) var(--__beam-radius-tr) var(--__beam-radius-br) var(--__beam-radius-bl));mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);mask-composite:exclude;z-index:2;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-stroke-opacity) * var(--vui-beam-strength));filter:hue-rotate(calc(var(--vui-beam-hue-base, 0deg) + var(--vui-beam-hue))) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}&:after,&:before{content:"";position:absolute;background:radial-gradient(ellipse calc(80px * var(--__beam-bw1) * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(19px * var(--__beam-bh1) * var(--__beam-bgh) * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at calc(27% + var(--__beam-bx1)) calc(0% + var(--__beam-by1)),oklab(from var(--__beam-c1) l a b/var(--__beam-bop-tl)),#0000),radial-gradient(ellipse calc(74px * var(--__beam-bw2) * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(11px * var(--__beam-bh2) * var(--__beam-bgh) * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at calc(73% + var(--__beam-bx2)) calc(-1% + var(--__beam-by2)),oklab(from var(--__beam-c6) l a b/var(--__beam-bop-tr)),#0000),radial-gradient(ellipse calc(15px * var(--__beam-bw3) * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(44px * var(--__beam-bh3) * var(--__beam-bgh) * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at calc(100% + var(--__beam-bx3)) calc(33% + var(--__beam-by3)),oklab(from var(--__beam-c7) l a b/var(--__beam-bop-tr)),#0000),radial-gradient(ellipse calc(19px * var(--__beam-bw1) * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(38px * var(--__beam-bh1) * var(--__beam-bgh) * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at calc(101% + var(--__beam-bx1)) calc(72% + var(--__beam-by1)),oklab(from var(--__beam-c8) l a b/var(--__beam-bop-br)),#0000),radial-gradient(ellipse calc(84px * var(--__beam-bw2) * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(13px * var(--__beam-bh2) * var(--__beam-bgh) * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at calc(67% + var(--__beam-bx2)) calc(100% + var(--__beam-by2)),oklab(from var(--__beam-c5) l a b/var(--__beam-bop-br)),#0000),radial-gradient(ellipse calc(60px * var(--__beam-bw3) * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(21px * var(--__beam-bh3) * var(--__beam-bgh) * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at calc(24% + var(--__beam-bx3)) calc(101% + var(--__beam-by3)),oklab(from var(--__beam-c2) l a b/var(--__beam-bop-bl)),#0000),radial-gradient(ellipse calc(17px * var(--__beam-bw1) * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(40px * var(--__beam-bh1) * var(--__beam-bgh) * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at calc(0% + var(--__beam-bx1)) calc(60% + var(--__beam-by1)),oklab(from var(--__beam-c3) l a b/var(--__beam-bop-bl)),#0000),radial-gradient(ellipse calc(13px * var(--__beam-bw2) * var(--vui-beam-pulse-glow-sx, 1) * var(--vui-beam-pulse-glow-boost, 1)) calc(32px * var(--__beam-bh2) * var(--__beam-bgh) * var(--vui-beam-pulse-glow-sy, 1) * var(--vui-beam-pulse-glow-boost, 1)) at calc(-1% + var(--__beam-bx2)) calc(28% + var(--__beam-by2)),oklab(from var(--__beam-c4) l a b/var(--__beam-bop-tl)),#0000);pointer-events:none}&:before{inset:-10px;z-index:-1;border-radius:calc(var(--__beam-radius-tl) + 10px) calc(var(--__beam-radius-tr) + 10px) calc(var(--__beam-radius-br) + 10px) calc(var(--__beam-radius-bl) + 10px);transform:scale(.95,.9);opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-inner-opacity) * var(--vui-beam-strength));filter:blur(var(--vui-beam-core-blur,3px)) hue-rotate(calc(var(--vui-beam-hue-base, 0deg) + var(--vui-beam-hue))) brightness(var(--vui-beam-brightness,1.3)) saturate(var(--vui-beam-saturation,1.2))}[data-beam-bloom]{display:block;opacity:calc(var(--vui-beam-opacity) * var(--vui-beam-bloom-opacity) * var(--vui-beam-strength));filter:blur(var(--vui-beam-bloom-blur,22.5px)) hue-rotate(calc(var(--vui-beam-hue-base, 0deg) + var(--vui-beam-hue))) brightness(var(--vui-beam-brightness,1.3)) saturate(var(--vui-beam-saturation,1.2))}}@media (prefers-reduced-motion:reduce){&:is([data-animate],[data-fading]),&:is([data-animate],[data-fading]) [data-beam-bloom],&:is([data-animate],[data-fading]):after,&:is([data-animate],[data-fading]):before{animation:none!important}}}&[data-beam-static-colors=true]:is([data-animate],[data-fading]):after,&[data-beam-static-colors=true]:is([data-animate],[data-fading]):before{animation-name:none;filter:brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}&[data-beam-static-colors=true]:is([data-animate],[data-fading]) [data-beam-bloom]{animation-name:none;filter:blur(8px) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}&[data-beam-variant=pulse-outside][data-beam-static-colors=true]:is([data-animate],[data-fading]):before{filter:blur(var(--vui-beam-core-blur,3px)) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}&[data-beam-variant=pulse-outside][data-beam-static-colors=true]:is([data-animate],[data-fading]) [data-beam-bloom]{filter:blur(var(--vui-beam-bloom-blur,22.5px)) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}&[data-beam-variant=pulse-inner][data-beam-static-colors=true]:is([data-animate],[data-fading]):after,&[data-beam-variant=pulse-inner][data-beam-static-colors=true]:is([data-animate],[data-fading]):before{filter:brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}&[data-beam-variant=pulse-inner][data-beam-static-colors=true]:is([data-animate],[data-fading]) [data-beam-bloom]{filter:blur(8px) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}}.viraui__beam-module__Bloom_XrnlV{pointer-events:none}@keyframes viraui__beam-module__vui-beam-spin_PCRBV{to{--vui-beam-angle:360deg}}@keyframes viraui__beam-module__vui-beam-fade-in_-pZOw{to{--vui-beam-opacity:1}}@keyframes viraui__beam-module__vui-beam-fade-out_ITNKo{0%{--vui-beam-opacity:1}to{--vui-beam-opacity:0}}@keyframes viraui__beam-module__vui-beam-hue-shift_9CzG5{0%,to{filter:hue-rotate(calc(var(--vui-beam-hue-base, 0deg) - var(--vui-beam-hue-range))) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}50%{filter:hue-rotate(calc(var(--vui-beam-hue-base, 0deg) + var(--vui-beam-hue-range))) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}}@keyframes viraui__beam-module__beam-travel_FuPkI{0%{--vui-beam-x:0.06;--vui-beam-w:0.5}10%{--vui-beam-x:0.15;--vui-beam-w:0.8}20%{--vui-beam-x:0.25;--vui-beam-w:1.1}30%{--vui-beam-x:0.35;--vui-beam-w:1.3}40%{--vui-beam-x:0.44;--vui-beam-w:1.45}50%{--vui-beam-x:0.5;--vui-beam-w:1.5}60%{--vui-beam-x:0.56;--vui-beam-w:1.45}70%{--vui-beam-x:0.65;--vui-beam-w:1.3}80%{--vui-beam-x:0.75;--vui-beam-w:1.1}90%{--vui-beam-x:0.85;--vui-beam-w:0.8}to{--vui-beam-x:0.94;--vui-beam-w:0.5}}@keyframes viraui__beam-module__beam-edge-fade_PXYJA{0%,12.5%,87.5%,to{--vui-beam-edge:0}32.5%,67.5%{--vui-beam-edge:1}}@keyframes viraui__beam-module__beam-breathe_3BBMd{0%,to{--vui-beam-h:0.8}25%{--vui-beam-h:1.25}55%{--vui-beam-h:0.85}80%{--vui-beam-h:1.3}}@keyframes viraui__beam-module__beam-spike_1iIfV{0%,to{--vui-beam-spike:0.8}25%{--vui-beam-spike:1.3}50%{--vui-beam-spike:0.9}75%{--vui-beam-spike:1.4}}@keyframes viraui__beam-module__beam-spike2_d1gJy{0%,to{--vui-beam-spike2:1.2}25%{--vui-beam-spike2:0.7}50%{--vui-beam-spike2:1.4}75%{--vui-beam-spike2:0.8}}@keyframes viraui__beam-module__beam-hue-shift-bloom_Onfv-{0%,to{filter:blur(8px) hue-rotate(calc(var(--vui-beam-hue-base, 0deg) - 40deg)) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}50%{filter:blur(8px) hue-rotate(calc(var(--vui-beam-hue-base, 0deg) + 40deg)) brightness(var(--vui-beam-brightness)) saturate(var(--vui-beam-saturation))}}}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { SurfaceProps } from '..';
|
|
2
|
+
import { ThemeTypes } from '@viraui/foundation/vira/types';
|
|
3
|
+
import type * as React from 'react';
|
|
4
|
+
export type BeamVariant = 'border' | 'line' | 'pulse-outside' | 'pulse-inner';
|
|
5
|
+
export type BeamSize = 'small' | 'medium';
|
|
6
|
+
export type BeamColor = 'primary' | ThemeTypes['highlight'] | 'colorful';
|
|
7
|
+
/**
|
|
8
|
+
* Public Beam props.
|
|
9
|
+
*
|
|
10
|
+
* Root is a transparent Surface shell that exposes only `radius`. Put the visual
|
|
11
|
+
* card as children (typically `Surface` with `radius="auto"`).
|
|
12
|
+
*
|
|
13
|
+
* @default Beam-owned props use the defaults below; `radius` keeps Surface defaults when omitted.
|
|
14
|
+
*/
|
|
15
|
+
export type BeamProps = React.ComponentProps<'div'> & Pick<SurfaceProps, 'radius' | 'color'> & {
|
|
16
|
+
/**
|
|
17
|
+
* Effect family: rotating border, bottom line travel, or pulse breathe.
|
|
18
|
+
*
|
|
19
|
+
* @default 'border'
|
|
20
|
+
*/
|
|
21
|
+
variant?: BeamVariant;
|
|
22
|
+
/**
|
|
23
|
+
* Border-only size preset (`small` compact / `medium` card). Ignored for other variants.
|
|
24
|
+
*
|
|
25
|
+
* @default 'medium'
|
|
26
|
+
*/
|
|
27
|
+
size?: BeamSize;
|
|
28
|
+
/**
|
|
29
|
+
* Beam color: `primary` → `--global-primary`, a highlight token name → `--highlight-*`,
|
|
30
|
+
* or `colorful` upstream neon multi-stop palette.
|
|
31
|
+
*
|
|
32
|
+
* @default 'primary'
|
|
33
|
+
*/
|
|
34
|
+
color?: BeamColor;
|
|
35
|
+
/**
|
|
36
|
+
* Whether the animation is logically on (fade in/out when toggled).
|
|
37
|
+
*
|
|
38
|
+
* @default true
|
|
39
|
+
*/
|
|
40
|
+
animate?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* CSS time string for spin / travel / pulse period scaling.
|
|
43
|
+
*
|
|
44
|
+
* @default variant-specific (`1.96s` border, `3.1s` line, `2.3s` pulse)
|
|
45
|
+
*/
|
|
46
|
+
duration?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Overall strength/opacity of beam layers (0–1). Does not affect children.
|
|
49
|
+
*
|
|
50
|
+
* @default 1
|
|
51
|
+
*/
|
|
52
|
+
strength?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Brightness filter multiplier for glow layers.
|
|
55
|
+
*
|
|
56
|
+
* @default `1.9` pulse-outside, `0.75` pulse-inner, else `1.3` (upstream dark presets)
|
|
57
|
+
*/
|
|
58
|
+
brightness?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Saturation filter multiplier for glow layers.
|
|
61
|
+
*
|
|
62
|
+
* @default 1.2
|
|
63
|
+
*/
|
|
64
|
+
saturation?: number;
|
|
65
|
+
/**
|
|
66
|
+
* Hue-rotate range for the CSS hue-shift animation (degrees, unitless number).
|
|
67
|
+
* Line variant caps at 13.
|
|
68
|
+
*
|
|
69
|
+
* @default 30
|
|
70
|
+
*/
|
|
71
|
+
hueRange?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Disables hue-shift / pulse hue drift for static token colors.
|
|
74
|
+
*
|
|
75
|
+
* @default false
|
|
76
|
+
*/
|
|
77
|
+
staticColors?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Shrinks the root to fit its content (`max-width: fit-content`).
|
|
80
|
+
*
|
|
81
|
+
* @default false
|
|
82
|
+
*/
|
|
83
|
+
fitContent?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Fired when the fade-in animation completes.
|
|
86
|
+
*/
|
|
87
|
+
onActivate?: () => void;
|
|
88
|
+
/**
|
|
89
|
+
* Fired when the fade-out animation completes.
|
|
90
|
+
*/
|
|
91
|
+
onDeactivate?: () => void;
|
|
92
|
+
};
|
|
93
|
+
export declare const Beam: React.FC<BeamProps>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Beam",
|
|
3
|
+
"structure": "<Beam>{children}</Beam>",
|
|
4
|
+
"summary": "Decorative animated border, line, or pulse glow on a transparent Surface shell (radius only). Put the visual card as children. CSS-module geometry plus shared ~30fps pulse rAF for pulse variants. Memory/CPU cost — use sparingly.",
|
|
5
|
+
"whenToUse": [
|
|
6
|
+
"Highlight a focal Surface with a traveling border beam",
|
|
7
|
+
"Bottom-edge line travel under a card or panel",
|
|
8
|
+
"Breathing pulse glow inside or outside a Surface",
|
|
9
|
+
"Token-colored or colorful multi-stop decorative emphasis"
|
|
10
|
+
],
|
|
11
|
+
"whenNotToUse": [
|
|
12
|
+
"Default decoration on many cards — reserve for rare focal moments",
|
|
13
|
+
"Accessible status or focus indication — Beam is decorative only",
|
|
14
|
+
"As a replacement for Surface color/padding — compose Surface as children",
|
|
15
|
+
"Pointer-driven highlight — use Glow instead"
|
|
16
|
+
],
|
|
17
|
+
"accessibility": [
|
|
18
|
+
"Root stays in the accessibility tree; bloom layer is empty div",
|
|
19
|
+
"Beam is not a focus or status indicator — consumer owns accessible names and state",
|
|
20
|
+
"Offscreen instances pause animations without firing activate/deactivate",
|
|
21
|
+
"prefers-reduced-motion disables motion and leaves a static resting look"
|
|
22
|
+
],
|
|
23
|
+
"antiPatterns": [
|
|
24
|
+
"Relying on Beam for keyboard focus visibility",
|
|
25
|
+
"Treating Beam as the visual card instead of composing Surface children",
|
|
26
|
+
"Expecting theme light/dark auto-tuning — colors are token-driven and theme-agnostic",
|
|
27
|
+
"Using many pulse Beam instances on one page — shared rAF still paints per instance"
|
|
28
|
+
],
|
|
29
|
+
"related": ["Surface", "Glow", "Shimmer"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"react": {
|
|
32
|
+
"radius": {
|
|
33
|
+
"description": "Sets corner radius from the Vira radius token scale, a per-corner [top-left, top-right, bottom-right, bottom-left] tuple (0 for square corners), or auto for concentric radii from the nearest ancestor Surface."
|
|
34
|
+
},
|
|
35
|
+
"variant": {
|
|
36
|
+
"description": "Effect family: rotating border, bottom line travel, or pulse breathe (outside/inner)."
|
|
37
|
+
},
|
|
38
|
+
"size": {
|
|
39
|
+
"description": "Border-only size preset (small compact / medium card). Ignored for other variants."
|
|
40
|
+
},
|
|
41
|
+
"color": {
|
|
42
|
+
"description": "Beam color: primary → --global-primary, highlight token name → --highlight-*, colorful → upstream neon multi-stop RGB palette.",
|
|
43
|
+
"refCss": "color"
|
|
44
|
+
},
|
|
45
|
+
"animate": {
|
|
46
|
+
"description": "Whether the animation is logically on (fade in/out when toggled)."
|
|
47
|
+
},
|
|
48
|
+
"duration": {
|
|
49
|
+
"description": "CSS time string for spin / travel / pulse period scaling.",
|
|
50
|
+
"refCss": "duration"
|
|
51
|
+
},
|
|
52
|
+
"strength": {
|
|
53
|
+
"description": "Overall strength/opacity of beam layers (0–1). Does not affect children.",
|
|
54
|
+
"refCss": "strength"
|
|
55
|
+
},
|
|
56
|
+
"brightness": {
|
|
57
|
+
"description": "Brightness filter multiplier for glow layers. Defaults to 1.9 for pulse-outside, 0.75 for pulse-inner, otherwise 1.3.",
|
|
58
|
+
"refCss": "brightness"
|
|
59
|
+
},
|
|
60
|
+
"saturation": {
|
|
61
|
+
"description": "Saturation filter multiplier for glow layers.",
|
|
62
|
+
"refCss": "saturation"
|
|
63
|
+
},
|
|
64
|
+
"hueRange": {
|
|
65
|
+
"description": "Hue-rotate range for the CSS hue-shift animation (degrees). Line variant caps at 13.",
|
|
66
|
+
"refCss": "hue-range"
|
|
67
|
+
},
|
|
68
|
+
"staticColors": {
|
|
69
|
+
"description": "Disables hue-shift / pulse hue drift for static token colors."
|
|
70
|
+
},
|
|
71
|
+
"fitContent": {
|
|
72
|
+
"description": "Shrinks the root to fit its content (`max-width: fit-content`)."
|
|
73
|
+
},
|
|
74
|
+
"onActivate": {
|
|
75
|
+
"description": "Fired when the fade-in animation completes."
|
|
76
|
+
},
|
|
77
|
+
"onDeactivate": {
|
|
78
|
+
"description": "Fired when the fade-out animation completes."
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"css": {
|
|
82
|
+
"angle": {
|
|
83
|
+
"description": "Conic / travel start angle animated by border and line variants."
|
|
84
|
+
},
|
|
85
|
+
"opacity": {
|
|
86
|
+
"description": "Fade opacity driven by fade-in / fade-out keyframes."
|
|
87
|
+
},
|
|
88
|
+
"duration": {
|
|
89
|
+
"description": "Spin / travel duration (and pulse period scale input).",
|
|
90
|
+
"refReact": "duration"
|
|
91
|
+
},
|
|
92
|
+
"strength": {
|
|
93
|
+
"description": "Overall beam layer strength multiplier.",
|
|
94
|
+
"refReact": "strength"
|
|
95
|
+
},
|
|
96
|
+
"brightness": {
|
|
97
|
+
"description": "Brightness filter multiplier.",
|
|
98
|
+
"refReact": "brightness"
|
|
99
|
+
},
|
|
100
|
+
"saturation": {
|
|
101
|
+
"description": "Saturation filter multiplier.",
|
|
102
|
+
"refReact": "saturation"
|
|
103
|
+
},
|
|
104
|
+
"hue-range": {
|
|
105
|
+
"description": "Hue-rotate oscillation range for non-static colors.",
|
|
106
|
+
"refReact": "hueRange"
|
|
107
|
+
},
|
|
108
|
+
"color": {
|
|
109
|
+
"description": "Resolved beam token color (primary or highlight). Colorful mode uses multi-stop internals.",
|
|
110
|
+
"refReact": "color"
|
|
111
|
+
},
|
|
112
|
+
"hue": {
|
|
113
|
+
"description": "Pulse hue drift angle written by the shared pulse driver."
|
|
114
|
+
},
|
|
115
|
+
"pulse-glow-sx": {
|
|
116
|
+
"description": "Per-axis X scale for pulse-outside glow geometry relative to a 350×140 reference."
|
|
117
|
+
},
|
|
118
|
+
"pulse-glow-sy": {
|
|
119
|
+
"description": "Per-axis Y scale for pulse-outside glow geometry relative to a 350×140 reference."
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import './beam.css';
|
|
3
|
+
import { clsx } from "../../core/clsx.js";
|
|
4
|
+
import { getBeamPulseDriverConfig, registerBeamPulse } from "./beam-pulse-driver.js";
|
|
5
|
+
import beam_module_default from "./beam.module.js";
|
|
6
|
+
import { Surface } from "../surface/surface.js";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
9
|
+
//#region src/components/beam/beam.tsx
|
|
10
|
+
var DEFAULT_DURATION = {
|
|
11
|
+
border: "1.96s",
|
|
12
|
+
line: "3.1s",
|
|
13
|
+
"pulse-inner": "2.3s",
|
|
14
|
+
"pulse-outside": "2.3s"
|
|
15
|
+
};
|
|
16
|
+
var assignRef = (ref, value) => {
|
|
17
|
+
if (typeof ref === "function") {
|
|
18
|
+
ref(value);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (ref) Reflect.set(ref, "current", value);
|
|
22
|
+
};
|
|
23
|
+
var parseDurationSeconds = (duration) => {
|
|
24
|
+
const trimmed = duration.trim();
|
|
25
|
+
if (trimmed.endsWith("ms")) return Number.parseFloat(trimmed) / 1e3;
|
|
26
|
+
if (trimmed.endsWith("s")) return Number.parseFloat(trimmed);
|
|
27
|
+
return Number.parseFloat(trimmed);
|
|
28
|
+
};
|
|
29
|
+
var resolveBeamColor = (color) => {
|
|
30
|
+
if (color === "primary") return "var(--global-primary)";
|
|
31
|
+
if (color === "colorful") return "var(--global-primary)";
|
|
32
|
+
return `var(--highlight-${color})`;
|
|
33
|
+
};
|
|
34
|
+
var Beam = ({ animate = true, brightness: brightnessProp, children, className, color = "primary", duration: durationProp, fitContent = false, hueRange = 30, onActivate, onAnimationEnd: consumerOnAnimationEnd, onDeactivate, radius, ref, saturation = 1.2, size = "medium", staticColors = false, strength = 1, style, variant = "border", ...otherProps }) => {
|
|
35
|
+
const rootRef = useRef(null);
|
|
36
|
+
const [isActive, setIsActive] = useState(animate);
|
|
37
|
+
const [isFading, setIsFading] = useState(false);
|
|
38
|
+
const [isVisible, setIsVisible] = useState(true);
|
|
39
|
+
const [pulseGlowScale, setPulseGlowScale] = useState({
|
|
40
|
+
x: 1,
|
|
41
|
+
y: 1
|
|
42
|
+
});
|
|
43
|
+
const duration = durationProp ?? DEFAULT_DURATION[variant];
|
|
44
|
+
const isPulse = variant === "pulse-inner" || variant === "pulse-outside";
|
|
45
|
+
const effectiveHueRange = variant === "line" ? Math.min(hueRange, 13) : hueRange;
|
|
46
|
+
let brightness = 1.3;
|
|
47
|
+
if (brightnessProp !== void 0) brightness = brightnessProp;
|
|
48
|
+
else if (variant === "pulse-outside") brightness = 1.9;
|
|
49
|
+
else if (variant === "pulse-inner") brightness = .75;
|
|
50
|
+
const setRootRef = useCallback((node) => {
|
|
51
|
+
rootRef.current = node;
|
|
52
|
+
assignRef(ref, node);
|
|
53
|
+
}, [ref]);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (animate && !isActive && !isFading) setIsActive(true);
|
|
56
|
+
else if (!animate && isActive && !isFading) setIsFading(true);
|
|
57
|
+
}, [
|
|
58
|
+
animate,
|
|
59
|
+
isActive,
|
|
60
|
+
isFading
|
|
61
|
+
]);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const el = rootRef.current;
|
|
64
|
+
if (!el || typeof IntersectionObserver === "undefined") return;
|
|
65
|
+
const observer = new IntersectionObserver((entries) => {
|
|
66
|
+
for (const entry of entries) setIsVisible(entry.isIntersecting);
|
|
67
|
+
}, { rootMargin: "256px" });
|
|
68
|
+
observer.observe(el);
|
|
69
|
+
return () => observer.disconnect();
|
|
70
|
+
}, []);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (variant !== "pulse-outside") {
|
|
73
|
+
setPulseGlowScale({
|
|
74
|
+
x: 1,
|
|
75
|
+
y: 1
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const el = rootRef.current;
|
|
80
|
+
if (!el) return;
|
|
81
|
+
const REF_WIDTH = 350;
|
|
82
|
+
const REF_HEIGHT = 140;
|
|
83
|
+
const MIN_SCALE = .35;
|
|
84
|
+
const MAX_SCALE = 4;
|
|
85
|
+
const clamp = (value) => Math.max(MIN_SCALE, Math.min(MAX_SCALE, value));
|
|
86
|
+
const measure = () => {
|
|
87
|
+
const child = [...el.children].find((node) => !node.hasAttribute("data-beam-bloom"));
|
|
88
|
+
if (!child) return;
|
|
89
|
+
const rect = child.getBoundingClientRect();
|
|
90
|
+
if (!rect.width || !rect.height) return;
|
|
91
|
+
const x = +clamp(rect.width / REF_WIDTH).toFixed(3);
|
|
92
|
+
const y = +clamp(rect.height / REF_HEIGHT).toFixed(3);
|
|
93
|
+
setPulseGlowScale((prev) => prev.x === x && prev.y === y ? prev : {
|
|
94
|
+
x,
|
|
95
|
+
y
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
measure();
|
|
99
|
+
const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(measure) : null;
|
|
100
|
+
resizeObserver?.observe(el);
|
|
101
|
+
const mutationObserver = typeof MutationObserver !== "undefined" ? new MutationObserver(measure) : null;
|
|
102
|
+
mutationObserver?.observe(el, { childList: true });
|
|
103
|
+
return () => {
|
|
104
|
+
resizeObserver?.disconnect();
|
|
105
|
+
mutationObserver?.disconnect();
|
|
106
|
+
};
|
|
107
|
+
}, [variant]);
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
if (!isPulse || !(isActive || isFading) || !isVisible) return;
|
|
110
|
+
const el = rootRef.current;
|
|
111
|
+
if (!el) return;
|
|
112
|
+
return registerBeamPulse(el, getBeamPulseDriverConfig(variant, parseDurationSeconds(duration), staticColors));
|
|
113
|
+
}, [
|
|
114
|
+
isPulse,
|
|
115
|
+
variant,
|
|
116
|
+
duration,
|
|
117
|
+
staticColors,
|
|
118
|
+
isActive,
|
|
119
|
+
isFading,
|
|
120
|
+
isVisible
|
|
121
|
+
]);
|
|
122
|
+
const handleAnimationEnd = useCallback((event) => {
|
|
123
|
+
const { animationName } = event;
|
|
124
|
+
if (animationName.includes("fade-out")) {
|
|
125
|
+
setIsActive(false);
|
|
126
|
+
setIsFading(false);
|
|
127
|
+
onDeactivate?.();
|
|
128
|
+
} else if (animationName.includes("fade-in")) onActivate?.();
|
|
129
|
+
consumerOnAnimationEnd?.(event);
|
|
130
|
+
}, [
|
|
131
|
+
onActivate,
|
|
132
|
+
onDeactivate,
|
|
133
|
+
consumerOnAnimationEnd
|
|
134
|
+
]);
|
|
135
|
+
const dynamicStyle = {
|
|
136
|
+
"--vui-beam-duration": duration,
|
|
137
|
+
"--vui-beam-strength": Math.max(0, Math.min(1, strength)),
|
|
138
|
+
"--vui-beam-brightness": brightness,
|
|
139
|
+
"--vui-beam-saturation": saturation,
|
|
140
|
+
"--vui-beam-hue-range": `${effectiveHueRange}deg`,
|
|
141
|
+
"--vui-beam-color": resolveBeamColor(color),
|
|
142
|
+
...variant === "pulse-outside" && {
|
|
143
|
+
"--vui-beam-pulse-glow-sx": pulseGlowScale.x,
|
|
144
|
+
"--vui-beam-pulse-glow-sy": pulseGlowScale.y
|
|
145
|
+
},
|
|
146
|
+
...style
|
|
147
|
+
};
|
|
148
|
+
const showAnimate = isActive && !isFading;
|
|
149
|
+
const showPaused = showAnimate && !isVisible;
|
|
150
|
+
return /* @__PURE__ */ jsxs(Surface, {
|
|
151
|
+
...otherProps,
|
|
152
|
+
ref: setRootRef,
|
|
153
|
+
className: clsx(beam_module_default.Beam, className),
|
|
154
|
+
style: dynamicStyle,
|
|
155
|
+
radius,
|
|
156
|
+
overflow: variant === "pulse-outside" ? void 0 : "hidden",
|
|
157
|
+
"data-beam-variant": variant,
|
|
158
|
+
...variant === "border" && { "data-beam-size": size },
|
|
159
|
+
"data-beam-color": color,
|
|
160
|
+
"data-beam-fit-content": fitContent ? "true" : "false",
|
|
161
|
+
"data-beam-static-colors": staticColors ? "true" : "false",
|
|
162
|
+
...showAnimate && { "data-animate": "" },
|
|
163
|
+
...isFading && { "data-fading": "" },
|
|
164
|
+
...showPaused && { "data-paused": "" },
|
|
165
|
+
onAnimationEnd: handleAnimationEnd,
|
|
166
|
+
children: [children, /* @__PURE__ */ jsx("div", {
|
|
167
|
+
className: beam_module_default.Bloom,
|
|
168
|
+
"data-beam-bloom": ""
|
|
169
|
+
})]
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
//#endregion
|
|
173
|
+
export { Beam };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/components/beam/beam.module.css
|
|
2
|
+
var Beam = "viraui__beam-module__Beam_xT5Pt";
|
|
3
|
+
var Bloom = "viraui__beam-module__Bloom_XrnlV";
|
|
4
|
+
var beam_module_default = {
|
|
5
|
+
Beam,
|
|
6
|
+
"vui-beam-spin": "viraui__beam-module__vui-beam-spin_PCRBV",
|
|
7
|
+
"vui-beam-fade-in": "viraui__beam-module__vui-beam-fade-in_-pZOw",
|
|
8
|
+
"vui-beam-fade-out": "viraui__beam-module__vui-beam-fade-out_ITNKo",
|
|
9
|
+
"vui-beam-hue-shift": "viraui__beam-module__vui-beam-hue-shift_9CzG5",
|
|
10
|
+
"beam-travel": "viraui__beam-module__beam-travel_FuPkI",
|
|
11
|
+
"beam-edge-fade": "viraui__beam-module__beam-edge-fade_PXYJA",
|
|
12
|
+
"beam-breathe": "viraui__beam-module__beam-breathe_3BBMd",
|
|
13
|
+
"beam-spike": "viraui__beam-module__beam-spike_1iIfV",
|
|
14
|
+
"beam-spike2": "viraui__beam-module__beam-spike2_d1gJy",
|
|
15
|
+
"beam-hue-shift-bloom": "viraui__beam-module__beam-hue-shift-bloom_Onfv-",
|
|
16
|
+
Bloom
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { Beam, Bloom, beam_module_default as default };
|
|
@@ -91,9 +91,5 @@ var unregisterGlow = (el) => {
|
|
|
91
91
|
entries.delete(el);
|
|
92
92
|
detachListener(doc);
|
|
93
93
|
};
|
|
94
|
-
/** Story / test helper: count of documents with an attached `pointermove` listener. */
|
|
95
|
-
var getGlowListenerCount = () => listeningDocs.size;
|
|
96
|
-
/** Story / test helper: registered glow root count. */
|
|
97
|
-
var getGlowRegistrySize = () => entries.size;
|
|
98
94
|
//#endregion
|
|
99
|
-
export {
|
|
95
|
+
export { registerGlow, unregisterGlow, unwrapGlowAngle };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
@layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-glow-starting-angle{syntax:"<number>";inherits:true;initial-value:0}@property --vui-glow-angle-lag{syntax:"<time>";inherits:true;initial-value:80ms}@property --vui-glow-
|
|
1
|
+
@layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-glow-starting-angle{syntax:"<number>";inherits:true;initial-value:0}@property --vui-glow-angle-lag{syntax:"<time>";inherits:true;initial-value:80ms}@property --vui-glow-active-opacity{syntax:"<number>";inherits:true;initial-value:0}@property --vui-glow-spread{syntax:"<number>";inherits:true;initial-value:250}@property --vui-glow-border-width{syntax:"<length>";inherits:true;initial-value:2px}@property --vui-glow-border-color{syntax:"<color>";inherits:true;initial-value:#0000}@property --vui-glow-border-offset{syntax:"<length>";inherits:true;initial-value:5px}@property --vui-glow-color{syntax:"*";inherits:true;initial-value:#0000}@property --__glow-radius-tl{syntax:"<length-percentage>";inherits:true;initial-value:0}@property --__glow-radius-tr{syntax:"<length-percentage>";inherits:true;initial-value:0}@property --__glow-radius-br{syntax:"<length-percentage>";inherits:true;initial-value:0}@property --__glow-radius-bl{syntax:"<length-percentage>";inherits:true;initial-value:0}.viraui__glow-module__Glow_7394e{--vui-glow-border-color:var(--global-contrast);--vui-glow-color:conic-gradient(from 180deg at 50% 70%,#0000,var(--global-primary),#0000);--__glow-radius-tl:var(--vui-surface-border-radius-top-left);--__glow-radius-tr:var(--vui-surface-border-radius-top-right);--__glow-radius-br:var(--vui-surface-border-radius-bottom-right);--__glow-radius-bl:var(--vui-surface-border-radius-bottom-left);position:relative;z-index:0;transition:--vui-glow-starting-angle var(--vui-glow-angle-lag) ease-out;&[data-glow-fit-content=true]{max-width:fit-content}&[data-glow-rainbow=true]{--vui-glow-color:conic-gradient(from 180deg at 50% 50%,var(--highlight-cyan),var(--highlight-blue),var(--highlight-green),var(--highlight-purple),var(--highlight-red),var(--highlight-indigo),var(--highlight-yellow),var(--highlight-salmon),var(--highlight-magenta),var(--highlight-cyan))}&>*{--vui-glow-starting-angle:initial;--vui-glow-active-opacity:initial}&:after,&:before{content:"";position:absolute;z-index:1;pointer-events:none;inset:calc(var(--vui-glow-border-offset) * -1);opacity:var(--vui-glow-active-opacity);border:var(--vui-glow-border-width) solid #0000;border-radius:calc(var(--vui-glow-border-offset) + var(--__glow-radius-tl)) calc(var(--vui-glow-border-offset) + var(--__glow-radius-tr)) calc(var(--vui-glow-border-offset) + var(--__glow-radius-br)) calc(var(--vui-glow-border-offset) + var(--__glow-radius-bl));transition:opacity 1s;@media (prefers-reduced-motion:reduce){display:none}@media (hover:none),(pointer:coarse){display:none}}&:before{background:var(--vui-glow-border-color);mask:linear-gradient(#0000,#0000),conic-gradient(from calc(((var(--vui-glow-starting-angle) + (var(--vui-glow-spread) * .25)) - (var(--vui-glow-spread) * 1.5)) * 1deg),oklch(100% 0 0deg/15%) 0deg,#fff,oklch(100% 0 0deg/15%) calc(var(--vui-glow-spread) * 2.5deg));mask-clip:padding-box,border-box;mask-composite:intersect}&:after{background:var(--vui-glow-color);filter:brightness(1.5);mask:linear-gradient(#0000,#0000),conic-gradient(from calc(((var(--vui-glow-starting-angle) + (var(--vui-glow-spread) * .25)) - (var(--vui-glow-spread) * .5)) * 1deg),#0000 0deg,#fff,#0000 calc(var(--vui-glow-spread) * .5deg));mask-clip:padding-box,border-box;mask-composite:intersect;background-size:calc(100% + var(--vui-glow-border-width) * 2) calc(100% + var(--vui-glow-border-width) * 2);background-position:calc(var(--vui-glow-border-width) * -1) calc(var(--vui-glow-border-width) * -1)}&[data-glow-global=true]:after{background-attachment:fixed}}}
|
|
@@ -75,23 +75,9 @@ export type GlowProps = React.ComponentProps<'div'> & Pick<SurfaceProps, 'radius
|
|
|
75
75
|
* CSS transition duration (ms) for `--vui-glow-starting-angle`. Soft follow by default;
|
|
76
76
|
* set `0` for instant snap. Tracker writes an unwrapped continuous angle so the
|
|
77
77
|
* transition does not take the long way across the 0° seam.
|
|
78
|
-
* Ignored when `autoRotate` is true.
|
|
79
78
|
*
|
|
80
79
|
* @default 80
|
|
81
80
|
*/
|
|
82
81
|
angleLag?: number;
|
|
83
|
-
/**
|
|
84
|
-
* Continuously rotates the highlight with a linear CSS animation and ignores the pointer.
|
|
85
|
-
* Always visible (including coarse pointers); still hidden under `prefers-reduced-motion`.
|
|
86
|
-
*
|
|
87
|
-
* @default false
|
|
88
|
-
*/
|
|
89
|
-
autoRotate?: boolean;
|
|
90
|
-
/**
|
|
91
|
-
* Full-turn duration in seconds when `autoRotate` is true.
|
|
92
|
-
*
|
|
93
|
-
* @default 2
|
|
94
|
-
*/
|
|
95
|
-
autoRotateSpeed?: number;
|
|
96
82
|
};
|
|
97
83
|
export declare const Glow: React.FC<GlowProps>;
|
|
@@ -5,28 +5,28 @@
|
|
|
5
5
|
"whenToUse": [
|
|
6
6
|
"Wrap a Surface (or other content) that should highlight toward the pointer",
|
|
7
7
|
"Grid of glow shells that share one pointer highlight path",
|
|
8
|
-
"Nested glow inside a padded Surface using radius=\"auto\""
|
|
9
|
-
"Decorative continuous spin with autoRotate (no pointer)"
|
|
8
|
+
"Nested glow inside a padded Surface using radius=\"auto\""
|
|
10
9
|
],
|
|
11
10
|
"whenNotToUse": [
|
|
12
11
|
"Default decoration on many cards — memory cost is high; reserve for rare focal moments",
|
|
13
|
-
"Static
|
|
12
|
+
"Static or continuously animated borders — use Surface border or Beam instead",
|
|
14
13
|
"Accessible status or focus indication — glow is decorative only",
|
|
15
14
|
"As a replacement for Surface color/padding — compose Surface as children"
|
|
16
15
|
],
|
|
17
16
|
"accessibility": [
|
|
18
17
|
"Root stays in the accessibility tree; highlight is decorative CSS only",
|
|
19
18
|
"Glow is not a focus or status indicator — consumer owns accessible names and state",
|
|
20
|
-
"
|
|
19
|
+
"Highlight hides under coarse pointer / no-hover and under prefers-reduced-motion"
|
|
21
20
|
],
|
|
22
21
|
"antiPatterns": [
|
|
23
22
|
"Relying on Glow for keyboard focus visibility",
|
|
24
23
|
"Treating Glow as the visual card (color/padding) instead of composing Surface children",
|
|
25
24
|
"Setting overflow:hidden on an ancestor Surface when outer glow must remain visible",
|
|
26
25
|
"Expecting soft bloom Light layer — Glow is border-ring only",
|
|
27
|
-
"Using many Glow instances
|
|
26
|
+
"Using many Glow instances on one page — memory- and CPU-heavy",
|
|
27
|
+
"Expecting continuous spin — use Beam instead"
|
|
28
28
|
],
|
|
29
|
-
"related": ["Surface", "Shimmer", "ProgressiveBlur"],
|
|
29
|
+
"related": ["Surface", "Beam", "Shimmer", "ProgressiveBlur"],
|
|
30
30
|
"properties": {
|
|
31
31
|
"react": {
|
|
32
32
|
"radius": {
|
|
@@ -68,29 +68,18 @@
|
|
|
68
68
|
"description": "Uses the multi-stop rainbow highlight gradient and disables global highlight attachment."
|
|
69
69
|
},
|
|
70
70
|
"angleLag": {
|
|
71
|
-
"description": "CSS transition duration in milliseconds for the pointer-driven starting angle. Soft follow by default; set 0 for instant snap.
|
|
71
|
+
"description": "CSS transition duration in milliseconds for the pointer-driven starting angle. Soft follow by default; set 0 for instant snap.",
|
|
72
72
|
"refCss": "angle-lag"
|
|
73
|
-
},
|
|
74
|
-
"autoRotate": {
|
|
75
|
-
"description": "Continuously rotates the highlight with a linear CSS animation and ignores the pointer. Always visible including on coarse pointers; still hidden under prefers-reduced-motion."
|
|
76
|
-
},
|
|
77
|
-
"autoRotateSpeed": {
|
|
78
|
-
"description": "Full-turn duration in seconds when autoRotate is true.",
|
|
79
|
-
"refCss": "auto-rotate-speed"
|
|
80
73
|
}
|
|
81
74
|
},
|
|
82
75
|
"css": {
|
|
83
76
|
"starting-angle": {
|
|
84
|
-
"description": "Conic gradient start angle in degrees (unitless number). Pointer tracker writes a continuous unwrapped value
|
|
77
|
+
"description": "Conic gradient start angle in degrees (unitless number). Pointer tracker writes a continuous unwrapped value."
|
|
85
78
|
},
|
|
86
79
|
"angle-lag": {
|
|
87
|
-
"description": "Transition duration for `--vui-glow-starting-angle
|
|
80
|
+
"description": "Transition duration for `--vui-glow-starting-angle`.",
|
|
88
81
|
"refReact": "angleLag"
|
|
89
82
|
},
|
|
90
|
-
"auto-rotate-speed": {
|
|
91
|
-
"description": "Duration of one full auto-rotate turn.",
|
|
92
|
-
"refReact": "autoRotateSpeed"
|
|
93
|
-
},
|
|
94
83
|
"active-opacity": {
|
|
95
84
|
"description": "Current glow opacity from proximity tracking. Written by the shared tracker."
|
|
96
85
|
},
|
|
@@ -18,7 +18,7 @@ var glowGlobalAttr = (rainbowColors, globalHighlight) => {
|
|
|
18
18
|
if (rainbowColors) return "false";
|
|
19
19
|
return globalHighlight ? "true" : "false";
|
|
20
20
|
};
|
|
21
|
-
var Glow = ({ angleLag = 80,
|
|
21
|
+
var Glow = ({ angleLag = 80, borderColor = "var(--global-contrast)", borderOffset = 5, borderWidth = 2, children, className, fitContent = false, globalHighlight = true, glowColor, opacity = 0, proximity = 170, rainbowColors = false, ref, spread = 250, style, radius, ...otherProps }) => {
|
|
22
22
|
const rootRef = useRef(null);
|
|
23
23
|
const setRootRef = (node) => {
|
|
24
24
|
rootRef.current = node;
|
|
@@ -26,7 +26,7 @@ var Glow = ({ angleLag = 80, autoRotate = false, autoRotateSpeed = 4, borderColo
|
|
|
26
26
|
};
|
|
27
27
|
useEffect(() => {
|
|
28
28
|
const el = rootRef.current;
|
|
29
|
-
if (!el
|
|
29
|
+
if (!el) return;
|
|
30
30
|
registerGlow({
|
|
31
31
|
el,
|
|
32
32
|
proximity,
|
|
@@ -35,18 +35,13 @@ var Glow = ({ angleLag = 80, autoRotate = false, autoRotateSpeed = 4, borderColo
|
|
|
35
35
|
return () => {
|
|
36
36
|
unregisterGlow(el);
|
|
37
37
|
};
|
|
38
|
-
}, [
|
|
39
|
-
autoRotate,
|
|
40
|
-
proximity,
|
|
41
|
-
opacity
|
|
42
|
-
]);
|
|
38
|
+
}, [proximity, opacity]);
|
|
43
39
|
const dynamicStyle = {
|
|
44
40
|
"--vui-glow-spread": spread,
|
|
45
41
|
"--vui-glow-border-width": `${borderWidth}px`,
|
|
46
42
|
"--vui-glow-border-color": borderColor,
|
|
47
43
|
"--vui-glow-border-offset": `${borderOffset}px`,
|
|
48
44
|
"--vui-glow-angle-lag": `${angleLag}ms`,
|
|
49
|
-
"--vui-glow-auto-rotate-speed": `${autoRotateSpeed}s`,
|
|
50
45
|
...glowColor && !rainbowColors && { "--vui-glow-color": glowColor },
|
|
51
46
|
...style
|
|
52
47
|
};
|
|
@@ -59,7 +54,6 @@ var Glow = ({ angleLag = 80, autoRotate = false, autoRotateSpeed = 4, borderColo
|
|
|
59
54
|
"data-glow-fit-content": fitContent ? "true" : "false",
|
|
60
55
|
"data-glow-global": glowGlobalAttr(rainbowColors, globalHighlight),
|
|
61
56
|
"data-glow-rainbow": rainbowColors ? "true" : "false",
|
|
62
|
-
"data-glow-auto-rotate": autoRotate ? "true" : "false",
|
|
63
57
|
children
|
|
64
58
|
});
|
|
65
59
|
};
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
//#region src/components/glow/glow.module.css
|
|
2
2
|
var Glow = "viraui__glow-module__Glow_7394e";
|
|
3
|
-
var glow_module_default = {
|
|
4
|
-
Glow,
|
|
5
|
-
"vui-glow-auto-rotate": "viraui__glow-module__vui-glow-auto-rotate_Zh-hL"
|
|
6
|
-
};
|
|
3
|
+
var glow_module_default = { Glow };
|
|
7
4
|
//#endregion
|
|
8
5
|
export { Glow, glow_module_default as default };
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Autocomplete } from "./components/autocomplete/autocomplete.js";
|
|
|
6
6
|
import { Avatar } from "./components/avatar/avatar.js";
|
|
7
7
|
import { AvatarGroup } from "./components/avatar-group/avatar-group.js";
|
|
8
8
|
import { Badge } from "./components/badge/badge.js";
|
|
9
|
+
import { Beam } from "./components/beam/beam.js";
|
|
9
10
|
import { Bleed } from "./components/bleed/bleed.js";
|
|
10
11
|
import { Spinner } from "./components/spinner/spinner.js";
|
|
11
12
|
import { Button } from "./components/button/button.js";
|
|
@@ -26,7 +27,6 @@ import { Elevator } from "./components/elevator/elevator.js";
|
|
|
26
27
|
import { Fieldset } from "./components/fieldset/fieldset.js";
|
|
27
28
|
import { FitText } from "./components/fit-text/fit-text.js";
|
|
28
29
|
import { Surface } from "./components/surface/surface.js";
|
|
29
|
-
import { getGlowListenerCount, getGlowRegistrySize } from "./components/glow/glow-tracker.js";
|
|
30
30
|
import { Glow } from "./components/glow/glow.js";
|
|
31
31
|
import { Grid } from "./components/grid/grid.js";
|
|
32
32
|
import { IconButton } from "./components/icon-button/icon-button.js";
|
|
@@ -66,4 +66,4 @@ import { ToggleGroup } from "./components/toggle-group/toggle-group.js";
|
|
|
66
66
|
import { Typewriter } from "./components/typewriter/typewriter.js";
|
|
67
67
|
import { TooltipContent } from "./components/tooltip/tooltip-content.js";
|
|
68
68
|
import { Tooltip, TooltipProvider, TooltipTrigger } from "./components/tooltip/tooltip.js";
|
|
69
|
-
export { Accordion, Annotate, Autocomplete, Avatar, AvatarGroup, Badge, Bleed, Button, ButtonLink, Checkbox, CheckboxGroup, Chip, ClampText, Dialog, DialogClose, DialogIndent, DialogIndentBackground, DialogSheet, DialogTrigger, ELEVATION_DEFAULTS, Elevator, Fieldset, FitText, Glow, Grid, IconButton, IconButtonLink, LinearProgress, Masonry, Menu, MenuCheckboxItem, MenuGroup, MenuItem, MenuLinkItem, MenuPopup, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuSubmenu, MenuSubmenuTrigger, MenuTrigger, Meter, Popover, PopoverClose, PopoverDescription, PopoverSheet, PopoverTitle, PopoverTrigger, ProgressiveBlur, Radio, RadioGroup, Select, Separator, Shimmer, Skeleton, Slider, Spinner, Stack, StaticNoise, Surface, Switch, Tabs, Text, Textarea, Textfield, Title, Toast, ToggleButton, ToggleGroup, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Typewriter, getElevationProps
|
|
69
|
+
export { Accordion, Annotate, Autocomplete, Avatar, AvatarGroup, Badge, Beam, Bleed, Button, ButtonLink, Checkbox, CheckboxGroup, Chip, ClampText, Dialog, DialogClose, DialogIndent, DialogIndentBackground, DialogSheet, DialogTrigger, ELEVATION_DEFAULTS, Elevator, Fieldset, FitText, Glow, Grid, IconButton, IconButtonLink, LinearProgress, Masonry, Menu, MenuCheckboxItem, MenuGroup, MenuItem, MenuLinkItem, MenuPopup, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuSubmenu, MenuSubmenuTrigger, MenuTrigger, Meter, Popover, PopoverClose, PopoverDescription, PopoverSheet, PopoverTitle, PopoverTrigger, ProgressiveBlur, Radio, RadioGroup, Select, Separator, Shimmer, Skeleton, Slider, Spinner, Stack, StaticNoise, Surface, Switch, Tabs, Text, Textarea, Textfield, Title, Toast, ToggleButton, ToggleGroup, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Typewriter, getElevationProps };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viraui/react",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"unplugin-dts": "1.0.3",
|
|
36
36
|
"vite": "8.1.4",
|
|
37
37
|
"vite-plugin-static-copy": "4.1.1",
|
|
38
|
-
"@viraui/foundation": "2026.
|
|
38
|
+
"@viraui/foundation": "2026.2.0",
|
|
39
39
|
"@viraui/icons": "2026.0.1",
|
|
40
40
|
"@viraui/postcss-config": "2026.0.0",
|
|
41
41
|
"@viraui/tsconfig": "2026.0.0"
|