@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,160 +1,241 @@
1
- <script lang="ts">
2
- import { cn } from '../../utils/cn.js';
3
- import { onMount } from 'svelte';
4
-
5
- interface Props {
6
- /** Number of horizontal lines receding toward the vanishing point. */
7
- rows?: number;
8
- /** Number of vertical convergence lines. */
9
- cols?: number;
10
- /** Normalized Y position of vanishing point (0-1). */
11
- vanishY?: number;
12
- /** If true, lines slowly drift toward the viewer. */
13
- animated?: boolean;
14
- /** Additional CSS classes. */
15
- class?: string;
16
- }
17
-
18
- let {
19
- rows = 16,
20
- cols = 12,
21
- vanishY = 0.38,
22
- animated = false,
23
- class: className = '',
24
- }: Props = $props();
25
-
26
- let canvasEl: HTMLCanvasElement | undefined = $state();
27
- let animFrame = 0;
28
- let offset = $state(0);
29
-
30
- const prefersReduced =
31
- typeof window !== 'undefined'
32
- ? window.matchMedia('(prefers-reduced-motion: reduce)').matches
33
- : false;
34
-
35
- function draw(canvas: HTMLCanvasElement, t: number) {
36
- const ctx = canvas.getContext('2d');
37
- if (!ctx) return;
38
-
39
- const w = canvas.width;
40
- const h = canvas.height;
41
- const vpX = w / 2;
42
- const vpY = h * vanishY;
43
-
44
- ctx.clearRect(0, 0, w, h);
45
-
46
- // horizontal lines receding toward vanishing point
47
- for (let i = 0; i < rows; i++) {
48
- const progress = (i + t) / rows;
49
- if (progress > 1) continue;
50
- const y = vpY + (h - vpY) * Math.pow(progress, 1.6);
51
- const nearness = Math.pow(progress, 0.8);
52
-
53
- // color: gold near, teal far
54
- const r = Math.round(199 * (1 - nearness) + 121 * nearness);
55
- const g = Math.round(156 * (1 - nearness) + 166 * nearness);
56
- const b = Math.round(87 * (1 - nearness) + 163 * nearness);
57
- const alpha = 0.18 * (1 - nearness) + 0.06 * nearness;
58
-
59
- // fade at vanishing point
60
- const fadeNear = Math.min(1, progress * 4);
61
- const finalAlpha = alpha * fadeNear;
62
-
63
- ctx.beginPath();
64
- ctx.moveTo(0, y);
65
- ctx.lineTo(w, y);
66
- ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, ${finalAlpha})`;
67
- ctx.lineWidth = 1;
68
- ctx.stroke();
69
- }
70
-
71
- // vertical convergence lines
72
- for (let i = 0; i < cols; i++) {
73
- const xBottom = (i / (cols - 1)) * w;
74
- const progress = Math.abs(i / (cols - 1) - 0.5) * 2; // 0 at center, 1 at edges
75
-
76
- // fade at edges
77
- const edgeFade = 1 - Math.pow(progress, 2) * 0.7;
78
- const alpha = 0.1 * edgeFade;
79
-
80
- ctx.beginPath();
81
- ctx.moveTo(vpX, vpY);
82
- ctx.lineTo(xBottom, h);
83
- ctx.strokeStyle = `rgba(199, 156, 87, ${alpha})`;
84
- ctx.lineWidth = 1;
85
- ctx.stroke();
86
- }
87
- }
88
-
89
- function animate() {
90
- if (!canvasEl) return;
91
- if (animated && !prefersReduced) {
92
- offset = (offset + 0.003) % 1;
93
- }
94
- draw(canvasEl, offset);
95
- if (animated && !prefersReduced) {
96
- animFrame = requestAnimationFrame(animate);
97
- }
98
- }
99
-
100
- onMount(() => {
101
- if (!canvasEl) return;
102
-
103
- const container = canvasEl.parentElement;
104
- if (!container) return;
105
-
106
- const ro = new ResizeObserver((entries) => {
107
- for (const entry of entries) {
108
- const { width, height } = entry.contentRect;
109
- const dpr = window.devicePixelRatio || 1;
110
- canvasEl!.width = width * dpr;
111
- canvasEl!.height = height * dpr;
112
- canvasEl!.style.width = `${width}px`;
113
- canvasEl!.style.height = `${height}px`;
114
- const ctx = canvasEl!.getContext('2d');
115
- if (ctx) ctx.scale(dpr, dpr);
116
- // set logical dimensions for draw
117
- canvasEl!.dataset.logicalW = String(width);
118
- canvasEl!.dataset.logicalH = String(height);
119
- draw(canvasEl!, offset);
120
- }
121
- });
122
-
123
- ro.observe(container);
124
-
125
- if (animated && !prefersReduced) {
126
- animFrame = requestAnimationFrame(animate);
127
- }
128
-
129
- return () => {
130
- ro.disconnect();
131
- if (animFrame) cancelAnimationFrame(animFrame);
132
- };
133
- });
134
-
135
- // redraw on offset changes when animated
136
- $effect(() => {
137
- if (canvasEl && animated && !prefersReduced) {
138
- draw(canvasEl, offset);
139
- }
140
- });
141
- </script>
142
-
143
- <div class={cn('hyvui-horizon-grid', className)} aria-hidden="true">
144
- <canvas bind:this={canvasEl}></canvas>
145
- </div>
146
-
147
- <style>
148
- .hyvui-horizon-grid {
149
- position: absolute;
150
- inset: 0;
151
- pointer-events: none;
152
- overflow: hidden;
153
- }
154
-
155
- .hyvui-horizon-grid canvas {
156
- display: block;
157
- width: 100%;
158
- height: 100%;
159
- }
160
- </style>
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+ import { onMount } from 'svelte';
4
+
5
+ /**
6
+ * Canvas-drawn perspective grid with gold-to-teal gradient. Typically placed at ground level in a DepthStage.
7
+ * @example
8
+ * <DepthStage>
9
+ * <DepthLayer level="ground">
10
+ * <HorizonGrid rows={16} cols={10} vanishY={0.4} animated />
11
+ * </DepthLayer>
12
+ * </DepthStage>
13
+ */
14
+ interface Props {
15
+ /** Number of horizontal lines receding toward the vanishing point. */
16
+ rows?: number;
17
+ /** Number of vertical convergence lines. */
18
+ cols?: number;
19
+ /** Normalized Y position of vanishing point (0-1). */
20
+ vanishY?: number;
21
+ /** If true, lines slowly drift toward the viewer. */
22
+ animated?: boolean;
23
+ /** Additional CSS classes. */
24
+ class?: string;
25
+ }
26
+
27
+ let {
28
+ rows = 16,
29
+ cols = 12,
30
+ vanishY = 0.38,
31
+ animated = false,
32
+ class: className = ''
33
+ }: Props = $props();
34
+
35
+ let rootEl: HTMLDivElement | undefined = $state();
36
+ let canvasEl: HTMLCanvasElement | undefined = $state();
37
+ let animFrame = 0;
38
+ let isVisible = $state(true);
39
+
40
+ let ctx: CanvasRenderingContext2D | null = null;
41
+ let logicalW = 0;
42
+ let logicalH = 0;
43
+ let offset = 0;
44
+ let lastT = 0;
45
+
46
+ const prefersReduced =
47
+ typeof window !== 'undefined'
48
+ ? window.matchMedia('(prefers-reduced-motion: reduce)').matches
49
+ : false;
50
+
51
+ function draw(t: number) {
52
+ if (!ctx) return;
53
+ if (!logicalW || !logicalH) return;
54
+
55
+ const w = logicalW;
56
+ const h = logicalH;
57
+ const vpX = w / 2;
58
+ const vpY = h * vanishY;
59
+
60
+ ctx.clearRect(0, 0, w, h);
61
+
62
+ // horizontal lines receding toward vanishing point
63
+ for (let i = 0; i < rows; i++) {
64
+ const progress = (i + t) / rows;
65
+ if (progress > 1) continue;
66
+ const y = vpY + (h - vpY) * Math.pow(progress, 1.6);
67
+ const nearness = Math.pow(progress, 0.8);
68
+
69
+ // color: gold near, teal far
70
+ const r = Math.round(199 * (1 - nearness) + 121 * nearness);
71
+ const g = Math.round(156 * (1 - nearness) + 166 * nearness);
72
+ const b = Math.round(87 * (1 - nearness) + 163 * nearness);
73
+ const alpha = 0.18 * (1 - nearness) + 0.06 * nearness;
74
+
75
+ // fade at vanishing point
76
+ const fadeNear = Math.min(1, progress * 4);
77
+ const finalAlpha = alpha * fadeNear;
78
+
79
+ ctx.beginPath();
80
+ ctx.moveTo(0, y);
81
+ ctx.lineTo(w, y);
82
+ ctx.strokeStyle = `rgba(${r}, ${g}, ${b}, ${finalAlpha})`;
83
+ ctx.lineWidth = 1;
84
+ ctx.stroke();
85
+ }
86
+
87
+ // vertical convergence lines
88
+ for (let i = 0; i < cols; i++) {
89
+ const xBottom = (i / (cols - 1)) * w;
90
+ const progress = Math.abs(i / (cols - 1) - 0.5) * 2; // 0 at center, 1 at edges
91
+
92
+ // fade at edges
93
+ const edgeFade = 1 - Math.pow(progress, 2) * 0.7;
94
+ const alpha = 0.1 * edgeFade;
95
+
96
+ ctx.beginPath();
97
+ ctx.moveTo(vpX, vpY);
98
+ ctx.lineTo(xBottom, h);
99
+ ctx.strokeStyle = `rgba(199, 156, 87, ${alpha})`;
100
+ ctx.lineWidth = 1;
101
+ ctx.stroke();
102
+ }
103
+ }
104
+
105
+ function stop() {
106
+ if (animFrame) cancelAnimationFrame(animFrame);
107
+ animFrame = 0;
108
+ }
109
+
110
+ function tick(t: number) {
111
+ if (!animated || prefersReduced || document.hidden || !isVisible) {
112
+ stop();
113
+ return;
114
+ }
115
+
116
+ const dt = lastT ? t - lastT : 16;
117
+ if (lastT && dt < 32) {
118
+ animFrame = requestAnimationFrame(tick);
119
+ return;
120
+ }
121
+ lastT = t;
122
+
123
+ // Drift speed tuned to roughly match the previous "0.003 per frame" feel.
124
+ offset = (offset + dt * 0.00018) % 1;
125
+ draw(offset);
126
+ animFrame = requestAnimationFrame(tick);
127
+ }
128
+
129
+ function start() {
130
+ if (animFrame) return;
131
+ lastT = 0;
132
+ animFrame = requestAnimationFrame(tick);
133
+ }
134
+
135
+ function syncCanvasSize(width: number, height: number) {
136
+ if (!canvasEl) return;
137
+ if (!ctx) ctx = canvasEl.getContext('2d');
138
+ if (!ctx) return;
139
+
140
+ const dpr = window.devicePixelRatio || 1;
141
+ logicalW = width;
142
+ logicalH = height;
143
+
144
+ canvasEl.width = Math.max(1, Math.floor(width * dpr));
145
+ canvasEl.height = Math.max(1, Math.floor(height * dpr));
146
+ canvasEl.style.width = `${width}px`;
147
+ canvasEl.style.height = `${height}px`;
148
+
149
+ // Reset transform before applying DPR scaling (avoids cumulative scaling).
150
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
151
+ draw(offset);
152
+ }
153
+
154
+ onMount(() => {
155
+ if (!rootEl || !canvasEl) return;
156
+
157
+ ctx = canvasEl.getContext('2d');
158
+
159
+ const ro = new ResizeObserver((entries) => {
160
+ const entry = entries[0];
161
+ if (!entry) return;
162
+ const { width, height } = entry.contentRect;
163
+ syncCanvasSize(width, height);
164
+ });
165
+
166
+ ro.observe(rootEl);
167
+
168
+ const io = new IntersectionObserver(
169
+ (entries) => {
170
+ const entry = entries[0];
171
+ if (!entry) return;
172
+ isVisible = entry.isIntersecting;
173
+ },
174
+ { root: null, threshold: 0 }
175
+ );
176
+
177
+ io.observe(rootEl);
178
+
179
+ function onVisibility() {
180
+ if (document.hidden) stop();
181
+ else if (animated && !prefersReduced && isVisible) start();
182
+ }
183
+
184
+ let scrollTimer = 0;
185
+ function onScroll() {
186
+ stop();
187
+ clearTimeout(scrollTimer);
188
+ scrollTimer = window.setTimeout(() => {
189
+ if (animated && !prefersReduced && isVisible && !document.hidden) start();
190
+ }, 150);
191
+ }
192
+
193
+ document.addEventListener('visibilitychange', onVisibility);
194
+ window.addEventListener('scroll', onScroll, { passive: true });
195
+
196
+ return () => {
197
+ document.removeEventListener('visibilitychange', onVisibility);
198
+ window.removeEventListener('scroll', onScroll);
199
+ clearTimeout(scrollTimer);
200
+ io.disconnect();
201
+ ro.disconnect();
202
+ stop();
203
+ };
204
+ });
205
+
206
+ $effect(() => {
207
+ if (!canvasEl) return;
208
+ draw(offset);
209
+
210
+ if (
211
+ animated &&
212
+ !prefersReduced &&
213
+ isVisible &&
214
+ typeof document !== 'undefined' &&
215
+ !document.hidden
216
+ ) {
217
+ start();
218
+ } else {
219
+ stop();
220
+ }
221
+ });
222
+ </script>
223
+
224
+ <div bind:this={rootEl} class={cn('hyvui-horizon-grid', className)} aria-hidden="true">
225
+ <canvas bind:this={canvasEl}></canvas>
226
+ </div>
227
+
228
+ <style>
229
+ .hyvui-horizon-grid {
230
+ position: absolute;
231
+ inset: 0;
232
+ pointer-events: none;
233
+ overflow: hidden;
234
+ }
235
+
236
+ .hyvui-horizon-grid canvas {
237
+ display: block;
238
+ width: 100%;
239
+ height: 100%;
240
+ }
241
+ </style>
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Canvas-drawn perspective grid with gold-to-teal gradient. Typically placed at ground level in a DepthStage.
3
+ * @example
4
+ * <DepthStage>
5
+ * <DepthLayer level="ground">
6
+ * <HorizonGrid rows={16} cols={10} vanishY={0.4} animated />
7
+ * </DepthLayer>
8
+ * </DepthStage>
9
+ */
1
10
  interface Props {
2
11
  /** Number of horizontal lines receding toward the vanishing point. */
3
12
  rows?: number;
@@ -1,57 +1,62 @@
1
- <script lang="ts">
2
- import { cn } from '../../utils/cn.js';
3
-
4
- interface Props {
5
- /** Width of the plinth surface. */
6
- width?: string;
7
- /** How far the plinth recedes (visual depth). */
8
- depth?: string;
9
- /** Surface tint color. */
10
- color?: string;
11
- /** Additional CSS classes. */
12
- class?: string;
13
- }
14
-
15
- let {
16
- width = '100%',
17
- depth = '40px',
18
- color = 'rgba(199, 156, 87, 0.03)',
19
- class: className = '',
20
- }: Props = $props();
21
- </script>
22
-
23
- <div class={cn('hyvui-plinth-wrap', className)}>
24
- <div
25
- class="hyvui-plinth"
26
- style:width={width}
27
- style:height={depth}
28
- style:background-color={color}
29
- ></div>
30
- </div>
31
-
32
- <style>
33
- .hyvui-plinth-wrap {
34
- perspective: var(--perspective-mid);
35
- perspective-origin: 50% 0%;
36
- display: flex;
37
- justify-content: center;
38
- pointer-events: none;
39
- }
40
-
41
- .hyvui-plinth {
42
- transform: rotateX(70deg) translateZ(-20px);
43
- border-top: 1px solid rgba(199, 156, 87, 0.08);
44
- opacity: 0.6;
45
- }
46
-
47
- @media (prefers-reduced-motion: reduce) {
48
- .hyvui-plinth-wrap {
49
- perspective: none;
50
- }
51
-
52
- .hyvui-plinth {
53
- transform: none;
54
- opacity: 0.3;
55
- }
56
- }
57
- </style>
1
+ <script lang="ts">
2
+ import { cn } from '../../utils/cn.js';
3
+
4
+ /**
5
+ * Near-invisible receding surface for anchoring floating elements in a DepthStage.
6
+ * @example
7
+ * <DepthStage>
8
+ * <DepthLayer level="raised">
9
+ * <FloatCard>content</FloatCard>
10
+ * <Plinth width="80%" depth="30px" />
11
+ * </DepthLayer>
12
+ * </DepthStage>
13
+ */
14
+ interface Props {
15
+ /** Width of the plinth surface. */
16
+ width?: string;
17
+ /** How far the plinth recedes (visual depth). */
18
+ depth?: string;
19
+ /** Surface tint color. */
20
+ color?: string;
21
+ /** Additional CSS classes. */
22
+ class?: string;
23
+ }
24
+
25
+ let {
26
+ width = '100%',
27
+ depth = '40px',
28
+ color = 'rgba(199, 156, 87, 0.03)',
29
+ class: className = ''
30
+ }: Props = $props();
31
+ </script>
32
+
33
+ <div class={cn('hyvui-plinth-wrap', className)}>
34
+ <div class="hyvui-plinth" style:width style:height={depth} style:background-color={color}></div>
35
+ </div>
36
+
37
+ <style>
38
+ .hyvui-plinth-wrap {
39
+ perspective: var(--perspective-mid);
40
+ perspective-origin: 50% 0%;
41
+ display: flex;
42
+ justify-content: center;
43
+ pointer-events: none;
44
+ }
45
+
46
+ .hyvui-plinth {
47
+ transform: rotateX(70deg) translateZ(-20px);
48
+ border-top: 1px solid rgba(199, 156, 87, 0.08);
49
+ opacity: 0.6;
50
+ }
51
+
52
+ @media (prefers-reduced-motion: reduce) {
53
+ .hyvui-plinth-wrap {
54
+ perspective: none;
55
+ }
56
+
57
+ .hyvui-plinth {
58
+ transform: none;
59
+ opacity: 0.3;
60
+ }
61
+ }
62
+ </style>
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Near-invisible receding surface for anchoring floating elements in a DepthStage.
3
+ * @example
4
+ * <DepthStage>
5
+ * <DepthLayer level="raised">
6
+ * <FloatCard>content</FloatCard>
7
+ * <Plinth width="80%" depth="30px" />
8
+ * </DepthLayer>
9
+ * </DepthStage>
10
+ */
1
11
  interface Props {
2
12
  /** Width of the plinth surface. */
3
13
  width?: string;