@birdapi/velinstyle 0.7.0 → 0.8.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.
Files changed (49) hide show
  1. package/README.de.md +26 -4
  2. package/README.md +26 -4
  3. package/cli/blueprint.js +8 -0
  4. package/cli/blueprints/bottom-nav-mobile.html +17 -0
  5. package/cli/blueprints/cookie-consent.html +9 -0
  6. package/cli/blueprints/empty-state.html +5 -0
  7. package/cli/blueprints/filter-bar.html +15 -0
  8. package/cli/blueprints/notification-center.html +13 -0
  9. package/cli/blueprints/onboarding.html +23 -0
  10. package/cli/blueprints/pricing-table.html +20 -0
  11. package/cli/blueprints/settings-panel.html +20 -0
  12. package/cli/index.js +116 -1
  13. package/cli/layout-audit.js +325 -0
  14. package/cli/scaffold-recipes.json +70 -0
  15. package/cli/scaffold.js +155 -0
  16. package/cli/scanner.js +68 -0
  17. package/components/index.js +19 -0
  18. package/components/sanitize.js +29 -3
  19. package/components/shadow-a11y-styles.js +18 -0
  20. package/components/velin-announcer.js +35 -0
  21. package/components/velin-bottom-nav.js +89 -0
  22. package/components/velin-combobox.js +149 -0
  23. package/components/velin-command.js +127 -0
  24. package/components/velin-counter.js +152 -0
  25. package/components/velin-flip.js +220 -0
  26. package/components/velin-icon.js +43 -9
  27. package/components/velin-live-dot.js +85 -0
  28. package/components/velin-menubar.js +83 -0
  29. package/components/velin-rating.js +91 -0
  30. package/components/velin-reveal.js +80 -0
  31. package/components/velin-segmented-control.js +108 -0
  32. package/components/velin-sheet.js +107 -0
  33. package/components/velin-sparkline.js +207 -0
  34. package/components/velin-theme-toggle.js +277 -60
  35. package/dist/velinstyle-components.iife.js +1629 -69
  36. package/dist/velinstyle-components.js +1649 -69
  37. package/dist/velinstyle-components.min.js +424 -80
  38. package/dist/velinstyle.css +472 -3
  39. package/dist/velinstyle.min.css +1 -1
  40. package/package.json +3 -2
  41. package/src/a11y/security.css +18 -0
  42. package/src/base/reset.css +12 -1
  43. package/src/components/nav.css +152 -151
  44. package/src/tokens/motion.css +7 -0
  45. package/src/utilities/animation.css +97 -1
  46. package/src/utilities/chart-animation.css +101 -0
  47. package/src/utilities/filter-effects.css +103 -0
  48. package/src/utilities/safe-area.css +39 -0
  49. package/src/velinstyle.css +3 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@birdapi/velinstyle",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org"
@@ -43,8 +43,9 @@
43
43
  "test": "vitest run",
44
44
  "test:a11y": "node tests/a11y/run.js",
45
45
  "test:contrast": "node scripts/check-contrast.js",
46
+ "test:security": "vitest run tests/sanitize.test.js tests/cli-security-scanner.test.js",
46
47
  "docs:prepare": "npm run build && node scripts/prepare-docs-pages.js",
47
- "docs:sync-site": "npm run build && node ../velinstyle-site/tools/sync-0.7.0.py"
48
+ "docs:sync-site": "npm run build && python ../velinstyle-site/tools/sync-0.7.0.py"
48
49
  },
49
50
  "keywords": [
50
51
  "css",
@@ -98,4 +98,22 @@
98
98
  transition: background-color 600000s 0s, color 600000s 0s;
99
99
  }
100
100
 
101
+ .velin-isolate {
102
+ isolation: isolate;
103
+ contain: layout style paint;
104
+ }
105
+
106
+ .velin-sensitive {
107
+ user-select: none;
108
+ -webkit-user-select: none;
109
+ }
110
+
111
+ .velin-overlay-lock {
112
+ overscroll-behavior: contain;
113
+ }
114
+
115
+ .velin-user-content img[src^="data:"] {
116
+ max-inline-size: 100%;
117
+ }
118
+
101
119
  }
@@ -152,7 +152,18 @@
152
152
  velin-scroll-top:not(:defined),
153
153
  velin-scrollspy:not(:defined),
154
154
  velin-theme-toggle:not(:defined),
155
- velin-icon:not(:defined) {
155
+ velin-icon:not(:defined),
156
+ velin-combobox:not(:defined),
157
+ velin-bottom-nav:not(:defined),
158
+ velin-sheet:not(:defined),
159
+ velin-segmented-control:not(:defined),
160
+ velin-rating:not(:defined),
161
+ velin-menubar:not(:defined),
162
+ velin-command:not(:defined),
163
+ velin-announcer:not(:defined),
164
+ velin-sparkline:not(:defined),
165
+ velin-counter:not(:defined),
166
+ velin-live-dot:not(:defined) {
156
167
  opacity: 0;
157
168
  visibility: hidden;
158
169
  }
@@ -1,151 +1,152 @@
1
- @layer components {
2
- .velin-nav {
3
- display: flex;
4
- align-items: center;
5
- gap: var(--velin-space-2);
6
- padding-block: var(--velin-space-3);
7
- padding-inline: var(--velin-space-4);
8
- background-color: var(--velin-color-surface-bright);
9
- border-block-end: 1px solid var(--velin-color-border);
10
- }
11
-
12
- .velin-nav__brand {
13
- font-size: var(--velin-text-lg);
14
- font-weight: var(--velin-weight-bold);
15
- color: var(--velin-color-text);
16
- text-decoration: none;
17
- margin-inline-end: auto;
18
- }
19
-
20
- .velin-nav__list {
21
- display: flex;
22
- align-items: center;
23
- gap: var(--velin-space-1);
24
- list-style: none;
25
- }
26
-
27
- .velin-nav__link {
28
- display: inline-flex;
29
- align-items: center;
30
- gap: var(--velin-space-1);
31
- min-block-size: 2.75rem; /* 44px touch target */
32
- padding-block: var(--velin-space-2);
33
- padding-inline: var(--velin-space-3);
34
- font-size: var(--velin-text-base);
35
- font-weight: var(--velin-weight-medium);
36
- color: var(--velin-color-text-muted);
37
- text-decoration: none;
38
- border-radius: var(--velin-radius-md);
39
- transition: var(--velin-transition-colors);
40
- }
41
-
42
- .velin-nav__link:hover {
43
- color: var(--velin-color-text);
44
- background-color: var(--velin-color-surface-dim);
45
- }
46
-
47
- .velin-nav__link[aria-current="page"],
48
- .velin-nav__link--active {
49
- color: var(--velin-color-primary);
50
- font-weight: var(--velin-weight-semibold);
51
- }
52
-
53
- /* Toggle button for mobile */
54
- .velin-nav__toggle {
55
- display: none;
56
- min-block-size: 2.75rem;
57
- min-inline-size: 2.75rem;
58
- align-items: center;
59
- justify-content: center;
60
- background: none;
61
- border: none;
62
- color: var(--velin-color-text);
63
- cursor: pointer;
64
- border-radius: var(--velin-radius-md);
65
- }
66
-
67
- .velin-nav__toggle:hover {
68
- background-color: var(--velin-color-surface-dim);
69
- }
70
-
71
- /* Mobile: stack nav vertically */
72
- @media (max-width: 47.999rem) {
73
- .velin-nav {
74
- flex-wrap: wrap;
75
- }
76
-
77
- .velin-nav__toggle {
78
- display: inline-flex;
79
- }
80
-
81
- .velin-nav__list {
82
- flex-direction: column;
83
- align-items: stretch;
84
- inline-size: 100%;
85
- display: none;
86
- }
87
-
88
- .velin-nav__list[data-velin-open] {
89
- display: flex;
90
- }
91
-
92
- .velin-nav__link {
93
- inline-size: 100%;
94
- }
95
- }
96
-
97
- /* Vertical nav variant */
98
- .velin-nav--vertical {
99
- flex-direction: column;
100
- align-items: stretch;
101
- border-block-end: none;
102
- border-inline-end: 1px solid var(--velin-color-border);
103
- }
104
-
105
- .velin-nav--vertical .velin-nav__brand {
106
- margin-inline-end: 0;
107
- margin-block-end: var(--velin-space-4);
108
- }
109
-
110
- .velin-nav--vertical .velin-nav__list {
111
- flex-direction: column;
112
- align-items: stretch;
113
- }
114
-
115
- /* Mega menu */
116
- .velin-nav__mega {
117
- position: absolute;
118
- inset-inline-start: 0;
119
- inset-inline-end: 0;
120
- inset-block-start: 100%;
121
- z-index: var(--velin-z-dropdown);
122
- background: var(--velin-color-surface-bright);
123
- border: 1px solid var(--velin-color-border);
124
- border-radius: var(--velin-radius-lg);
125
- box-shadow: var(--velin-shadow-lg);
126
- padding: var(--velin-space-6);
127
- display: none;
128
- }
129
-
130
- .velin-nav__item:hover > .velin-nav__mega,
131
- .velin-nav__item:focus-within > .velin-nav__mega {
132
- display: grid;
133
- grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
134
- gap: var(--velin-space-6);
135
- }
136
-
137
- .velin-nav__mega-section {
138
- display: flex;
139
- flex-direction: column;
140
- gap: var(--velin-space-2);
141
- }
142
-
143
- .velin-nav__mega-title {
144
- font-size: var(--velin-text-xs);
145
- font-weight: var(--velin-weight-bold);
146
- text-transform: uppercase;
147
- letter-spacing: 0.06em;
148
- color: var(--velin-color-text-muted);
149
- margin-block-end: var(--velin-space-2);
150
- }
151
- }
1
+ @layer components {
2
+ .velin-nav {
3
+ display: flex;
4
+ align-items: center;
5
+ gap: var(--velin-space-2);
6
+ padding-block: var(--velin-space-3);
7
+ padding-inline: var(--velin-space-4);
8
+ background-color: var(--velin-color-surface-bright);
9
+ border-block-end: 1px solid var(--velin-color-border);
10
+ }
11
+
12
+ .velin-nav__brand {
13
+ font-size: var(--velin-text-lg);
14
+ font-weight: var(--velin-weight-bold);
15
+ color: var(--velin-color-text);
16
+ text-decoration: none;
17
+ margin-inline-end: auto;
18
+ }
19
+
20
+ .velin-nav__list {
21
+ display: flex;
22
+ align-items: center;
23
+ gap: var(--velin-space-1);
24
+ list-style: none;
25
+ }
26
+
27
+ .velin-nav__link {
28
+ display: inline-flex;
29
+ align-items: center;
30
+ gap: var(--velin-space-1);
31
+ min-block-size: 2.75rem; /* 44px touch target */
32
+ padding-block: var(--velin-space-2);
33
+ padding-inline: var(--velin-space-3);
34
+ font-size: var(--velin-text-base);
35
+ font-weight: var(--velin-weight-medium);
36
+ color: var(--velin-color-text-muted);
37
+ text-decoration: none;
38
+ border-radius: var(--velin-radius-md);
39
+ transition: var(--velin-transition-colors);
40
+ }
41
+
42
+ .velin-nav__link:hover {
43
+ color: var(--velin-color-text);
44
+ background-color: var(--velin-color-surface-dim);
45
+ }
46
+
47
+ .velin-nav__link[aria-current="page"],
48
+ .velin-nav__link--active {
49
+ color: var(--velin-color-primary);
50
+ font-weight: var(--velin-weight-semibold);
51
+ }
52
+
53
+ /* Toggle button for mobile */
54
+ .velin-nav__toggle {
55
+ display: none;
56
+ min-block-size: 2.75rem;
57
+ min-inline-size: 2.75rem;
58
+ align-items: center;
59
+ justify-content: center;
60
+ background: none;
61
+ border: none;
62
+ color: var(--velin-color-text);
63
+ cursor: pointer;
64
+ border-radius: var(--velin-radius-md);
65
+ }
66
+
67
+ .velin-nav__toggle:hover {
68
+ background-color: var(--velin-color-surface-dim);
69
+ }
70
+
71
+ /* Mobile: stack nav vertically */
72
+ /* Aligns with --velin-bp-md (48rem) used in safe-area utilities */
73
+ @media (max-width: 47.999rem) {
74
+ .velin-nav {
75
+ flex-wrap: wrap;
76
+ }
77
+
78
+ .velin-nav__toggle {
79
+ display: inline-flex;
80
+ }
81
+
82
+ .velin-nav__list {
83
+ flex-direction: column;
84
+ align-items: stretch;
85
+ inline-size: 100%;
86
+ display: none;
87
+ }
88
+
89
+ .velin-nav__list[data-velin-open] {
90
+ display: flex;
91
+ }
92
+
93
+ .velin-nav__link {
94
+ inline-size: 100%;
95
+ }
96
+ }
97
+
98
+ /* Vertical nav variant */
99
+ .velin-nav--vertical {
100
+ flex-direction: column;
101
+ align-items: stretch;
102
+ border-block-end: none;
103
+ border-inline-end: 1px solid var(--velin-color-border);
104
+ }
105
+
106
+ .velin-nav--vertical .velin-nav__brand {
107
+ margin-inline-end: 0;
108
+ margin-block-end: var(--velin-space-4);
109
+ }
110
+
111
+ .velin-nav--vertical .velin-nav__list {
112
+ flex-direction: column;
113
+ align-items: stretch;
114
+ }
115
+
116
+ /* Mega menu */
117
+ .velin-nav__mega {
118
+ position: absolute;
119
+ inset-inline-start: 0;
120
+ inset-inline-end: 0;
121
+ inset-block-start: 100%;
122
+ z-index: var(--velin-z-dropdown);
123
+ background: var(--velin-color-surface-bright);
124
+ border: 1px solid var(--velin-color-border);
125
+ border-radius: var(--velin-radius-lg);
126
+ box-shadow: var(--velin-shadow-lg);
127
+ padding: var(--velin-space-6);
128
+ display: none;
129
+ }
130
+
131
+ .velin-nav__item:hover > .velin-nav__mega,
132
+ .velin-nav__item:focus-within > .velin-nav__mega {
133
+ display: grid;
134
+ grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
135
+ gap: var(--velin-space-6);
136
+ }
137
+
138
+ .velin-nav__mega-section {
139
+ display: flex;
140
+ flex-direction: column;
141
+ gap: var(--velin-space-2);
142
+ }
143
+
144
+ .velin-nav__mega-title {
145
+ font-size: var(--velin-text-xs);
146
+ font-weight: var(--velin-weight-bold);
147
+ text-transform: uppercase;
148
+ letter-spacing: 0.06em;
149
+ color: var(--velin-color-text-muted);
150
+ margin-block-end: var(--velin-space-2);
151
+ }
152
+ }
@@ -6,6 +6,8 @@
6
6
  --velin-duration-normal: 200ms;
7
7
  --velin-duration-slow: 300ms;
8
8
  --velin-duration-slower: 500ms;
9
+ --velin-duration-slowest: 800ms;
10
+ --velin-duration-cinematic: 1200ms;
9
11
 
10
12
  /* Easing curves */
11
13
  --velin-ease-default: cubic-bezier(0.4, 0, 0.2, 1);
@@ -13,6 +15,11 @@
13
15
  --velin-ease-out: cubic-bezier(0, 0, 0.2, 1);
14
16
  --velin-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
15
17
  --velin-ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
18
+ --velin-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1.4);
19
+ --velin-ease-elastic: cubic-bezier(0.68, -0.6, 0.32, 1.6);
20
+ --velin-ease-expo-out: cubic-bezier(0.16, 1, 0.3, 1);
21
+ --velin-ease-back-out: cubic-bezier(0.34, 1.4, 0.64, 1);
22
+ --velin-ease-snappy: cubic-bezier(0.2, 0.8, 0.2, 1);
16
23
 
17
24
  /* Common transitions */
18
25
  --velin-transition-colors: color var(--velin-duration-normal) var(--velin-ease-default),
@@ -259,5 +259,101 @@
259
259
 
260
260
  /* === Hover trigger === */
261
261
  .velin-animate-hover { animation-play-state: paused; }
262
- .velin-animate-hover:hover { animation-play-state: running; }
262
+ @media (hover: hover) and (pointer: fine) {
263
+ .velin-animate-hover:hover { animation-play-state: running; }
264
+ }
265
+
266
+ /* === 0.7.5 Animations === */
267
+ .velin-animate-blur-in {
268
+ animation: velin-blur-in var(--velin-duration-normal) var(--velin-ease-out) both;
269
+ }
270
+ .velin-animate-blur-out {
271
+ animation: velin-blur-out var(--velin-duration-normal) var(--velin-ease-in) both;
272
+ }
273
+ .velin-animate-rotate-in {
274
+ animation: velin-rotate-in var(--velin-duration-normal) var(--velin-ease-out) both;
275
+ }
276
+ .velin-animate-float {
277
+ animation: velin-float 3s var(--velin-ease-in-out) infinite;
278
+ }
279
+ .velin-animate-flash {
280
+ animation: velin-flash 0.6s var(--velin-ease-default) both;
281
+ }
282
+ .velin-animate-reveal-up {
283
+ animation: velin-reveal-up var(--velin-duration-normal) var(--velin-ease-out) both;
284
+ }
285
+ .velin-animate-reveal-down {
286
+ animation: velin-reveal-down var(--velin-duration-normal) var(--velin-ease-out) both;
287
+ }
288
+ .velin-animate-glow {
289
+ animation: velin-glow 2s var(--velin-ease-in-out) infinite;
290
+ }
291
+ .velin-animate-slide-fade-left {
292
+ animation: velin-slide-fade-left var(--velin-duration-normal) var(--velin-ease-out) both;
293
+ }
294
+ .velin-animate-slide-fade-right {
295
+ animation: velin-slide-fade-right var(--velin-duration-normal) var(--velin-ease-out) both;
296
+ }
297
+
298
+ @keyframes velin-blur-in {
299
+ from { opacity: 0; filter: blur(8px); }
300
+ to { opacity: 1; filter: blur(0); }
301
+ }
302
+ @keyframes velin-blur-out {
303
+ from { opacity: 1; filter: blur(0); }
304
+ to { opacity: 0; filter: blur(8px); }
305
+ }
306
+ @keyframes velin-rotate-in {
307
+ from { opacity: 0; transform: rotate(-8deg) scale(0.95); }
308
+ to { opacity: 1; transform: rotate(0) scale(1); }
309
+ }
310
+ @keyframes velin-float {
311
+ 0%, 100% { transform: translateY(0); }
312
+ 50% { transform: translateY(-0.35rem); }
313
+ }
314
+ @keyframes velin-flash {
315
+ 0%, 50%, 100% { opacity: 1; }
316
+ 25%, 75% { opacity: 0.35; }
317
+ }
318
+ @keyframes velin-reveal-up {
319
+ from { clip-path: inset(100% 0 0 0); opacity: 0; }
320
+ to { clip-path: inset(0 0 0 0); opacity: 1; }
321
+ }
322
+ @keyframes velin-reveal-down {
323
+ from { clip-path: inset(0 0 100% 0); opacity: 0; }
324
+ to { clip-path: inset(0 0 0 0); opacity: 1; }
325
+ }
326
+ @keyframes velin-glow {
327
+ 0%, 100% { box-shadow: 0 0 0 0 transparent; }
328
+ 50% { box-shadow: 0 0 1rem 0 color-mix(in oklch, var(--velin-color-primary, #2563eb) 35%, transparent); }
329
+ }
330
+ @keyframes velin-slide-fade-left {
331
+ from { opacity: 0; transform: translateX(1rem); }
332
+ to { opacity: 1; transform: translateX(0); }
333
+ }
334
+ @keyframes velin-slide-fade-right {
335
+ from { opacity: 0; transform: translateX(-1rem); }
336
+ to { opacity: 1; transform: translateX(0); }
337
+ }
338
+
339
+ @media (prefers-reduced-motion: reduce) {
340
+ .velin-animate-blur-in,
341
+ .velin-animate-blur-out,
342
+ .velin-animate-rotate-in,
343
+ .velin-animate-reveal-up,
344
+ .velin-animate-reveal-down,
345
+ .velin-animate-slide-fade-left,
346
+ .velin-animate-slide-fade-right {
347
+ animation: none;
348
+ opacity: 1;
349
+ filter: none;
350
+ transform: none;
351
+ clip-path: none;
352
+ }
353
+ .velin-animate-float,
354
+ .velin-animate-glow,
355
+ .velin-animate-flash {
356
+ animation: none;
357
+ }
358
+ }
263
359
  }
@@ -0,0 +1,101 @@
1
+ /*
2
+ * Chart animation utilities — keyframes and helpers for sparkline draw-in,
3
+ * value bumps, live pulses, and count-up fades. Consumed by the
4
+ * <velin-sparkline>, <velin-counter>, <velin-live-dot> Web Components plus
5
+ * any plain SVG/HTML that wants a "live" feel without a chart library.
6
+ *
7
+ * Each animated class is gated by `prefers-reduced-motion: reduce` so the
8
+ * baseline experience stays calm. CSS custom properties exposed so authors
9
+ * can re-skin without forking selectors.
10
+ */
11
+
12
+ @layer utilities {
13
+ /* === Keyframes === */
14
+ @keyframes velin-stroke-draw {
15
+ from { stroke-dashoffset: var(--velin-chart-len, 1000); }
16
+ to { stroke-dashoffset: 0; }
17
+ }
18
+
19
+ @keyframes velin-sparkline-glow {
20
+ 0%, 100% {
21
+ filter: drop-shadow(0 0 0 transparent);
22
+ }
23
+ 50% {
24
+ filter: drop-shadow(0 0 0.5rem color-mix(in oklch, var(--velin-chart-glow-color, var(--velin-color-primary, #2563eb)) 50%, transparent));
25
+ }
26
+ }
27
+
28
+ @keyframes velin-value-bump {
29
+ 0% { transform: scale(1); }
30
+ 35% { transform: scale(1.06); color: var(--velin-chart-bump-color, var(--velin-color-primary, #2563eb)); }
31
+ 100% { transform: scale(1); }
32
+ }
33
+
34
+ @keyframes velin-live-pulse {
35
+ 0% {
36
+ box-shadow: 0 0 0 0 color-mix(in oklch, var(--velin-live-color, var(--velin-color-success, oklch(60% 0.16 145))) 65%, transparent);
37
+ }
38
+ 70% {
39
+ box-shadow: 0 0 0 0.65rem color-mix(in oklch, var(--velin-live-color, var(--velin-color-success, oklch(60% 0.16 145))) 0%, transparent);
40
+ }
41
+ 100% {
42
+ box-shadow: 0 0 0 0 transparent;
43
+ }
44
+ }
45
+
46
+ @keyframes velin-count-fade {
47
+ from { opacity: 0; transform: translateY(0.4rem); }
48
+ to { opacity: 1; transform: translateY(0); }
49
+ }
50
+
51
+ /* === Utility classes === */
52
+ .velin-chart-line {
53
+ fill: none;
54
+ stroke: currentColor;
55
+ stroke-width: 2;
56
+ stroke-linecap: round;
57
+ stroke-linejoin: round;
58
+ stroke-dasharray: var(--velin-chart-len, 1000);
59
+ stroke-dashoffset: 0;
60
+ animation: velin-stroke-draw var(--velin-duration-cinematic, 1200ms) var(--velin-ease-expo-out, ease-out) both;
61
+ }
62
+
63
+ .velin-chart-area {
64
+ fill: var(--velin-chart-area-fill, currentColor);
65
+ opacity: 0;
66
+ animation: velin-count-fade var(--velin-duration-slowest, 800ms) var(--velin-ease-expo-out, ease-out) 200ms forwards;
67
+ }
68
+
69
+ .velin-chart-glow {
70
+ animation: velin-sparkline-glow 2.4s var(--velin-ease-in-out, ease-in-out) infinite;
71
+ }
72
+
73
+ .velin-spark-tick {
74
+ animation: velin-value-bump var(--velin-duration-slow, 300ms) var(--velin-ease-spring, ease-out) both;
75
+ }
76
+
77
+ .velin-live-pulse {
78
+ animation: velin-live-pulse 1.8s var(--velin-ease-out, ease-out) infinite;
79
+ border-radius: 50%;
80
+ }
81
+
82
+ .velin-count-fade {
83
+ animation: velin-count-fade var(--velin-duration-slow, 300ms) var(--velin-ease-expo-out, ease-out) both;
84
+ }
85
+
86
+ @media (prefers-reduced-motion: reduce) {
87
+ .velin-chart-line,
88
+ .velin-chart-area,
89
+ .velin-chart-glow,
90
+ .velin-spark-tick,
91
+ .velin-live-pulse,
92
+ .velin-count-fade {
93
+ animation: none;
94
+ stroke-dashoffset: 0;
95
+ opacity: 1;
96
+ transform: none;
97
+ filter: none;
98
+ box-shadow: none;
99
+ }
100
+ }
101
+ }