@hyvnt/hyvui 0.2.0 → 0.4.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 (181) hide show
  1. package/README.md +294 -253
  2. package/dist/components/ambient/ArcaneVein.svelte +151 -0
  3. package/dist/components/ambient/ArcaneVein.svelte.d.ts +31 -0
  4. package/dist/components/ambient/BrassFiligree.svelte +109 -0
  5. package/dist/components/ambient/BrassFiligree.svelte.d.ts +20 -0
  6. package/dist/components/ambient/CornerBrackets.svelte +91 -87
  7. package/dist/components/ambient/CornerBrackets.svelte.d.ts +8 -0
  8. package/dist/components/ambient/CrystalShard.svelte +151 -0
  9. package/dist/components/ambient/CrystalShard.svelte.d.ts +19 -0
  10. package/dist/components/ambient/DataStream.svelte +117 -94
  11. package/dist/components/ambient/DataStream.svelte.d.ts +6 -0
  12. package/dist/components/ambient/EnergyArc.svelte +189 -0
  13. package/dist/components/ambient/EnergyArc.svelte.d.ts +32 -0
  14. package/dist/components/ambient/GlyphMark.svelte +75 -69
  15. package/dist/components/ambient/GlyphMark.svelte.d.ts +6 -0
  16. package/dist/components/ambient/GridOverlay.svelte +34 -28
  17. package/dist/components/ambient/GridOverlay.svelte.d.ts +8 -0
  18. package/dist/components/ambient/HexGrid.svelte +119 -0
  19. package/dist/components/ambient/HexGrid.svelte.d.ts +21 -0
  20. package/dist/components/ambient/ParallaxLayer.svelte +45 -41
  21. package/dist/components/ambient/ParallaxLayer.svelte.d.ts +7 -0
  22. package/dist/components/ambient/ScanBand.svelte +103 -91
  23. package/dist/components/ambient/ScanBand.svelte.d.ts +8 -0
  24. package/dist/components/ambient/ShimmerCloud.svelte +180 -0
  25. package/dist/components/ambient/ShimmerCloud.svelte.d.ts +21 -0
  26. package/dist/components/ambient/SignalRing.svelte +106 -100
  27. package/dist/components/ambient/SignalRing.svelte.d.ts +6 -0
  28. package/dist/components/ambient/ThreadLine.svelte +78 -78
  29. package/dist/components/ambient/ThreadLine.svelte.d.ts +7 -0
  30. package/dist/components/ambient/Vignette.svelte +30 -26
  31. package/dist/components/ambient/Vignette.svelte.d.ts +6 -0
  32. package/dist/components/depth/DepthLayer.svelte +30 -27
  33. package/dist/components/depth/DepthLayer.svelte.d.ts +8 -0
  34. package/dist/components/depth/DepthStage.svelte +67 -62
  35. package/dist/components/depth/DepthStage.svelte.d.ts +8 -0
  36. package/dist/components/depth/FloatCard.svelte +129 -104
  37. package/dist/components/depth/FloatCard.svelte.d.ts +8 -0
  38. package/dist/components/depth/HorizonGrid.svelte +241 -160
  39. package/dist/components/depth/HorizonGrid.svelte.d.ts +9 -0
  40. package/dist/components/depth/Plinth.svelte +62 -57
  41. package/dist/components/depth/Plinth.svelte.d.ts +10 -0
  42. package/dist/components/display/Avatar.svelte +69 -69
  43. package/dist/components/display/Avatar.svelte.d.ts +5 -0
  44. package/dist/components/display/Badge.svelte +75 -63
  45. package/dist/components/display/Badge.svelte.d.ts +6 -0
  46. package/dist/components/display/Blockquote.svelte +35 -34
  47. package/dist/components/display/Blockquote.svelte.d.ts +4 -0
  48. package/dist/components/display/CodeBlock.svelte +76 -76
  49. package/dist/components/display/CodeBlock.svelte.d.ts +5 -0
  50. package/dist/components/display/MetricCard.svelte +100 -83
  51. package/dist/components/display/MetricCard.svelte.d.ts +6 -0
  52. package/dist/components/display/Table.svelte +106 -104
  53. package/dist/components/display/Table.svelte.d.ts +7 -0
  54. package/dist/components/feedback/Alert.svelte +95 -76
  55. package/dist/components/feedback/Alert.svelte.d.ts +6 -0
  56. package/dist/components/feedback/EmptyState.svelte +75 -68
  57. package/dist/components/feedback/EmptyState.svelte.d.ts +7 -0
  58. package/dist/components/feedback/ErrorState.svelte +78 -73
  59. package/dist/components/feedback/ErrorState.svelte.d.ts +5 -0
  60. package/dist/components/feedback/Skeleton.svelte +58 -52
  61. package/dist/components/feedback/Skeleton.svelte.d.ts +6 -0
  62. package/dist/components/feedback/StatusDot.svelte +84 -54
  63. package/dist/components/feedback/StatusDot.svelte.d.ts +6 -0
  64. package/dist/components/feedback/StatusLine.svelte +128 -122
  65. package/dist/components/feedback/StatusLine.svelte.d.ts +6 -0
  66. package/dist/components/feedback/Toast.svelte +144 -136
  67. package/dist/components/feedback/Toast.svelte.d.ts +10 -0
  68. package/dist/components/inputs/Button.svelte +310 -237
  69. package/dist/components/inputs/Button.svelte.d.ts +8 -0
  70. package/dist/components/inputs/Checkbox.svelte +109 -105
  71. package/dist/components/inputs/Checkbox.svelte.d.ts +5 -0
  72. package/dist/components/inputs/FileUpload.svelte +170 -163
  73. package/dist/components/inputs/FileUpload.svelte.d.ts +5 -0
  74. package/dist/components/inputs/Input.svelte +153 -147
  75. package/dist/components/inputs/Input.svelte.d.ts +7 -0
  76. package/dist/components/inputs/Select.svelte +164 -150
  77. package/dist/components/inputs/Select.svelte.d.ts +8 -0
  78. package/dist/components/inputs/Textarea.svelte +160 -154
  79. package/dist/components/inputs/Textarea.svelte.d.ts +6 -0
  80. package/dist/components/inputs/Toggle.svelte +125 -120
  81. package/dist/components/inputs/Toggle.svelte.d.ts +5 -0
  82. package/dist/components/layout/Card.svelte +81 -76
  83. package/dist/components/layout/Card.svelte.d.ts +11 -0
  84. package/dist/components/layout/Drawer.svelte +140 -109
  85. package/dist/components/layout/Drawer.svelte.d.ts +6 -0
  86. package/dist/components/layout/Grid.svelte +128 -43
  87. package/dist/components/layout/Grid.svelte.d.ts +18 -2
  88. package/dist/components/layout/Modal.svelte +191 -159
  89. package/dist/components/layout/Modal.svelte.d.ts +10 -0
  90. package/dist/components/layout/Panel.svelte +58 -54
  91. package/dist/components/layout/Panel.svelte.d.ts +9 -0
  92. package/dist/components/layout/Popover.svelte +188 -67
  93. package/dist/components/layout/Popover.svelte.d.ts +19 -1
  94. package/dist/components/layout/Stack.svelte +65 -53
  95. package/dist/components/layout/Stack.svelte.d.ts +12 -0
  96. package/dist/components/navigation/Breadcrumb.svelte +78 -73
  97. package/dist/components/navigation/Breadcrumb.svelte.d.ts +8 -0
  98. package/dist/components/navigation/DropdownMenu.svelte +179 -124
  99. package/dist/components/navigation/DropdownMenu.svelte.d.ts +24 -2
  100. package/dist/components/navigation/SidebarNav.svelte +96 -90
  101. package/dist/components/navigation/SidebarNav.svelte.d.ts +9 -0
  102. package/dist/components/navigation/Tabs.svelte +106 -86
  103. package/dist/components/navigation/Tabs.svelte.d.ts +8 -0
  104. package/dist/components/navigation/Topbar.svelte +94 -85
  105. package/dist/components/navigation/Topbar.svelte.d.ts +9 -0
  106. package/dist/components/patterns/ActionBar.svelte +82 -76
  107. package/dist/components/patterns/ActionBar.svelte.d.ts +10 -0
  108. package/dist/components/patterns/ChapterMark.svelte +152 -0
  109. package/dist/components/patterns/ChapterMark.svelte.d.ts +19 -0
  110. package/dist/components/patterns/ConfirmDialog.svelte +75 -64
  111. package/dist/components/patterns/ConfirmDialog.svelte.d.ts +12 -0
  112. package/dist/components/patterns/DepthPortal.svelte +123 -0
  113. package/dist/components/patterns/DepthPortal.svelte.d.ts +24 -0
  114. package/dist/components/patterns/Manifesto.svelte +171 -0
  115. package/dist/components/patterns/Manifesto.svelte.d.ts +25 -0
  116. package/dist/components/patterns/PageHeader.svelte +117 -114
  117. package/dist/components/patterns/PageHeader.svelte.d.ts +8 -0
  118. package/dist/components/patterns/PullQuote.svelte +145 -0
  119. package/dist/components/patterns/PullQuote.svelte.d.ts +23 -0
  120. package/dist/components/patterns/RegisterSwitcher.svelte +132 -0
  121. package/dist/components/patterns/RegisterSwitcher.svelte.d.ts +21 -0
  122. package/dist/components/patterns/SearchBar.svelte +59 -59
  123. package/dist/components/patterns/SearchBar.svelte.d.ts +5 -0
  124. package/dist/components/patterns/ShowcaseFrame.svelte +117 -0
  125. package/dist/components/patterns/ShowcaseFrame.svelte.d.ts +28 -0
  126. package/dist/components/patterns/TerminalBoot.svelte +118 -104
  127. package/dist/components/patterns/TerminalBoot.svelte.d.ts +12 -0
  128. package/dist/components/primitives/Divider.svelte +56 -29
  129. package/dist/components/primitives/Divider.svelte.d.ts +5 -0
  130. package/dist/components/primitives/Icon.svelte +53 -49
  131. package/dist/components/primitives/Icon.svelte.d.ts +9 -0
  132. package/dist/components/primitives/Label.svelte +45 -44
  133. package/dist/components/primitives/Label.svelte.d.ts +6 -0
  134. package/dist/components/primitives/Surface.svelte +154 -87
  135. package/dist/components/primitives/Surface.svelte.d.ts +7 -0
  136. package/dist/components/primitives/Text.svelte +130 -98
  137. package/dist/components/primitives/Text.svelte.d.ts +7 -0
  138. package/dist/components/scenes/ArchiveScene.svelte +102 -95
  139. package/dist/components/scenes/ArchiveScene.svelte.d.ts +17 -1
  140. package/dist/components/scenes/DepthScene.svelte +128 -0
  141. package/dist/components/scenes/DepthScene.svelte.d.ts +36 -0
  142. package/dist/components/scenes/LogScene.svelte +86 -77
  143. package/dist/components/scenes/LogScene.svelte.d.ts +14 -0
  144. package/dist/components/scenes/NarrativeScene.svelte +100 -92
  145. package/dist/components/scenes/NarrativeScene.svelte.d.ts +9 -0
  146. package/dist/components/scenes/ReadoutScene.svelte +131 -107
  147. package/dist/components/scenes/ReadoutScene.svelte.d.ts +14 -1
  148. package/dist/components/scenes/StageScene.svelte +111 -104
  149. package/dist/components/scenes/StageScene.svelte.d.ts +14 -0
  150. package/dist/components/system/AppShell.svelte +62 -0
  151. package/dist/components/system/AppShell.svelte.d.ts +32 -0
  152. package/dist/examples/ArcaneShard.svelte +364 -0
  153. package/dist/examples/ArcaneShard.svelte.d.ts +3 -0
  154. package/dist/examples/FieldReport.svelte +226 -223
  155. package/dist/examples/HextechForge.svelte +324 -0
  156. package/dist/examples/HextechForge.svelte.d.ts +3 -0
  157. package/dist/examples/ObservationDeck.svelte +333 -317
  158. package/dist/examples/SignalLost.svelte +191 -191
  159. package/dist/index.d.ts +15 -1
  160. package/dist/index.js +16 -1
  161. package/dist/styles.css +115 -0
  162. package/dist/system/actions/echo.js +21 -12
  163. package/dist/system/actions/resolve.js +28 -14
  164. package/dist/system/actions/reveal.js +2 -2
  165. package/dist/system/actions/surface.js +12 -2
  166. package/dist/system/depth/depth.css +49 -49
  167. package/dist/system/depth/depth.js +1 -1
  168. package/dist/system/expressions.css +80 -80
  169. package/dist/system/override-template.css +72 -72
  170. package/dist/system/register.css +74 -74
  171. package/dist/system/register.d.ts +1 -1
  172. package/dist/system/register.js +5 -1
  173. package/dist/system/scroll-lock.d.ts +6 -0
  174. package/dist/system/scroll-lock.js +23 -0
  175. package/dist/tokens/arcane.css +96 -0
  176. package/dist/tokens/hextech.css +96 -0
  177. package/dist/tokens/tokens.css +102 -86
  178. package/dist/tokens/tokens.d.ts +41 -0
  179. package/dist/tokens/tokens.js +44 -3
  180. package/dist/utils/motion.js +1 -1
  181. package/package.json +71 -60
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Decorative. Parent must have `position: relative`. Renders `aria-hidden`.
3
+ * @example
4
+ * <div style="position: relative;">
5
+ * <GridOverlay />
6
+ * content here
7
+ * </div>
8
+ */
1
9
  interface Props {
2
10
  /** Additional CSS classes. */
3
11
  class?: string;
@@ -0,0 +1,119 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+
4
+ /**
5
+ * Decorative hex grid overlay. Adapts color and character per active register:
6
+ * hextech → cyan precise etching; arcane → violet cracked. Parent must have
7
+ * `position: relative`. Renders `aria-hidden`.
8
+ * @example
9
+ * <div style="position: relative;">
10
+ * <HexGrid />
11
+ * content here
12
+ * </div>
13
+ */
14
+ interface Props {
15
+ /** Controls animation playback. */
16
+ animated?: boolean;
17
+ /** Override grid opacity (0–1). Defaults to register's --reg-ornament-opacity. */
18
+ opacity?: number | undefined;
19
+ /** Additional CSS classes. */
20
+ class?: string;
21
+ }
22
+
23
+ let { animated = true, opacity = undefined, class: className = '' }: Props = $props();
24
+
25
+ const prefersReduced =
26
+ typeof window !== 'undefined'
27
+ ? window.matchMedia('(prefers-reduced-motion: reduce)').matches
28
+ : false;
29
+
30
+ const shouldAnimate = $derived(animated && !prefersReduced);
31
+ </script>
32
+
33
+ <div
34
+ class={cn('hyvui-hex-grid', shouldAnimate && 'hyvui-hex-grid--animated', className)}
35
+ style={opacity !== undefined ? `--hex-opacity-override: ${opacity}` : undefined}
36
+ aria-hidden="true"
37
+ ></div>
38
+
39
+ <style>
40
+ .hyvui-hex-grid {
41
+ position: absolute;
42
+ inset: 0;
43
+ pointer-events: none;
44
+
45
+ /* Hex pattern via three-angle stripes (vertical + two diagonals).
46
+ Color and opacity come from register tokens so they shift automatically. */
47
+ background-image:
48
+ linear-gradient(to right, var(--hg-color) 1px, transparent 1px),
49
+ linear-gradient(60deg, var(--hg-color) 1px, transparent 1px),
50
+ linear-gradient(-60deg, var(--hg-color) 1px, transparent 1px);
51
+ background-size: 40px 23px;
52
+
53
+ opacity: var(--hex-opacity-override, var(--reg-ornament-opacity));
54
+ mask-image: linear-gradient(to bottom, transparent, black 12%, black 88%, transparent);
55
+ -webkit-mask-image: linear-gradient(
56
+ to bottom,
57
+ transparent,
58
+ black 12%,
59
+ black 88%,
60
+ transparent
61
+ );
62
+
63
+ /* default color (field-notebook) */
64
+ --hg-color: rgba(199, 156, 87, 0.45);
65
+ }
66
+
67
+ /* ── hextech ─ cyan precision ─────────────────────────────────────── */
68
+ :global([data-register='hextech']) .hyvui-hex-grid {
69
+ --hg-color: rgba(93, 217, 240, 0.55);
70
+ background-size: 48px 28px;
71
+ }
72
+
73
+ /* ── arcane ─ violet instability ─────────────────────────────────── */
74
+ :global([data-register='arcane']) .hyvui-hex-grid {
75
+ --hg-color: rgba(184, 69, 201, 0.5);
76
+ background-size: 40px 23px;
77
+ }
78
+
79
+ /* ── pulse animation ─────────────────────────────────────────────── */
80
+ .hyvui-hex-grid--animated {
81
+ animation: hg-pulse var(--orn-shimmer-rate, 4s) var(--orn-pulse-rhythm, ease-in-out) infinite;
82
+ }
83
+
84
+ :global([data-register='arcane']) .hyvui-hex-grid--animated {
85
+ animation:
86
+ hg-pulse var(--orn-shimmer-rate, 2.4s) var(--orn-pulse-rhythm, ease-in-out) infinite,
87
+ hg-flicker 7s step-end infinite;
88
+ }
89
+
90
+ @keyframes hg-pulse {
91
+ 0%,
92
+ 100% {
93
+ opacity: var(--hex-opacity-override, var(--reg-ornament-opacity));
94
+ }
95
+ 50% {
96
+ opacity: calc(var(--hex-opacity-override, var(--reg-ornament-opacity)) * 1.8);
97
+ }
98
+ }
99
+
100
+ @keyframes hg-flicker {
101
+ 0%,
102
+ 97%,
103
+ 100% {
104
+ opacity: var(--hex-opacity-override, var(--reg-ornament-opacity));
105
+ }
106
+ 98% {
107
+ opacity: 0.02;
108
+ }
109
+ 99% {
110
+ opacity: calc(var(--hex-opacity-override, var(--reg-ornament-opacity)) * 2.2);
111
+ }
112
+ }
113
+
114
+ @media (prefers-reduced-motion: reduce) {
115
+ .hyvui-hex-grid--animated {
116
+ animation: none;
117
+ }
118
+ }
119
+ </style>
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Decorative hex grid overlay. Adapts color and character per active register:
3
+ * hextech → cyan precise etching; arcane → violet cracked. Parent must have
4
+ * `position: relative`. Renders `aria-hidden`.
5
+ * @example
6
+ * <div style="position: relative;">
7
+ * <HexGrid />
8
+ * content here
9
+ * </div>
10
+ */
11
+ interface Props {
12
+ /** Controls animation playback. */
13
+ animated?: boolean;
14
+ /** Override grid opacity (0–1). Defaults to register's --reg-ornament-opacity. */
15
+ opacity?: number | undefined;
16
+ /** Additional CSS classes. */
17
+ class?: string;
18
+ }
19
+ declare const HexGrid: import("svelte").Component<Props, {}, "">;
20
+ type HexGrid = ReturnType<typeof HexGrid>;
21
+ export default HexGrid;
@@ -1,41 +1,45 @@
1
- <script lang="ts">
2
- import { cn } from '../../utils/cn.js';
3
- import type { Snippet } from 'svelte';
4
-
5
- interface Props {
6
- /** Motion multiplier applied to --px/--py custom properties. */
7
- strength?: number;
8
- /** Additional CSS classes. */
9
- class?: string;
10
- /** Content to apply parallax to. */
11
- children?: Snippet;
12
- }
13
-
14
- let {
15
- strength = 18,
16
- class: className = '',
17
- children,
18
- }: Props = $props();
19
- </script>
20
-
21
- <div
22
- class={cn('hyvui-parallax', className)}
23
- style:transform="translate(calc(var(--px, 0) * {strength}px), calc(var(--py, 0) * {strength}px))"
24
- aria-hidden="true"
25
- >
26
- {#if children}{@render children()}{/if}
27
- </div>
28
-
29
- <style>
30
- .hyvui-parallax {
31
- will-change: transform;
32
- transition: transform 0.15s ease-out;
33
- }
34
-
35
- @media (prefers-reduced-motion: reduce) {
36
- .hyvui-parallax {
37
- transform: none !important;
38
- transition: none;
39
- }
40
- }
41
- </style>
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+ import type { Snippet } from 'svelte';
4
+
5
+ /**
6
+ * Reads `--px` and `--py` CSS custom properties for pointer-driven parallax. Set these on a parent via JS.
7
+ * @example
8
+ * <div onpointermove={trackPointer} style="position:relative;">
9
+ * <ParallaxLayer strength={12}><GlyphMark /></ParallaxLayer>
10
+ * </div>
11
+ */
12
+ interface Props {
13
+ /** Motion multiplier applied to --px/--py custom properties. */
14
+ strength?: number;
15
+ /** Additional CSS classes. */
16
+ class?: string;
17
+ /** Content to apply parallax to. */
18
+ children?: Snippet;
19
+ }
20
+
21
+ let { strength = 18, class: className = '', children }: Props = $props();
22
+ </script>
23
+
24
+ <div
25
+ class={cn('hyvui-parallax', className)}
26
+ style:transform="translate(calc(var(--px, 0) * {strength}px), calc(var(--py, 0) * {strength}px))"
27
+ aria-hidden="true"
28
+ >
29
+ {#if children}{@render children()}{/if}
30
+ </div>
31
+
32
+ <style>
33
+ .hyvui-parallax {
34
+ will-change: transform;
35
+ backface-visibility: hidden;
36
+ transition: transform 0.15s var(--ease-smooth);
37
+ }
38
+
39
+ @media (prefers-reduced-motion: reduce) {
40
+ .hyvui-parallax {
41
+ transform: none !important;
42
+ transition: none;
43
+ }
44
+ }
45
+ </style>
@@ -1,4 +1,11 @@
1
1
  import type { Snippet } from 'svelte';
2
+ /**
3
+ * Reads `--px` and `--py` CSS custom properties for pointer-driven parallax. Set these on a parent via JS.
4
+ * @example
5
+ * <div onpointermove={trackPointer} style="position:relative;">
6
+ * <ParallaxLayer strength={12}><GlyphMark /></ParallaxLayer>
7
+ * </div>
8
+ */
2
9
  interface Props {
3
10
  /** Motion multiplier applied to --px/--py custom properties. */
4
11
  strength?: number;
@@ -1,91 +1,103 @@
1
- <script lang="ts">
2
- import { cn } from '../../utils/cn.js';
3
-
4
- interface Props {
5
- /** Enable the scan animation. */
6
- active?: boolean;
7
- /** Animation axis. */
8
- axis?: 'x' | 'y';
9
- /** Thickness/width of the band. */
10
- size?: string;
11
- /** Animation duration. */
12
- duration?: string;
13
- /** Gradient used for the band. */
14
- gradient?: string;
15
- /** Additional CSS classes. */
16
- class?: string;
17
- }
18
-
19
- let {
20
- active = true,
21
- axis = 'y',
22
- size = '1px',
23
- duration = '8s',
24
- gradient = 'linear-gradient(90deg, transparent, rgba(199, 156, 87, 0.18), transparent)',
25
- class: className = '',
26
- }: Props = $props();
27
- </script>
28
-
29
- {#if active}
30
- <div
31
- class={cn('hyvui-scan-band', axis === 'x' ? 'hyvui-scan-band-x' : 'hyvui-scan-band-y', className)}
32
- style:--scan-size={size}
33
- style:--scan-duration={duration}
34
- style:--scan-gradient={gradient}
35
- aria-hidden="true"
36
- ></div>
37
- {/if}
38
-
39
- <style>
40
- .hyvui-scan-band {
41
- position: absolute;
42
- inset: 0;
43
- pointer-events: none;
44
- overflow: hidden;
45
- }
46
-
47
- .hyvui-scan-band::after {
48
- content: '';
49
- position: absolute;
50
- background: var(--scan-gradient);
51
- }
52
-
53
- .hyvui-scan-band-y::after {
54
- left: 0;
55
- right: 0;
56
- height: var(--scan-size);
57
- animation: scan-band-y var(--scan-duration) linear infinite;
58
- }
59
-
60
- .hyvui-scan-band-x::after {
61
- top: 0;
62
- bottom: 0;
63
- width: var(--scan-size);
64
- animation: scan-band-x var(--scan-duration) linear infinite;
65
- }
66
-
67
- @keyframes scan-band-y {
68
- 0% {
69
- transform: translateY(-100%);
70
- }
71
- 100% {
72
- transform: translateY(100vh);
73
- }
74
- }
75
-
76
- @keyframes scan-band-x {
77
- 0% {
78
- transform: translateX(-120%);
79
- }
80
- 100% {
81
- transform: translateX(520%);
82
- }
83
- }
84
-
85
- @media (prefers-reduced-motion: reduce) {
86
- .hyvui-scan-band::after {
87
- animation: none;
88
- display: none;
89
- }
90
- }
91
- </style>
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+
4
+ /**
5
+ * Decorative. Parent must have `position: relative`. Renders `aria-hidden`.
6
+ * @example
7
+ * <div style="position: relative;">
8
+ * <ScanBand axis="x" duration="6s" />
9
+ * content with scan line
10
+ * </div>
11
+ */
12
+ interface Props {
13
+ /** Enable the scan animation. */
14
+ active?: boolean;
15
+ /** Animation axis. */
16
+ axis?: 'x' | 'y';
17
+ /** Thickness/width of the band. */
18
+ size?: string;
19
+ /** Animation duration. */
20
+ duration?: string;
21
+ /** Gradient used for the band. */
22
+ gradient?: string;
23
+ /** Additional CSS classes. */
24
+ class?: string;
25
+ }
26
+
27
+ let {
28
+ active = true,
29
+ axis = 'y',
30
+ size = '1px',
31
+ duration = '8s',
32
+ gradient = 'linear-gradient(90deg, transparent, rgba(199, 156, 87, 0.18), transparent)',
33
+ class: className = ''
34
+ }: Props = $props();
35
+ </script>
36
+
37
+ {#if active}
38
+ <div
39
+ class={cn(
40
+ 'hyvui-scan-band',
41
+ axis === 'x' ? 'hyvui-scan-band-x' : 'hyvui-scan-band-y',
42
+ className
43
+ )}
44
+ style:--scan-size={size}
45
+ style:--scan-duration={duration}
46
+ style:--scan-gradient={gradient}
47
+ aria-hidden="true"
48
+ ></div>
49
+ {/if}
50
+
51
+ <style>
52
+ .hyvui-scan-band {
53
+ position: absolute;
54
+ inset: 0;
55
+ pointer-events: none;
56
+ overflow: hidden;
57
+ }
58
+
59
+ .hyvui-scan-band::after {
60
+ content: '';
61
+ position: absolute;
62
+ background: var(--scan-gradient);
63
+ }
64
+
65
+ .hyvui-scan-band-y::after {
66
+ left: 0;
67
+ right: 0;
68
+ height: var(--scan-size);
69
+ animation: scan-band-y var(--scan-duration) linear infinite;
70
+ }
71
+
72
+ .hyvui-scan-band-x::after {
73
+ top: 0;
74
+ bottom: 0;
75
+ width: var(--scan-size);
76
+ animation: scan-band-x var(--scan-duration) linear infinite;
77
+ }
78
+
79
+ @keyframes scan-band-y {
80
+ 0% {
81
+ transform: translateY(-100%);
82
+ }
83
+ 100% {
84
+ transform: translateY(100%);
85
+ }
86
+ }
87
+
88
+ @keyframes scan-band-x {
89
+ 0% {
90
+ transform: translateX(-120%);
91
+ }
92
+ 100% {
93
+ transform: translateX(520%);
94
+ }
95
+ }
96
+
97
+ @media (prefers-reduced-motion: reduce) {
98
+ .hyvui-scan-band::after {
99
+ animation: none;
100
+ display: none;
101
+ }
102
+ }
103
+ </style>
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Decorative. Parent must have `position: relative`. Renders `aria-hidden`.
3
+ * @example
4
+ * <div style="position: relative;">
5
+ * <ScanBand axis="x" duration="6s" />
6
+ * content with scan line
7
+ * </div>
8
+ */
1
9
  interface Props {
2
10
  /** Enable the scan animation. */
3
11
  active?: boolean;
@@ -0,0 +1,180 @@
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+ import { onMount } from 'svelte';
4
+
5
+ /**
6
+ * Particle-smoke ambient effect using canvas.
7
+ * Under `hextech`: slow blue-mist dots, precise motion.
8
+ * Under `arcane`: pink/violet shimmer drift, chaotic.
9
+ * Parent must have `position: relative`. Renders `aria-hidden`.
10
+ *
11
+ * @example
12
+ * <div style="position: relative;">
13
+ * <ShimmerCloud />
14
+ * content
15
+ * </div>
16
+ */
17
+ interface Props {
18
+ /** Particle count. */
19
+ count?: number;
20
+ /** Additional CSS classes. */
21
+ class?: string;
22
+ }
23
+
24
+ let { count = 28, class: className = '' }: Props = $props();
25
+
26
+ const prefersReduced =
27
+ typeof window !== 'undefined'
28
+ ? window.matchMedia('(prefers-reduced-motion: reduce)').matches
29
+ : false;
30
+
31
+ let canvas: HTMLCanvasElement | undefined = $state();
32
+ let rafId: number | undefined;
33
+
34
+ type Particle = {
35
+ x: number;
36
+ y: number;
37
+ r: number;
38
+ vx: number;
39
+ vy: number;
40
+ alpha: number;
41
+ alphaDir: number;
42
+ alphaSpeed: number;
43
+ };
44
+
45
+ function getRegister(): string {
46
+ if (typeof document === 'undefined') return '';
47
+ return document.body.dataset.register ?? '';
48
+ }
49
+
50
+ function getParticleColor(register: string): string {
51
+ if (register === 'hextech') return '93, 217, 240';
52
+ if (register === 'arcane') return '184, 69, 201';
53
+ return '199, 156, 87';
54
+ }
55
+
56
+ function getParticleColorB(register: string): string {
57
+ // Second color for arcane (two-color particles add richness)
58
+ if (register === 'arcane') return '233, 76, 188';
59
+ return getParticleColor(register);
60
+ }
61
+
62
+ onMount(() => {
63
+ if (prefersReduced || !canvas) return;
64
+
65
+ const ctx = canvas.getContext('2d');
66
+ if (!ctx) return;
67
+
68
+ let particles: Particle[] = [];
69
+
70
+ function resize() {
71
+ if (!canvas || !canvas.parentElement) return;
72
+ const { width, height } = canvas.parentElement.getBoundingClientRect();
73
+ canvas.width = width;
74
+ canvas.height = height;
75
+ }
76
+
77
+ function spawn(): Particle {
78
+ const w = canvas?.width ?? 400;
79
+ const h = canvas?.height ?? 300;
80
+ const register = getRegister();
81
+ const fast = register === 'arcane';
82
+ return {
83
+ x: Math.random() * w,
84
+ y: Math.random() * h,
85
+ r: 1 + Math.random() * (fast ? 3 : 2),
86
+ vx: (Math.random() - 0.5) * (fast ? 0.45 : 0.2),
87
+ vy: -(Math.random() * (fast ? 0.5 : 0.3) + 0.05),
88
+ alpha: Math.random() * 0.3,
89
+ alphaDir: 1,
90
+ alphaSpeed: 0.003 + Math.random() * (fast ? 0.008 : 0.004)
91
+ };
92
+ }
93
+
94
+ resize();
95
+ particles = Array.from({ length: count }, spawn);
96
+
97
+ const ro = new ResizeObserver(resize);
98
+ if (canvas.parentElement) ro.observe(canvas.parentElement);
99
+
100
+ let io_visible = true;
101
+ const io = new IntersectionObserver((entries) => {
102
+ io_visible = entries[0]?.isIntersecting ?? true;
103
+ });
104
+ io.observe(canvas);
105
+
106
+ function draw() {
107
+ if (!ctx || !canvas) return;
108
+ const register = getRegister();
109
+ const colorA = getParticleColor(register);
110
+ const colorB = getParticleColorB(register);
111
+
112
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
113
+
114
+ if (io_visible) {
115
+ particles.forEach((p, i) => {
116
+ // breathing alpha
117
+ p.alpha += p.alphaDir * p.alphaSpeed;
118
+ if (p.alpha >= (register === 'arcane' ? 0.38 : 0.22)) p.alphaDir = -1;
119
+ if (p.alpha <= 0.02) p.alphaDir = 1;
120
+
121
+ // drift
122
+ p.x += p.vx;
123
+ p.y += p.vy;
124
+
125
+ // wrap
126
+ if (p.y < -10) p.y = canvas.height + 10;
127
+ if (p.x < -10) p.x = canvas.width + 10;
128
+ if (p.x > canvas.width + 10) p.x = -10;
129
+
130
+ const color = i % 2 === 0 ? colorA : colorB;
131
+ ctx.beginPath();
132
+ ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
133
+ ctx.fillStyle = `rgba(${color}, ${p.alpha})`;
134
+ ctx.fill();
135
+ });
136
+ }
137
+
138
+ rafId = requestAnimationFrame(draw);
139
+ }
140
+
141
+ rafId = requestAnimationFrame(draw);
142
+
143
+ return () => {
144
+ if (rafId !== undefined) cancelAnimationFrame(rafId);
145
+ ro.disconnect();
146
+ io.disconnect();
147
+ };
148
+ });
149
+ </script>
150
+
151
+ <canvas
152
+ bind:this={canvas}
153
+ class={cn('hyvui-shimmer-cloud', className)}
154
+ aria-hidden="true"
155
+ ></canvas>
156
+
157
+ <style>
158
+ .hyvui-shimmer-cloud {
159
+ position: absolute;
160
+ inset: 0;
161
+ width: 100%;
162
+ height: 100%;
163
+ pointer-events: none;
164
+ opacity: 0.7;
165
+ }
166
+
167
+ :global([data-register='hextech']) .hyvui-shimmer-cloud {
168
+ opacity: 0.55;
169
+ }
170
+
171
+ :global([data-register='arcane']) .hyvui-shimmer-cloud {
172
+ opacity: 0.85;
173
+ }
174
+
175
+ @media (prefers-reduced-motion: reduce) {
176
+ .hyvui-shimmer-cloud {
177
+ display: none;
178
+ }
179
+ }
180
+ </style>
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Particle-smoke ambient effect using canvas.
3
+ * Under `hextech`: slow blue-mist dots, precise motion.
4
+ * Under `arcane`: pink/violet shimmer drift, chaotic.
5
+ * Parent must have `position: relative`. Renders `aria-hidden`.
6
+ *
7
+ * @example
8
+ * <div style="position: relative;">
9
+ * <ShimmerCloud />
10
+ * content
11
+ * </div>
12
+ */
13
+ interface Props {
14
+ /** Particle count. */
15
+ count?: number;
16
+ /** Additional CSS classes. */
17
+ class?: string;
18
+ }
19
+ declare const ShimmerCloud: import("svelte").Component<Props, {}, "">;
20
+ type ShimmerCloud = ReturnType<typeof ShimmerCloud>;
21
+ export default ShimmerCloud;