@birdapi/velinstyle 1.2.2 → 1.3.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/README.de.md +18 -4
- package/README.md +42 -7
- package/cli/cli-manifest.json +441 -174
- package/cli/docgen/extract-cli.js +2 -1
- package/cli/docs-generate.js +9 -0
- package/cli/experience.js +173 -0
- package/cli/index.js +1294 -417
- package/cli/motion.js +738 -0
- package/cli/production/extract.js +46 -6
- package/cli/production/report.js +104 -14
- package/cli/production/run.js +44 -1
- package/cli/security.js +190 -0
- package/cli/skills.js +53 -10
- package/cli/transparency.js +140 -41
- package/cli/workflow.js +32 -17
- package/components/velin-theme-toggle.js +15 -1
- package/core/a11y/component-contracts.json +391 -274
- package/core/attributes/registry.js +1 -1
- package/core/motion/analyze.js +38 -0
- package/core/motion/blueprint.js +51 -0
- package/core/motion/composer.js +114 -0
- package/core/motion/data/registry.json +352 -0
- package/core/motion/effects.js +17 -1
- package/core/motion/index.js +112 -9
- package/core/motion/optimize.js +44 -0
- package/core/motion/policy.js +77 -0
- package/core/motion/registry.js +131 -0
- package/core/motion/review.js +59 -0
- package/core/motion/scan.js +150 -0
- package/core/motion/timeline.js +68 -0
- package/core/motion/transitions.js +49 -0
- package/core/motion/triggers.js +168 -0
- package/core/security/checks/fs.js +92 -0
- package/core/security/detect.js +35 -0
- package/core/security/engine.js +116 -0
- package/core/security/index.js +18 -0
- package/core/security/registry.js +85 -0
- package/core/security/report/report.js +113 -0
- package/core/security/rules/ci/index.js +146 -0
- package/core/security/rules/consumer/index.js +174 -0
- package/core/security/rules/deps/index.js +182 -0
- package/core/security/rules/index.js +24 -0
- package/core/security/rules/publish/index.js +230 -0
- package/core/security/rules/repo/index.js +143 -0
- package/core/security/rules/secrets/index.js +133 -0
- package/core/security/score/dependency-health.js +53 -0
- package/core/security/score/release-health.js +66 -0
- package/core/security/score/score.js +102 -0
- package/core/security/timeline/timeline.js +76 -0
- package/dist/chunks/attributes-HK7VIOLA.js +1080 -0
- package/dist/chunks/attributes-VRHHVNDO.js +1080 -0
- package/dist/chunks/chunk-MYKUI364.js +116 -0
- package/dist/chunks/chunk-NEVBPD5T.js +116 -0
- package/dist/chunks/runtime-entry.js +1 -1
- package/dist/chunks/velin-theme-toggle-J2NHC7IM.js +304 -0
- package/dist/llms.txt +2 -2
- package/dist/search-index.json +166 -2
- package/dist/velin-agent.json +156 -32
- package/dist/velinstyle-components.iife.js +862 -17
- package/dist/velinstyle-components.js +862 -17
- package/dist/velinstyle-components.min.js +126 -126
- package/dist/velinstyle.css +42 -7
- package/dist/velinstyle.min.css +1 -1
- package/package.json +158 -143
- package/packages/velinstyle-cli-core/package.json +11 -0
- package/packages/velinstyle-cli-core/src/config.js +84 -0
- package/packages/velinstyle-cli-core/src/contract.js +56 -0
- package/packages/velinstyle-cli-core/src/event-bus.js +50 -0
- package/packages/velinstyle-cli-core/src/index.js +18 -0
- package/packages/velinstyle-cli-core/src/lifecycle.js +33 -0
- package/packages/velinstyle-cli-core/src/runner.js +129 -0
- package/packages/velinstyle-cli-registry/data/commands.json +1216 -0
- package/packages/velinstyle-cli-registry/data/layouts.json +33 -0
- package/packages/velinstyle-cli-registry/data/widgets.json +35 -0
- package/packages/velinstyle-cli-registry/package.json +13 -0
- package/packages/velinstyle-cli-registry/src/index.js +88 -0
- package/packages/velinstyle-cli-renderer/package.json +8 -0
- package/packages/velinstyle-cli-renderer/src/index.js +286 -0
- package/packages/velinstyle-cli-theme/package.json +8 -0
- package/packages/velinstyle-cli-theme/src/index.js +84 -0
- package/packages/velinstyle-cli-ui/package.json +8 -0
- package/packages/velinstyle-cli-ui/src/index.js +34 -0
- package/packages/velinstyle-motion/README.md +81 -0
- package/packages/velinstyle-motion/package.json +37 -0
- package/packages/velinstyle-motion/src/ai/mini-prompt.js +119 -0
- package/packages/velinstyle-motion/src/ai/provider.js +59 -0
- package/packages/velinstyle-motion/src/ai/providers/pixverse-setup.js +402 -0
- package/packages/velinstyle-motion/src/ai/providers/pixverse.js +245 -0
- package/packages/velinstyle-motion/src/config.js +257 -0
- package/packages/velinstyle-motion/src/export/html-clip.js +141 -0
- package/packages/velinstyle-motion/src/index.js +304 -0
- package/packages/velinstyle-motion/src/jobs/cache.js +30 -0
- package/packages/velinstyle-motion/src/jobs/queue.js +69 -0
- package/packages/velinstyle-motion/src/jobs/usage.js +53 -0
- package/packages/velinstyle-motion/src/local/effects.js +139 -0
- package/packages/velinstyle-motion/src/local/engine.js +96 -0
- package/packages/velinstyle-motion/src/providers-catalog.js +44 -0
- package/packages/velinstyle-motion/src/registry/presets.js +25 -0
- package/packages/velinstyle-motion/src/registry/presets.json +85 -0
- package/packages/velinstyle-motion/src/ux/cost-gate.js +37 -0
- package/packages/velinstyle-motion/src/ux/pixverse-gate.js +153 -0
- package/packages/velinstyle-motion/src/ux/wizard.js +171 -0
- package/packages/velinstyle-motion/src/validate-image.js +91 -0
- package/packages/velinstyle-skills/catalog.json +1 -1
- package/packages/velinstyle-skills/registry.json +1 -142
- package/packages/velinstyle-skills/skills/velin-motion-reduced-safe/SKILL.md +1 -1
- package/packages/velinstyle-skills/skills/velin-motion-reveal-feedback/SKILL.md +1 -1
- package/src/tokens/motion.css +7 -0
- package/src/utilities/scroll-animation.css +66 -0
|
@@ -0,0 +1,1080 @@
|
|
|
1
|
+
import {
|
|
2
|
+
disconnectInViewObserver,
|
|
3
|
+
observeInView
|
|
4
|
+
} from "./chunk-7MCGPHVC.js";
|
|
5
|
+
import {
|
|
6
|
+
lazyDefine
|
|
7
|
+
} from "./chunk-MYKUI364.js";
|
|
8
|
+
|
|
9
|
+
// core/motion/effects.js
|
|
10
|
+
var VISIBLE_CLASS = "velin-in-view";
|
|
11
|
+
var EFFECT_MAP = {
|
|
12
|
+
reveal: ["velin-animate-on-scroll"],
|
|
13
|
+
fade: ["velin-animate-on-scroll", "velin-animate-on-scroll--fade"],
|
|
14
|
+
slide: ["velin-animate-on-scroll", "velin-animate-on-scroll--slide-up"],
|
|
15
|
+
"slide-up": ["velin-animate-on-scroll", "velin-animate-on-scroll--slide-up"],
|
|
16
|
+
"slide-down": ["velin-animate-on-scroll", "velin-animate-on-scroll--slide-down"],
|
|
17
|
+
"slide-left": ["velin-animate-on-scroll", "velin-animate-on-scroll--slide-left"],
|
|
18
|
+
"slide-right": ["velin-animate-on-scroll", "velin-animate-on-scroll--slide-right"],
|
|
19
|
+
scale: ["velin-animate-on-scroll", "velin-animate-on-scroll--scale"],
|
|
20
|
+
zoom: ["velin-animate-on-scroll", "velin-animate-on-scroll--scale"],
|
|
21
|
+
parallax: ["velin-parallax"],
|
|
22
|
+
"parallax-slow": ["velin-parallax", "velin-parallax--slow"],
|
|
23
|
+
hover: ["velin-animate-hover"],
|
|
24
|
+
"hover-lift": ["velin-animate-hover"],
|
|
25
|
+
lift: ["velin-animate-hover"],
|
|
26
|
+
flip: ["velin-animate-scale-in"],
|
|
27
|
+
blur: ["velin-animate-fade-in"],
|
|
28
|
+
pulse: ["velin-animate-pulse"],
|
|
29
|
+
shake: ["velin-animate-shake"],
|
|
30
|
+
bounce: ["velin-animate-bounce"],
|
|
31
|
+
shimmer: ["velin-skeleton"],
|
|
32
|
+
heroReveal: ["velin-animate-on-scroll", "velin-animate-on-scroll--fade"],
|
|
33
|
+
fadeOut: ["velin-animate-fade-out"]
|
|
34
|
+
};
|
|
35
|
+
function applyEffects(el, attrs = {}) {
|
|
36
|
+
const classes = /* @__PURE__ */ new Set();
|
|
37
|
+
for (const [name, value] of Object.entries(attrs)) {
|
|
38
|
+
if (value === "false" || value === "off") continue;
|
|
39
|
+
if (name === "motion" && value && value !== "true") {
|
|
40
|
+
const presetKey = value;
|
|
41
|
+
const mapped2 = EFFECT_MAP[presetKey];
|
|
42
|
+
if (mapped2) mapped2.forEach((c) => classes.add(c));
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const mapped = EFFECT_MAP[name];
|
|
46
|
+
if (mapped) mapped.forEach((c) => classes.add(c));
|
|
47
|
+
if (name === "slide" && value && value !== "true" && value !== "") {
|
|
48
|
+
const key = `slide-${value}`;
|
|
49
|
+
EFFECT_MAP[key]?.forEach((c) => classes.add(c));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
classes.forEach((c) => el.classList.add(c));
|
|
53
|
+
if (!el.classList.contains("velin-animate-on-scroll") && attrs.reveal !== void 0) {
|
|
54
|
+
el.classList.add("velin-animate-on-scroll");
|
|
55
|
+
}
|
|
56
|
+
return [...classes];
|
|
57
|
+
}
|
|
58
|
+
function markVisible(el) {
|
|
59
|
+
el.classList.add(VISIBLE_CLASS);
|
|
60
|
+
el.dataset.velinVisible = "true";
|
|
61
|
+
}
|
|
62
|
+
function prefersReducedMotion() {
|
|
63
|
+
return typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// core/motion/stagger.js
|
|
67
|
+
var STAGGER_ATTR = "velin-stagger";
|
|
68
|
+
function applyStagger(listEl, value = "true") {
|
|
69
|
+
const base = value === "true" || value === "" ? 60 : parseInt(value, 10) || 60;
|
|
70
|
+
const children = [...listEl.children];
|
|
71
|
+
children.forEach((child, i) => {
|
|
72
|
+
child.style.setProperty("--velin-stagger-delay", `${i * base}ms`);
|
|
73
|
+
child.classList.add("velin-stagger-item");
|
|
74
|
+
});
|
|
75
|
+
listEl.classList.add("velin-stagger");
|
|
76
|
+
}
|
|
77
|
+
function enhanceStagger(root = document) {
|
|
78
|
+
root.querySelectorAll(`[${STAGGER_ATTR}]`).forEach((el) => {
|
|
79
|
+
if (el.dataset.velinStaggerDone) return;
|
|
80
|
+
el.dataset.velinStaggerDone = "1";
|
|
81
|
+
applyStagger(el, el.getAttribute(STAGGER_ATTR) || "true");
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// core/motion/scroll.js
|
|
86
|
+
function smoothScrollTo(target, options = {}) {
|
|
87
|
+
const reduced = typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
88
|
+
const el = typeof target === "string" ? document.querySelector(target) : target;
|
|
89
|
+
if (!el) return;
|
|
90
|
+
el.scrollIntoView({
|
|
91
|
+
behavior: reduced || options.instant ? "auto" : "smooth",
|
|
92
|
+
block: options.block || "start",
|
|
93
|
+
inline: options.inline || "nearest"
|
|
94
|
+
});
|
|
95
|
+
if (el.tabIndex < 0 && !el.hasAttribute("tabindex")) {
|
|
96
|
+
el.setAttribute("tabindex", "-1");
|
|
97
|
+
}
|
|
98
|
+
el.focus({ preventScroll: true });
|
|
99
|
+
}
|
|
100
|
+
function bindSmoothScroll(root = document) {
|
|
101
|
+
root.querySelectorAll("[velin-scroll]").forEach((link) => {
|
|
102
|
+
if (link.dataset.velinScrollBound) return;
|
|
103
|
+
link.dataset.velinScrollBound = "1";
|
|
104
|
+
link.addEventListener("click", (e) => {
|
|
105
|
+
const href = link.getAttribute("href");
|
|
106
|
+
if (!href || !href.startsWith("#")) return;
|
|
107
|
+
const target = document.querySelector(href);
|
|
108
|
+
if (!target) return;
|
|
109
|
+
e.preventDefault();
|
|
110
|
+
smoothScrollTo(target);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// core/motion/data/registry.json
|
|
116
|
+
var registry_default = {
|
|
117
|
+
schema: "velinstyle.motion.registry",
|
|
118
|
+
version: 1,
|
|
119
|
+
presets: [
|
|
120
|
+
{
|
|
121
|
+
id: "fade",
|
|
122
|
+
title: "Fade",
|
|
123
|
+
description: "Opacity entrance; layout-safe default reveal.",
|
|
124
|
+
kind: "preset",
|
|
125
|
+
category: "hero",
|
|
126
|
+
tags: ["entrance"],
|
|
127
|
+
duration: 400,
|
|
128
|
+
fps: "60",
|
|
129
|
+
reducedMotion: true,
|
|
130
|
+
layoutSafe: true,
|
|
131
|
+
status: "stable",
|
|
132
|
+
cssClass: "velin-animate-on-scroll--fade",
|
|
133
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: "slide",
|
|
137
|
+
title: "Slide Up",
|
|
138
|
+
description: "Slide from below into view.",
|
|
139
|
+
kind: "preset",
|
|
140
|
+
category: "cards",
|
|
141
|
+
tags: ["entrance", "slide"],
|
|
142
|
+
duration: 500,
|
|
143
|
+
fps: "60",
|
|
144
|
+
reducedMotion: true,
|
|
145
|
+
layoutSafe: true,
|
|
146
|
+
status: "stable",
|
|
147
|
+
cssClass: "velin-animate-on-scroll--slide-up",
|
|
148
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: "slide-down",
|
|
152
|
+
title: "Slide Down",
|
|
153
|
+
description: "Slide from above into view.",
|
|
154
|
+
kind: "preset",
|
|
155
|
+
category: "navigation",
|
|
156
|
+
tags: ["entrance", "slide"],
|
|
157
|
+
duration: 500,
|
|
158
|
+
reducedMotion: true,
|
|
159
|
+
layoutSafe: true,
|
|
160
|
+
status: "stable",
|
|
161
|
+
cssClass: "velin-animate-on-scroll--slide-down",
|
|
162
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: "slide-left",
|
|
166
|
+
title: "Slide Left",
|
|
167
|
+
description: "Slide from the inline-end.",
|
|
168
|
+
kind: "preset",
|
|
169
|
+
category: "lists",
|
|
170
|
+
tags: ["entrance", "slide"],
|
|
171
|
+
duration: 500,
|
|
172
|
+
reducedMotion: true,
|
|
173
|
+
layoutSafe: true,
|
|
174
|
+
status: "stable",
|
|
175
|
+
cssClass: "velin-animate-on-scroll--slide-left",
|
|
176
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: "slide-right",
|
|
180
|
+
title: "Slide Right",
|
|
181
|
+
description: "Slide from the inline-start.",
|
|
182
|
+
kind: "preset",
|
|
183
|
+
category: "lists",
|
|
184
|
+
tags: ["entrance", "slide"],
|
|
185
|
+
duration: 500,
|
|
186
|
+
reducedMotion: true,
|
|
187
|
+
layoutSafe: true,
|
|
188
|
+
status: "stable",
|
|
189
|
+
cssClass: "velin-animate-on-scroll--slide-right",
|
|
190
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: "zoom",
|
|
194
|
+
title: "Zoom / Scale",
|
|
195
|
+
description: "Scale-in entrance for cards and media.",
|
|
196
|
+
kind: "preset",
|
|
197
|
+
category: "media",
|
|
198
|
+
tags: ["entrance", "scale"],
|
|
199
|
+
duration: 450,
|
|
200
|
+
reducedMotion: true,
|
|
201
|
+
layoutSafe: true,
|
|
202
|
+
status: "stable",
|
|
203
|
+
cssClass: "velin-animate-on-scroll--scale",
|
|
204
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
id: "blur",
|
|
208
|
+
title: "Blur In",
|
|
209
|
+
description: "Soft fade/blur entrance (maps to fade-in utility).",
|
|
210
|
+
kind: "preset",
|
|
211
|
+
category: "overlay",
|
|
212
|
+
tags: ["entrance"],
|
|
213
|
+
duration: 500,
|
|
214
|
+
reducedMotion: true,
|
|
215
|
+
layoutSafe: true,
|
|
216
|
+
status: "beta",
|
|
217
|
+
cssClass: "velin-animate-fade-in",
|
|
218
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: "flip",
|
|
222
|
+
title: "Flip / Scale In",
|
|
223
|
+
description: "Attention scale-in for emphasis.",
|
|
224
|
+
kind: "preset",
|
|
225
|
+
category: "feedback",
|
|
226
|
+
tags: ["attention"],
|
|
227
|
+
duration: 400,
|
|
228
|
+
reducedMotion: true,
|
|
229
|
+
layoutSafe: true,
|
|
230
|
+
status: "beta",
|
|
231
|
+
cssClass: "velin-animate-scale-in",
|
|
232
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
id: "lift",
|
|
236
|
+
title: "Hover Lift",
|
|
237
|
+
description: "Subtle lift on hover for interactive cards and buttons.",
|
|
238
|
+
kind: "preset",
|
|
239
|
+
category: "buttons",
|
|
240
|
+
tags: ["hover"],
|
|
241
|
+
duration: 200,
|
|
242
|
+
reducedMotion: true,
|
|
243
|
+
layoutSafe: true,
|
|
244
|
+
status: "stable",
|
|
245
|
+
cssClass: "velin-animate-hover",
|
|
246
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
id: "pulse",
|
|
250
|
+
title: "Pulse",
|
|
251
|
+
description: "Attention pulse; prefer finite loops under performance policy.",
|
|
252
|
+
kind: "preset",
|
|
253
|
+
category: "feedback",
|
|
254
|
+
tags: ["attention"],
|
|
255
|
+
duration: 1e3,
|
|
256
|
+
reducedMotion: true,
|
|
257
|
+
layoutSafe: true,
|
|
258
|
+
status: "stable",
|
|
259
|
+
cssClass: "velin-animate-pulse",
|
|
260
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
id: "shake",
|
|
264
|
+
title: "Shake",
|
|
265
|
+
description: "Error/attention shake feedback.",
|
|
266
|
+
kind: "preset",
|
|
267
|
+
category: "forms",
|
|
268
|
+
tags: ["attention", "feedback"],
|
|
269
|
+
duration: 400,
|
|
270
|
+
reducedMotion: true,
|
|
271
|
+
layoutSafe: true,
|
|
272
|
+
status: "stable",
|
|
273
|
+
cssClass: "velin-animate-shake",
|
|
274
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
id: "bounce",
|
|
278
|
+
title: "Bounce",
|
|
279
|
+
description: "Playful bounce entrance or attention.",
|
|
280
|
+
kind: "preset",
|
|
281
|
+
category: "feedback",
|
|
282
|
+
tags: ["attention"],
|
|
283
|
+
duration: 600,
|
|
284
|
+
reducedMotion: true,
|
|
285
|
+
layoutSafe: true,
|
|
286
|
+
status: "stable",
|
|
287
|
+
cssClass: "velin-animate-bounce",
|
|
288
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
id: "shimmer",
|
|
292
|
+
title: "Shimmer",
|
|
293
|
+
description: "Loading shimmer surface (skeleton companion).",
|
|
294
|
+
kind: "preset",
|
|
295
|
+
category: "loading",
|
|
296
|
+
tags: ["loading"],
|
|
297
|
+
duration: 1200,
|
|
298
|
+
reducedMotion: true,
|
|
299
|
+
layoutSafe: true,
|
|
300
|
+
status: "stable",
|
|
301
|
+
cssClass: "velin-skeleton",
|
|
302
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
id: "heroReveal",
|
|
306
|
+
title: "Hero Reveal",
|
|
307
|
+
description: "Staggered entrance for hero content; layout-safe; reduced-motion fallback.",
|
|
308
|
+
kind: "preset",
|
|
309
|
+
category: "hero",
|
|
310
|
+
tags: ["entrance", "stagger"],
|
|
311
|
+
duration: 600,
|
|
312
|
+
fps: "60",
|
|
313
|
+
reducedMotion: true,
|
|
314
|
+
layoutSafe: true,
|
|
315
|
+
status: "stable",
|
|
316
|
+
cssClass: "velin-animate-on-scroll--fade",
|
|
317
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
id: "parallax",
|
|
321
|
+
title: "Parallax",
|
|
322
|
+
description: "Scroll-driven parallax shift.",
|
|
323
|
+
kind: "preset",
|
|
324
|
+
category: "media",
|
|
325
|
+
tags: ["scroll"],
|
|
326
|
+
duration: 0,
|
|
327
|
+
reducedMotion: true,
|
|
328
|
+
layoutSafe: false,
|
|
329
|
+
status: "stable",
|
|
330
|
+
cssClass: "velin-parallax",
|
|
331
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
id: "fadeOut",
|
|
335
|
+
title: "Fade Out",
|
|
336
|
+
description: "Exit fade (planned composition with timelines).",
|
|
337
|
+
kind: "preset",
|
|
338
|
+
category: "overlay",
|
|
339
|
+
tags: ["exit"],
|
|
340
|
+
duration: 300,
|
|
341
|
+
reducedMotion: true,
|
|
342
|
+
layoutSafe: true,
|
|
343
|
+
status: "beta",
|
|
344
|
+
cssClass: "velin-animate-fade-out",
|
|
345
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
346
|
+
}
|
|
347
|
+
],
|
|
348
|
+
timelines: [
|
|
349
|
+
{
|
|
350
|
+
id: "hero-intro",
|
|
351
|
+
title: "Hero Intro",
|
|
352
|
+
description: "Title \u2192 subtitle \u2192 buttons \u2192 image",
|
|
353
|
+
kind: "timeline",
|
|
354
|
+
category: "hero",
|
|
355
|
+
steps: ["title", "subtitle", "buttons", "image"],
|
|
356
|
+
status: "beta",
|
|
357
|
+
compatibility: { cssOnly: false, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
id: "card-stagger",
|
|
361
|
+
title: "Card Stagger",
|
|
362
|
+
description: "Sequential card entrance labels.",
|
|
363
|
+
kind: "timeline",
|
|
364
|
+
category: "cards",
|
|
365
|
+
steps: ["card-1", "card-2", "card-3"],
|
|
366
|
+
status: "beta",
|
|
367
|
+
compatibility: { cssOnly: false, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
368
|
+
}
|
|
369
|
+
],
|
|
370
|
+
transitions: [
|
|
371
|
+
{
|
|
372
|
+
id: "cross-fade",
|
|
373
|
+
title: "Cross Fade",
|
|
374
|
+
description: "Page/view cross-fade (View Transition friendly).",
|
|
375
|
+
kind: "transition",
|
|
376
|
+
category: "page",
|
|
377
|
+
status: "planned",
|
|
378
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
id: "modal-enter",
|
|
382
|
+
title: "Modal Enter",
|
|
383
|
+
description: "Overlay modal entrance transition.",
|
|
384
|
+
kind: "transition",
|
|
385
|
+
category: "modal",
|
|
386
|
+
status: "planned",
|
|
387
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
id: "drawer-enter",
|
|
391
|
+
title: "Drawer Enter",
|
|
392
|
+
description: "Drawer slide transition.",
|
|
393
|
+
kind: "transition",
|
|
394
|
+
category: "drawer",
|
|
395
|
+
status: "planned",
|
|
396
|
+
compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
397
|
+
}
|
|
398
|
+
],
|
|
399
|
+
physics: [
|
|
400
|
+
{
|
|
401
|
+
id: "spring",
|
|
402
|
+
title: "Spring",
|
|
403
|
+
description: "Spring easing token mapping (runtime simulation later).",
|
|
404
|
+
kind: "physics",
|
|
405
|
+
category: "feedback",
|
|
406
|
+
status: "planned",
|
|
407
|
+
token: "--velin-ease-spring",
|
|
408
|
+
compatibility: { cssOnly: true, runtime: false, webComponents: false, studio: true, production: true, review: true, cli: true }
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
id: "elastic",
|
|
412
|
+
title: "Elastic",
|
|
413
|
+
description: "Elastic easing token mapping.",
|
|
414
|
+
kind: "physics",
|
|
415
|
+
category: "feedback",
|
|
416
|
+
status: "planned",
|
|
417
|
+
token: "--velin-ease-elastic",
|
|
418
|
+
compatibility: { cssOnly: true, runtime: false, webComponents: false, studio: true, production: true, review: true, cli: true }
|
|
419
|
+
}
|
|
420
|
+
],
|
|
421
|
+
composers: [
|
|
422
|
+
{
|
|
423
|
+
id: "sequence",
|
|
424
|
+
title: "Sequence Composer",
|
|
425
|
+
description: "Ordered fade/wait/slide composition API.",
|
|
426
|
+
kind: "composer",
|
|
427
|
+
category: "page",
|
|
428
|
+
status: "planned",
|
|
429
|
+
compatibility: { cssOnly: false, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true }
|
|
430
|
+
}
|
|
431
|
+
],
|
|
432
|
+
triggers: [
|
|
433
|
+
{ id: "load", title: "Load", description: "Start when document/module initializes.", kind: "trigger", category: "page", status: "stable", compatibility: { cssOnly: false, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
434
|
+
{ id: "visible", title: "Visible", description: "Start when element enters the viewport (IntersectionObserver).", kind: "trigger", category: "page", status: "stable", compatibility: { cssOnly: false, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
435
|
+
{ id: "hidden", title: "Hidden", description: "Fire when element leaves the viewport. (Planned \u2014 runtime falls back to visible.)", kind: "trigger", category: "page", status: "planned", compatibility: { cssOnly: false, runtime: false, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
436
|
+
{ id: "hover", title: "Hover", description: "Pointer enter / hover.", kind: "trigger", category: "buttons", status: "stable", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
437
|
+
{ id: "focus", title: "Focus", description: "Keyboard/pointer focus.", kind: "trigger", category: "forms", status: "stable", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
438
|
+
{ id: "click", title: "Click", description: "Pointer activation.", kind: "trigger", category: "buttons", status: "stable", compatibility: { cssOnly: false, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
439
|
+
{ id: "scroll", title: "Scroll", description: "Scroll-linked or scroll-progress trigger.", kind: "trigger", category: "page", status: "stable", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
440
|
+
{ id: "idle", title: "Idle", description: "After user idle timeout. (Planned \u2014 runtime falls back to visible.)", kind: "trigger", category: "feedback", status: "planned", compatibility: { cssOnly: false, runtime: false, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
441
|
+
{ id: "media", title: "Media", description: "Match media query condition. (Planned \u2014 runtime falls back to visible.)", kind: "trigger", category: "page", status: "planned", compatibility: { cssOnly: false, runtime: false, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
442
|
+
{ id: "container", title: "Container", description: "Container query / size trigger. (Planned \u2014 runtime falls back to visible.)", kind: "trigger", category: "cards", status: "planned", compatibility: { cssOnly: false, runtime: false, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
443
|
+
{ id: "theme", title: "Theme", description: "Theme change trigger. (Planned \u2014 runtime falls back to visible.)", kind: "trigger", category: "page", status: "planned", compatibility: { cssOnly: false, runtime: false, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
444
|
+
{ id: "custom", title: "Custom", description: "Custom event name via velin-trigger-event. (Planned \u2014 runtime falls back to visible.)", kind: "trigger", category: "page", status: "planned", compatibility: { cssOnly: false, runtime: false, webComponents: true, studio: true, production: true, review: true, cli: true } }
|
|
445
|
+
],
|
|
446
|
+
policies: [
|
|
447
|
+
{ id: "safe", title: "Safe", description: "Default layout-safe motion with capped durations.", kind: "policy", category: "page", status: "stable", maxDurationMs: 800, allowInfinite: false, compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
448
|
+
{ id: "reduced", title: "Reduced", description: "Honors prefers-reduced-motion first; shorten or skip decorative motion.", kind: "policy", category: "page", status: "stable", maxDurationMs: 200, forceReduced: true, compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
449
|
+
{ id: "marketing", title: "Marketing", description: "Expressive hero motion allowed within caps.", kind: "policy", category: "hero", status: "stable", maxDurationMs: 1200, allowInfinite: false, compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
450
|
+
{ id: "minimal", title: "Minimal", description: "Very little motion; feedback only.", kind: "policy", category: "page", status: "stable", maxDurationMs: 300, allowInfinite: false, compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
451
|
+
{ id: "accessibility", title: "Accessibility", description: "Strict reduced-motion + focus-safe interactions.", kind: "policy", category: "page", status: "stable", maxDurationMs: 200, forceReduced: true, compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
452
|
+
{ id: "performance", title: "Performance", description: "FPS-friendly; ban infinite decorative loops.", kind: "policy", category: "page", status: "stable", maxDurationMs: 600, allowInfinite: false, compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
453
|
+
{ id: "disabled", title: "Disabled", description: "All motion off.", kind: "policy", category: "page", status: "stable", disabled: true, compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } }
|
|
454
|
+
],
|
|
455
|
+
blueprints: [
|
|
456
|
+
{ id: "landing", title: "Landing", description: "Hero reveal + staggered sections + CTA hover.", kind: "blueprint", category: "hero", status: "beta", presets: ["heroReveal", "fade", "lift"], timeline: "hero-intro", policy: "marketing", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
457
|
+
{ id: "dashboard", title: "Dashboard", description: "Minimal card stagger for admin shells.", kind: "blueprint", category: "cards", status: "beta", presets: ["fade", "slide"], timeline: "card-stagger", policy: "minimal", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
458
|
+
{ id: "pricing", title: "Pricing", description: "Pricing cards with stagger and lift.", kind: "blueprint", category: "cards", status: "beta", presets: ["slide", "lift"], policy: "marketing", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
459
|
+
{ id: "portfolio", title: "Portfolio", description: "Media zoom + parallax-safe reveals.", kind: "blueprint", category: "media", status: "beta", presets: ["zoom", "fade"], policy: "marketing", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
460
|
+
{ id: "shop", title: "Shop", description: "Product cards and feedback pulses.", kind: "blueprint", category: "cards", status: "planned", presets: ["fade", "lift"], policy: "safe", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
461
|
+
{ id: "forum", title: "Forum", description: "List slides and form feedback.", kind: "blueprint", category: "lists", status: "planned", presets: ["slide", "shake"], policy: "safe", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
462
|
+
{ id: "docs", title: "Docs", description: "Minimal fade for documentation pages.", kind: "blueprint", category: "page", status: "beta", presets: ["fade"], policy: "minimal", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
463
|
+
{ id: "blog", title: "Blog", description: "Article hero fade and card slides.", kind: "blueprint", category: "hero", status: "planned", presets: ["fade", "slide"], policy: "safe", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
464
|
+
{ id: "crm", title: "CRM", description: "Table-safe minimal motion.", kind: "blueprint", category: "tables", status: "planned", presets: ["fade"], policy: "performance", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } },
|
|
465
|
+
{ id: "login", title: "Login", description: "Form feedback and focus-safe motion.", kind: "blueprint", category: "forms", status: "beta", presets: ["fade", "shake"], policy: "accessibility", compatibility: { cssOnly: true, runtime: true, webComponents: true, studio: true, production: true, review: true, cli: true } }
|
|
466
|
+
]
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// core/motion/registry.js
|
|
470
|
+
var cached = registry_default;
|
|
471
|
+
function loadMotionRegistry(override = null) {
|
|
472
|
+
if (override) {
|
|
473
|
+
cached = override;
|
|
474
|
+
return cached;
|
|
475
|
+
}
|
|
476
|
+
return cached || registry_default;
|
|
477
|
+
}
|
|
478
|
+
function listMotion(namespace, filter = {}) {
|
|
479
|
+
const reg = loadMotionRegistry();
|
|
480
|
+
const key = namespace.endsWith("s") ? namespace : `${namespace}s`;
|
|
481
|
+
const items = reg[key] || reg[namespace] || [];
|
|
482
|
+
return items.filter((item) => {
|
|
483
|
+
if (filter.status && item.status !== filter.status) return false;
|
|
484
|
+
if (filter.category && item.category !== filter.category) return false;
|
|
485
|
+
if (filter.tag && !(item.tags || []).includes(filter.tag)) return false;
|
|
486
|
+
return true;
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
function getMotion(namespace, id) {
|
|
490
|
+
return listMotion(namespace).find((item) => item.id === id) || null;
|
|
491
|
+
}
|
|
492
|
+
function getPreset(id) {
|
|
493
|
+
return getMotion("presets", id);
|
|
494
|
+
}
|
|
495
|
+
function getPolicy(id) {
|
|
496
|
+
return getMotion("policies", id);
|
|
497
|
+
}
|
|
498
|
+
function getTrigger(id) {
|
|
499
|
+
return getMotion("triggers", id);
|
|
500
|
+
}
|
|
501
|
+
function getTimeline(id) {
|
|
502
|
+
return getMotion("timelines", id);
|
|
503
|
+
}
|
|
504
|
+
function resolvePresetId(id) {
|
|
505
|
+
if (!id) return null;
|
|
506
|
+
const direct = getPreset(id);
|
|
507
|
+
if (direct) return direct;
|
|
508
|
+
const aliases = {
|
|
509
|
+
scale: "zoom",
|
|
510
|
+
"hover-lift": "lift",
|
|
511
|
+
reveal: "fade"
|
|
512
|
+
};
|
|
513
|
+
const mapped = aliases[id];
|
|
514
|
+
return mapped ? getPreset(mapped) : null;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// core/motion/triggers.js
|
|
518
|
+
var TRIGGER_V1 = ["load", "visible", "hover", "focus", "click", "scroll"];
|
|
519
|
+
var TRIGGER_V2 = ["hidden", "idle", "media", "container", "theme", "custom"];
|
|
520
|
+
var ALL_TRIGGERS = [...TRIGGER_V1, ...TRIGGER_V2];
|
|
521
|
+
function readTrigger(el, fallback = "visible") {
|
|
522
|
+
const raw = (el.getAttribute("velin-trigger") || fallback || "visible").trim().toLowerCase();
|
|
523
|
+
return raw || fallback;
|
|
524
|
+
}
|
|
525
|
+
function bindTrigger(el, triggerId, onFire, opts = {}) {
|
|
526
|
+
const once = opts.once !== false;
|
|
527
|
+
const reduced = opts.reduced ?? prefersReducedMotion();
|
|
528
|
+
const id = (triggerId || "visible").toLowerCase();
|
|
529
|
+
const meta = getTrigger(id);
|
|
530
|
+
if (reduced || el.dataset.velinMotion === "off") {
|
|
531
|
+
onFire();
|
|
532
|
+
return () => {
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
if (meta?.status === "planned" && !TRIGGER_V1.includes(id)) {
|
|
536
|
+
return bindTrigger(el, "visible", onFire, opts);
|
|
537
|
+
}
|
|
538
|
+
let fired = false;
|
|
539
|
+
const fire = () => {
|
|
540
|
+
if (once && fired) return;
|
|
541
|
+
fired = true;
|
|
542
|
+
onFire();
|
|
543
|
+
};
|
|
544
|
+
if (id === "load") {
|
|
545
|
+
fire();
|
|
546
|
+
return () => {
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
if (id === "visible" || id === "scroll") {
|
|
550
|
+
return observeInView(el, () => fire());
|
|
551
|
+
}
|
|
552
|
+
if (id === "hover") {
|
|
553
|
+
const enter = () => fire();
|
|
554
|
+
el.addEventListener("pointerenter", enter);
|
|
555
|
+
return () => el.removeEventListener("pointerenter", enter);
|
|
556
|
+
}
|
|
557
|
+
if (id === "focus") {
|
|
558
|
+
const onFocus = () => fire();
|
|
559
|
+
el.addEventListener("focusin", onFocus);
|
|
560
|
+
return () => el.removeEventListener("focusin", onFocus);
|
|
561
|
+
}
|
|
562
|
+
if (id === "click") {
|
|
563
|
+
const onClick = () => fire();
|
|
564
|
+
el.addEventListener("click", onClick);
|
|
565
|
+
return () => el.removeEventListener("click", onClick);
|
|
566
|
+
}
|
|
567
|
+
if (id === "hidden") {
|
|
568
|
+
const stop = observeInView(el, () => {
|
|
569
|
+
});
|
|
570
|
+
if (typeof IntersectionObserver === "undefined") return () => {
|
|
571
|
+
};
|
|
572
|
+
const io = new IntersectionObserver((entries) => {
|
|
573
|
+
for (const entry of entries) {
|
|
574
|
+
if (!entry.isIntersecting) fire();
|
|
575
|
+
}
|
|
576
|
+
}, { threshold: 0 });
|
|
577
|
+
io.observe(el);
|
|
578
|
+
return () => {
|
|
579
|
+
stop();
|
|
580
|
+
io.disconnect();
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
if (id === "idle") {
|
|
584
|
+
const ms = Number(el.getAttribute("velin-trigger-delay") || 3e3);
|
|
585
|
+
const t = setTimeout(fire, ms);
|
|
586
|
+
return () => clearTimeout(t);
|
|
587
|
+
}
|
|
588
|
+
if (id === "media") {
|
|
589
|
+
const q = el.getAttribute("velin-trigger-media") || "(min-width: 768px)";
|
|
590
|
+
if (typeof window === "undefined" || !window.matchMedia) {
|
|
591
|
+
fire();
|
|
592
|
+
return () => {
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
const mql = window.matchMedia(q);
|
|
596
|
+
const handler = () => {
|
|
597
|
+
if (mql.matches) fire();
|
|
598
|
+
};
|
|
599
|
+
handler();
|
|
600
|
+
mql.addEventListener?.("change", handler);
|
|
601
|
+
return () => mql.removeEventListener?.("change", handler);
|
|
602
|
+
}
|
|
603
|
+
if (id === "theme") {
|
|
604
|
+
const root = el.ownerDocument?.documentElement;
|
|
605
|
+
if (!root || typeof MutationObserver === "undefined") {
|
|
606
|
+
fire();
|
|
607
|
+
return () => {
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
const mo = new MutationObserver(() => fire());
|
|
611
|
+
mo.observe(root, { attributes: true, attributeFilter: ["data-velin-theme", "class"] });
|
|
612
|
+
return () => mo.disconnect();
|
|
613
|
+
}
|
|
614
|
+
if (id === "custom") {
|
|
615
|
+
const evt = el.getAttribute("velin-trigger-event") || "velin:motion";
|
|
616
|
+
const handler = () => fire();
|
|
617
|
+
el.addEventListener(evt, handler);
|
|
618
|
+
return () => el.removeEventListener(evt, handler);
|
|
619
|
+
}
|
|
620
|
+
if (id === "container") {
|
|
621
|
+
return observeInView(el, () => fire());
|
|
622
|
+
}
|
|
623
|
+
return observeInView(el, () => fire());
|
|
624
|
+
}
|
|
625
|
+
function armMotionElement(el, applyPreset) {
|
|
626
|
+
applyPreset();
|
|
627
|
+
const trigger = readTrigger(el, el.hasAttribute("velin-hover") ? "hover" : "visible");
|
|
628
|
+
if (prefersReducedMotion() || el.dataset.velinMotion === "off") {
|
|
629
|
+
markVisible(el);
|
|
630
|
+
return () => {
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
if (trigger === "hover" || trigger === "focus") {
|
|
634
|
+
markVisible(el);
|
|
635
|
+
return () => {
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
if (trigger === "load") {
|
|
639
|
+
markVisible(el);
|
|
640
|
+
return () => {
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
return bindTrigger(el, trigger, () => markVisible(el));
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// core/motion/policy.js
|
|
647
|
+
function resolvePolicy(id = "safe") {
|
|
648
|
+
return getPolicy(id) || getPolicy("safe");
|
|
649
|
+
}
|
|
650
|
+
function shouldDisableMotion(policy) {
|
|
651
|
+
if (!policy) return prefersReducedMotion();
|
|
652
|
+
if (policy.disabled) return true;
|
|
653
|
+
if (policy.forceReduced && prefersReducedMotion()) return true;
|
|
654
|
+
if (prefersReducedMotion() && policy.id !== "marketing") return true;
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
function capDuration(ms, policy) {
|
|
658
|
+
const max = policy?.maxDurationMs ?? 800;
|
|
659
|
+
if (policy?.disabled) return 0;
|
|
660
|
+
return Math.min(Number(ms) || 0, max);
|
|
661
|
+
}
|
|
662
|
+
function applyMotionVars(el, policy) {
|
|
663
|
+
const duration = el.getAttribute("velin-duration");
|
|
664
|
+
const delay = el.getAttribute("velin-delay");
|
|
665
|
+
const ease = el.getAttribute("velin-ease");
|
|
666
|
+
if (duration) {
|
|
667
|
+
const capped = capDuration(Number(duration), policy);
|
|
668
|
+
el.style.setProperty("--velin-motion-duration", `${capped}ms`);
|
|
669
|
+
el.style.animationDuration = `${capped}ms`;
|
|
670
|
+
}
|
|
671
|
+
if (delay) el.style.animationDelay = `${Number(delay)}ms`;
|
|
672
|
+
if (ease) {
|
|
673
|
+
const map = {
|
|
674
|
+
spring: "var(--velin-ease-spring)",
|
|
675
|
+
bounce: "var(--velin-ease-bounce)",
|
|
676
|
+
elastic: "var(--velin-ease-elastic)",
|
|
677
|
+
default: "var(--velin-ease-default)"
|
|
678
|
+
};
|
|
679
|
+
el.style.animationTimingFunction = map[ease] || ease;
|
|
680
|
+
}
|
|
681
|
+
if (policy?.disabled || shouldDisableMotion(policy)) {
|
|
682
|
+
el.dataset.velinMotion = "off";
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// core/motion/timeline.js
|
|
687
|
+
function runTimeline(timelineId, root, opts = {}) {
|
|
688
|
+
const tl = getTimeline(timelineId);
|
|
689
|
+
if (!tl) return { ok: false, error: `Unknown timeline "${timelineId}"`, ran: [] };
|
|
690
|
+
const stepMs = opts.stepMs ?? 120;
|
|
691
|
+
const ran = [];
|
|
692
|
+
if (opts.policyDisabled || prefersReducedMotion()) {
|
|
693
|
+
for (const step of tl.steps || []) {
|
|
694
|
+
const el = resolveStep(root, step);
|
|
695
|
+
if (el) markVisible(el);
|
|
696
|
+
ran.push(step);
|
|
697
|
+
}
|
|
698
|
+
return { ok: true, ran };
|
|
699
|
+
}
|
|
700
|
+
let delay = 0;
|
|
701
|
+
for (const step of tl.steps || []) {
|
|
702
|
+
const el = resolveStep(root, step);
|
|
703
|
+
if (!el) continue;
|
|
704
|
+
const d = delay;
|
|
705
|
+
delay += stepMs;
|
|
706
|
+
if (typeof window !== "undefined" && window.setTimeout) {
|
|
707
|
+
window.setTimeout(() => markVisible(el), d);
|
|
708
|
+
} else {
|
|
709
|
+
markVisible(el);
|
|
710
|
+
}
|
|
711
|
+
ran.push(step);
|
|
712
|
+
}
|
|
713
|
+
return { ok: true, ran, timeline: tl };
|
|
714
|
+
}
|
|
715
|
+
function resolveStep(root, step) {
|
|
716
|
+
if (!root?.querySelector) return null;
|
|
717
|
+
return root.querySelector(`[data-velin-step="${step}"]`) || root.querySelector(`[velin-step="${step}"]`) || root.querySelector(`#${CSS.escape?.(step) || step}`);
|
|
718
|
+
}
|
|
719
|
+
function enhanceTimelines(root) {
|
|
720
|
+
if (!root?.querySelectorAll) return () => {
|
|
721
|
+
};
|
|
722
|
+
const nodes = root.querySelectorAll("[velin-timeline]");
|
|
723
|
+
const teardowns = [];
|
|
724
|
+
for (const el of nodes) {
|
|
725
|
+
const id = el.getAttribute("velin-timeline");
|
|
726
|
+
const result = runTimeline(id, el);
|
|
727
|
+
teardowns.push(() => result);
|
|
728
|
+
}
|
|
729
|
+
return () => {
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// core/motion/composer.js
|
|
734
|
+
function enhanceSequences(root) {
|
|
735
|
+
if (!root?.querySelectorAll) return () => {
|
|
736
|
+
};
|
|
737
|
+
const nodes = [...root.querySelectorAll("[velin-motion-sequence]")];
|
|
738
|
+
for (const section of nodes) {
|
|
739
|
+
const children = [...section.children];
|
|
740
|
+
let i = 0;
|
|
741
|
+
const stepMs = Number(section.getAttribute("velin-sequence-gap") || 120);
|
|
742
|
+
for (const child of children) {
|
|
743
|
+
child.classList.add("velin-animate-on-scroll", "velin-animate-on-scroll--fade");
|
|
744
|
+
if (prefersReducedMotion()) {
|
|
745
|
+
markVisible(child);
|
|
746
|
+
} else if (typeof setTimeout !== "undefined") {
|
|
747
|
+
const delayMs = i * stepMs;
|
|
748
|
+
setTimeout(() => markVisible(child), delayMs);
|
|
749
|
+
} else {
|
|
750
|
+
markVisible(child);
|
|
751
|
+
}
|
|
752
|
+
i += 1;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return () => {
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// core/motion/index.js
|
|
760
|
+
var LEGACY_ATTRS = ["velin-reveal", "velin-fade", "velin-slide", "velin-scale", "velin-parallax", "velin-hover"];
|
|
761
|
+
var teardownFns = [];
|
|
762
|
+
function initMotion(options = {}) {
|
|
763
|
+
const root = options.root || (typeof document !== "undefined" ? document : null);
|
|
764
|
+
if (!root) return () => {
|
|
765
|
+
};
|
|
766
|
+
const policy = resolvePolicy(
|
|
767
|
+
options.policy || root.documentElement?.getAttribute?.("data-velin-motion-policy") || root.querySelector?.("[data-velin-motion-policy]")?.getAttribute("data-velin-motion-policy") || "safe"
|
|
768
|
+
);
|
|
769
|
+
const selector = options.selector || [
|
|
770
|
+
"[velin-motion]",
|
|
771
|
+
"[velin-reveal]",
|
|
772
|
+
"[velin-fade]",
|
|
773
|
+
"[velin-slide]",
|
|
774
|
+
"[velin-scale]",
|
|
775
|
+
"[velin-parallax]",
|
|
776
|
+
"[velin-hover]",
|
|
777
|
+
".velin-animate-on-scroll"
|
|
778
|
+
].join(",");
|
|
779
|
+
const elements = root.querySelectorAll(selector);
|
|
780
|
+
const reduced = shouldDisableMotion(policy) || prefersReducedMotion();
|
|
781
|
+
for (const el of elements) {
|
|
782
|
+
if (el.dataset.velinMotionInit) continue;
|
|
783
|
+
el.dataset.velinMotionInit = "1";
|
|
784
|
+
applyMotionVars(el, policy);
|
|
785
|
+
const attrs = {};
|
|
786
|
+
const motionPreset = el.getAttribute("velin-motion");
|
|
787
|
+
if (motionPreset) {
|
|
788
|
+
attrs.motion = motionPreset;
|
|
789
|
+
const resolved = resolvePresetId(motionPreset);
|
|
790
|
+
if (resolved?.id) attrs[resolved.id] = "true";
|
|
791
|
+
}
|
|
792
|
+
for (const name of LEGACY_ATTRS) {
|
|
793
|
+
if (el.hasAttribute(name)) attrs[name.replace("velin-", "")] = el.getAttribute(name) || "true";
|
|
794
|
+
}
|
|
795
|
+
applyEffects(el, attrs);
|
|
796
|
+
if (reduced || el.dataset.velinMotion === "off") {
|
|
797
|
+
markVisible(el);
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
const stop = armMotionElement(el, () => applyEffects(el, attrs));
|
|
801
|
+
teardownFns.push(stop);
|
|
802
|
+
}
|
|
803
|
+
enhanceStagger(root);
|
|
804
|
+
bindSmoothScroll(root);
|
|
805
|
+
enhanceTimelines(root);
|
|
806
|
+
enhanceSequences(root);
|
|
807
|
+
return () => {
|
|
808
|
+
teardownFns.forEach((fn) => {
|
|
809
|
+
try {
|
|
810
|
+
fn();
|
|
811
|
+
} catch {
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
teardownFns = [];
|
|
815
|
+
disconnectInViewObserver();
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// core/attributes/registry.js
|
|
820
|
+
var registry = /* @__PURE__ */ new Map();
|
|
821
|
+
function registerAttribute(name, handler) {
|
|
822
|
+
registry.set(name, handler);
|
|
823
|
+
}
|
|
824
|
+
function getAttributeHandler(name) {
|
|
825
|
+
return registry.get(name);
|
|
826
|
+
}
|
|
827
|
+
function bridgeComponent(el, tag, attrs = {}) {
|
|
828
|
+
el.setAttribute("data-velin-component", tag);
|
|
829
|
+
for (const [k, v] of Object.entries(attrs)) {
|
|
830
|
+
if (v != null) el.setAttribute(k, v);
|
|
831
|
+
}
|
|
832
|
+
return lazyDefine(tag);
|
|
833
|
+
}
|
|
834
|
+
function registerBuiltins() {
|
|
835
|
+
registerAttribute("velin-modal", {
|
|
836
|
+
enhance(el) {
|
|
837
|
+
bridgeComponent(el, "velin-modal", { open: el.getAttribute("velin-modal") || "" });
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
registerAttribute("velin-tabs", { enhance: (el) => bridgeComponent(el, "velin-tabs") });
|
|
841
|
+
registerAttribute("velin-accordion", { enhance: (el) => bridgeComponent(el, "velin-accordion") });
|
|
842
|
+
registerAttribute("velin-tooltip", {
|
|
843
|
+
enhance(el) {
|
|
844
|
+
const tip = el.getAttribute("velin-tooltip") || el.getAttribute("title") || "";
|
|
845
|
+
if (tip && el.hasAttribute("title")) el.removeAttribute("title");
|
|
846
|
+
if (el.tagName === "VELIN-TOOLTIP" || el.tagName === "VELIN-TOOLTIP-WC") {
|
|
847
|
+
el.setAttribute("content", tip);
|
|
848
|
+
return lazyDefine("velin-tooltip");
|
|
849
|
+
}
|
|
850
|
+
const wc = document.createElement("velin-tooltip");
|
|
851
|
+
wc.setAttribute("content", tip);
|
|
852
|
+
const parent = el.parentElement;
|
|
853
|
+
if (parent) {
|
|
854
|
+
parent.insertBefore(wc, el);
|
|
855
|
+
wc.appendChild(el);
|
|
856
|
+
}
|
|
857
|
+
return lazyDefine("velin-tooltip");
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
registerAttribute("velin-copy", {
|
|
861
|
+
enhance(el) {
|
|
862
|
+
const text = el.getAttribute("velin-copy") || el.textContent?.trim() || "";
|
|
863
|
+
if (!el.querySelector("velin-copy")) {
|
|
864
|
+
const wc = document.createElement("velin-copy");
|
|
865
|
+
wc.setAttribute("value", text);
|
|
866
|
+
if (el.tagName === "BUTTON") {
|
|
867
|
+
wc.append(...el.childNodes);
|
|
868
|
+
el.replaceWith(wc);
|
|
869
|
+
return lazyDefine("velin-copy");
|
|
870
|
+
}
|
|
871
|
+
el.appendChild(wc);
|
|
872
|
+
}
|
|
873
|
+
return lazyDefine("velin-copy");
|
|
874
|
+
}
|
|
875
|
+
});
|
|
876
|
+
registerAttribute("velin-counter", {
|
|
877
|
+
enhance(el) {
|
|
878
|
+
bridgeComponent(el, "velin-counter", {
|
|
879
|
+
value: el.getAttribute("velin-counter") || "0"
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
});
|
|
883
|
+
registerAttribute("velin-notify", {
|
|
884
|
+
async enhance(el) {
|
|
885
|
+
await lazyDefine("velin-toast");
|
|
886
|
+
el.addEventListener("click", () => {
|
|
887
|
+
const msg = el.getAttribute("velin-notify") || el.textContent || "";
|
|
888
|
+
document.dispatchEvent(
|
|
889
|
+
new CustomEvent("velin-toast-show", { detail: { message: msg, variant: el.dataset.variant || "info" } })
|
|
890
|
+
);
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
registerAttribute("velin-theme", {
|
|
895
|
+
enhance(el) {
|
|
896
|
+
const theme = el.getAttribute("velin-theme") || "toggle";
|
|
897
|
+
if (theme === "toggle") {
|
|
898
|
+
if (!el.querySelector("velin-theme-toggle")) {
|
|
899
|
+
el.appendChild(document.createElement("velin-theme-toggle"));
|
|
900
|
+
}
|
|
901
|
+
return lazyDefine("velin-theme-toggle");
|
|
902
|
+
}
|
|
903
|
+
document.documentElement.setAttribute("data-velin-theme", theme);
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
registerAttribute("velin-scroll-top", {
|
|
907
|
+
enhance(el) {
|
|
908
|
+
const raw = el.getAttribute("velin-scroll-top");
|
|
909
|
+
const threshold = raw && raw !== "true" ? raw : "300";
|
|
910
|
+
if (el.tagName === "VELIN-SCROLL-TOP") {
|
|
911
|
+
if (raw && raw !== "true") el.setAttribute("threshold", threshold);
|
|
912
|
+
return lazyDefine("velin-scroll-top");
|
|
913
|
+
}
|
|
914
|
+
let wc = document.querySelector("velin-scroll-top");
|
|
915
|
+
if (!wc) {
|
|
916
|
+
wc = document.createElement("velin-scroll-top");
|
|
917
|
+
wc.setAttribute("threshold", threshold);
|
|
918
|
+
(el === document.body || el === document.documentElement ? document.body : el).appendChild(wc);
|
|
919
|
+
}
|
|
920
|
+
return lazyDefine("velin-scroll-top");
|
|
921
|
+
}
|
|
922
|
+
});
|
|
923
|
+
registerAttribute("velin-progress", {
|
|
924
|
+
enhance(el) {
|
|
925
|
+
const ring = el.hasAttribute("ring");
|
|
926
|
+
if (ring) return bridgeComponent(el, "velin-progress-ring");
|
|
927
|
+
el.classList.add("velin-progress");
|
|
928
|
+
const val = parseInt(el.getAttribute("velin-progress") || "0", 10);
|
|
929
|
+
el.setAttribute("role", "progressbar");
|
|
930
|
+
el.setAttribute("aria-valuenow", String(val));
|
|
931
|
+
el.style.setProperty("--velin-progress", `${Math.min(100, val)}%`);
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
registerAttribute("velin-search", {
|
|
935
|
+
async enhance(el) {
|
|
936
|
+
if (el.tagName !== "VELIN-SEARCH") {
|
|
937
|
+
const host = document.createElement("velin-search");
|
|
938
|
+
host.setAttribute("index", el.getAttribute("data-search-index") || "/search-index.json");
|
|
939
|
+
el.replaceWith(host);
|
|
940
|
+
host.appendChild(el);
|
|
941
|
+
}
|
|
942
|
+
return lazyDefine("velin-search");
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
registerAttribute("velin-lazy", {
|
|
946
|
+
enhance(el) {
|
|
947
|
+
if (el.tagName === "IMG") {
|
|
948
|
+
el.loading = "lazy";
|
|
949
|
+
el.decoding = "async";
|
|
950
|
+
if (el.hasAttribute("velin-skeleton") || el.dataset.velinSkeleton) {
|
|
951
|
+
el.classList.add("velin-skeleton", "velin-skeleton--image");
|
|
952
|
+
el.addEventListener("load", () => el.classList.remove("velin-skeleton", "velin-skeleton--image"), { once: true });
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
registerAttribute("velin-skeleton", {
|
|
958
|
+
enhance(el) {
|
|
959
|
+
const variant = el.getAttribute("velin-skeleton") || "text";
|
|
960
|
+
el.classList.add("velin-skeleton", `velin-skeleton--${variant}`);
|
|
961
|
+
const hasText = Boolean(el.textContent?.trim()) && el.children.length > 0;
|
|
962
|
+
if (!hasText && !el.textContent?.trim()) {
|
|
963
|
+
el.setAttribute("aria-hidden", "true");
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
registerAttribute("velin-loading", {
|
|
968
|
+
enhance(el) {
|
|
969
|
+
el.classList.add("velin-spinner");
|
|
970
|
+
el.setAttribute("aria-busy", "true");
|
|
971
|
+
el.setAttribute("role", "status");
|
|
972
|
+
if (!el.getAttribute("aria-label")?.trim()) {
|
|
973
|
+
el.setAttribute("aria-label", "Loading");
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
});
|
|
977
|
+
registerAttribute("velin-grid", {
|
|
978
|
+
enhance(el) {
|
|
979
|
+
const cols = el.getAttribute("velin-grid") || "auto";
|
|
980
|
+
el.classList.add("velin-grid");
|
|
981
|
+
if (cols !== "auto") el.style.setProperty("--velin-grid-cols", cols);
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
registerAttribute("velin-anchor", {
|
|
985
|
+
enhance(el) {
|
|
986
|
+
if (!el.id && el.getAttribute("velin-anchor")) el.id = el.getAttribute("velin-anchor");
|
|
987
|
+
el.setAttribute("tabindex", "-1");
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
registerAttribute("velin-code", {
|
|
991
|
+
async enhance(el) {
|
|
992
|
+
const lang = el.getAttribute("velin-code") || el.getAttribute("language") || el.getAttribute("data-language") || "";
|
|
993
|
+
el.classList.add("velin-code-block");
|
|
994
|
+
if (lang && lang !== "true") {
|
|
995
|
+
el.dataset.language = lang;
|
|
996
|
+
if (!el.getAttribute("language")) el.setAttribute("language", lang);
|
|
997
|
+
}
|
|
998
|
+
if (!el.querySelector("[data-velin-copy]")) {
|
|
999
|
+
const btn = document.createElement("button");
|
|
1000
|
+
btn.type = "button";
|
|
1001
|
+
btn.className = "velin-code-block__copy velin-btn velin-btn--sm";
|
|
1002
|
+
btn.textContent = "Copy";
|
|
1003
|
+
btn.setAttribute("data-velin-copy", "");
|
|
1004
|
+
btn.setAttribute("velin-copy", el.querySelector("code")?.textContent || el.textContent || "");
|
|
1005
|
+
el.style.position = "relative";
|
|
1006
|
+
el.appendChild(btn);
|
|
1007
|
+
}
|
|
1008
|
+
await lazyDefine("velin-copy");
|
|
1009
|
+
const { initHighlight, highlightElement } = await import("./highlight-M2ELQNYK.js");
|
|
1010
|
+
const immediate = el.getAttribute("data-velin-highlight") === "immediate" || typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1011
|
+
if (immediate) {
|
|
1012
|
+
await highlightElement(el);
|
|
1013
|
+
} else {
|
|
1014
|
+
initHighlight(el, { root: el });
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
registerAttribute("velin-quote", {
|
|
1019
|
+
enhance(el) {
|
|
1020
|
+
if (el.tagName === "BLOCKQUOTE" || el.tagName === "Q") {
|
|
1021
|
+
el.classList.add("velin-quote");
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
registerAttribute("velin-highlight", {
|
|
1026
|
+
enhance(el) {
|
|
1027
|
+
el.classList.add("velin-highlight");
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1030
|
+
registerAttribute("velin-transparency", {
|
|
1031
|
+
async enhance(el) {
|
|
1032
|
+
const { attach } = await import("./attach-H2ZSEAE6.js");
|
|
1033
|
+
attach(el);
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
registerAttribute("velin-disclosure", {
|
|
1037
|
+
async enhance(el) {
|
|
1038
|
+
const { attach } = await import("./attach-H2ZSEAE6.js");
|
|
1039
|
+
if (!el.hasAttribute("velin-transparency")) el.setAttribute("velin-transparency", "");
|
|
1040
|
+
attach(el);
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
for (const motion of ["velin-motion", "velin-trigger", "velin-duration", "velin-delay", "velin-ease", "velin-timeline", "velin-motion-sequence", "velin-reveal", "velin-fade", "velin-slide", "velin-scale", "velin-parallax", "velin-hover", "velin-stagger", "velin-scroll"]) {
|
|
1044
|
+
registerAttribute(motion, { enhance() {
|
|
1045
|
+
} });
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
registerBuiltins();
|
|
1049
|
+
var enhanced = /* @__PURE__ */ new WeakSet();
|
|
1050
|
+
async function bootAttributes(root = document) {
|
|
1051
|
+
const selector = [...registry.keys()].map((a) => `[${a}]`).join(",");
|
|
1052
|
+
const elements = root.querySelectorAll(selector);
|
|
1053
|
+
for (const el of elements) {
|
|
1054
|
+
if (enhanced.has(el)) continue;
|
|
1055
|
+
for (const attr of registry.keys()) {
|
|
1056
|
+
if (!el.hasAttribute(attr)) continue;
|
|
1057
|
+
const handler = registry.get(attr);
|
|
1058
|
+
if (handler?.enhance) await handler.enhance(el);
|
|
1059
|
+
}
|
|
1060
|
+
enhanced.add(el);
|
|
1061
|
+
}
|
|
1062
|
+
initMotion({ root });
|
|
1063
|
+
if (typeof HTMLElement !== "undefined") {
|
|
1064
|
+
const { bindDeclarativeSearch } = await import("./velin-search-L3UG6NDQ.js");
|
|
1065
|
+
bindDeclarativeSearch(root);
|
|
1066
|
+
if (!document.querySelector("velin-announcer")) {
|
|
1067
|
+
const { getAnnouncer } = await import("./a11y-utils-IRC7LOPF.js");
|
|
1068
|
+
getAnnouncer();
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
function listRegisteredAttributes() {
|
|
1073
|
+
return [...registry.keys()];
|
|
1074
|
+
}
|
|
1075
|
+
export {
|
|
1076
|
+
bootAttributes,
|
|
1077
|
+
getAttributeHandler,
|
|
1078
|
+
listRegisteredAttributes,
|
|
1079
|
+
registerAttribute
|
|
1080
|
+
};
|