@stephen-netu/design-system 0.1.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 (204) hide show
  1. package/README.md +66 -0
  2. package/package.json +130 -0
  3. package/src/b0-app-shell/clarification-overlay/ClarificationOverlay.svelte +221 -0
  4. package/src/b0-app-shell/clarification-overlay/index.ts +1 -0
  5. package/src/b0-app-shell/command-bar/CommandBar.svelte +565 -0
  6. package/src/b0-app-shell/index.ts +14 -0
  7. package/src/base.css +152 -0
  8. package/src/d0-data-viz/BarChart.svelte +236 -0
  9. package/src/d0-data-viz/CheckpointBar.svelte +574 -0
  10. package/src/d0-data-viz/CheckpointPill.svelte +297 -0
  11. package/src/d0-data-viz/MetricCard.svelte +159 -0
  12. package/src/d0-data-viz/SparkLine.svelte +66 -0
  13. package/src/d0-data-viz/StorageMap.svelte +182 -0
  14. package/src/d0-data-viz/Timeline.svelte +188 -0
  15. package/src/d0-data-viz/checkpoint-types.ts +162 -0
  16. package/src/d0-data-viz/d0-tokens.css +32 -0
  17. package/src/d0-data-viz/index.ts +58 -0
  18. package/src/d0-data-viz/storage-map-types.ts +142 -0
  19. package/src/d0-data-viz/theme-integration.ts +134 -0
  20. package/src/d0-data-viz/types.ts +53 -0
  21. package/src/demos/BspSizingInfographic.svelte +386 -0
  22. package/src/demos/BspZoneDemo.svelte +255 -0
  23. package/src/demos/ControlRoomDemo.svelte +570 -0
  24. package/src/demos/NodepadBlocksShowcase.svelte +189 -0
  25. package/src/demos/index.ts +7 -0
  26. package/src/editor/CodeEditor.svelte +188 -0
  27. package/src/editor/FindReplaceDialog.svelte +516 -0
  28. package/src/editor/MarkdownEditor.svelte +660 -0
  29. package/src/editor/index.ts +2 -0
  30. package/src/entity-graph-view/WorldView.svelte +171 -0
  31. package/src/entity-graph-view/index.ts +6 -0
  32. package/src/entity-graph-view/types.ts +364 -0
  33. package/src/epistemic/index.ts +2 -0
  34. package/src/epistemic/types.ts +134 -0
  35. package/src/epistemic/utils.ts +32 -0
  36. package/src/f0-forms/checkbox/Checkbox.svelte +158 -0
  37. package/src/f0-forms/field/FormField.svelte +91 -0
  38. package/src/f0-forms/index.ts +11 -0
  39. package/src/f0-forms/radio/RadioGroup.svelte +192 -0
  40. package/src/f0-forms/search/SearchBar.svelte +99 -0
  41. package/src/f0-forms/select/Select.svelte +198 -0
  42. package/src/f0-forms/textarea/TextArea.svelte +170 -0
  43. package/src/f0-forms/toggle/FormToggle.svelte +147 -0
  44. package/src/f0-forms/validation/validate.ts +47 -0
  45. package/src/fabric/ai/ChatPanel.svelte +648 -0
  46. package/src/fabric/ai/index.ts +11 -0
  47. package/src/fabric/data/index.ts +21 -0
  48. package/src/fabric/feedback/StatusBar.svelte +274 -0
  49. package/src/fabric/feedback/ToastContainer.svelte +122 -0
  50. package/src/fabric/feedback/index.ts +14 -0
  51. package/src/fabric/index.ts +37 -0
  52. package/src/fabric/input/CommandBar.svelte +79 -0
  53. package/src/fabric/input/KeyboardShortcuts.svelte +388 -0
  54. package/src/fabric/input/index.ts +12 -0
  55. package/src/fabric/layout/GlassBox.svelte +193 -0
  56. package/src/fabric/layout/KanbanBoard.svelte +158 -0
  57. package/src/fabric/layout/KanbanCard.svelte +226 -0
  58. package/src/fabric/layout/KanbanColumn.svelte +278 -0
  59. package/src/fabric/layout/index.ts +17 -0
  60. package/src/fabric/navigation/ActivityRail.svelte +271 -0
  61. package/src/fabric/navigation/index.ts +11 -0
  62. package/src/g0-geo/SovereignMap.svelte +149 -0
  63. package/src/g0-geo/index.ts +2 -0
  64. package/src/index.ts +68 -0
  65. package/src/l0-layout/block-writer/BlockConnectionLines.svelte +98 -0
  66. package/src/l0-layout/block-writer/BlockContent.svelte +213 -0
  67. package/src/l0-layout/block-writer/BlockHeader.svelte +230 -0
  68. package/src/l0-layout/block-writer/BlockItem.svelte +177 -0
  69. package/src/l0-layout/block-writer/BlockWriter.svelte +219 -0
  70. package/src/l0-layout/block-writer/block-writer-types.ts +63 -0
  71. package/src/l0-layout/block-writer/index.ts +24 -0
  72. package/src/l0-layout/block-writer/markdown.ts +110 -0
  73. package/src/l0-layout/chat-view/ChatViewLayout.svelte +61 -0
  74. package/src/l0-layout/epistemic/AllayDetailPanel.svelte +113 -0
  75. package/src/l0-layout/epistemic/AssayDetailPanel.svelte +164 -0
  76. package/src/l0-layout/epistemic/AvenueTrace.svelte +253 -0
  77. package/src/l0-layout/epistemic/KnowledgeDetailPanel.svelte +204 -0
  78. package/src/l0-layout/epistemic/ParadigmLens.svelte +170 -0
  79. package/src/l0-layout/epistemic/ProvenanceChain.svelte +164 -0
  80. package/src/l0-layout/epistemic/TrustView.svelte +195 -0
  81. package/src/l0-layout/epistemic/index.ts +7 -0
  82. package/src/l0-layout/grove/AccordCard.svelte +156 -0
  83. package/src/l0-layout/grove/GroveCard.svelte +197 -0
  84. package/src/l0-layout/grove/MemberBadge.svelte +96 -0
  85. package/src/l0-layout/grove/MemberList.svelte +59 -0
  86. package/src/l0-layout/grove/grove-types.ts +29 -0
  87. package/src/l0-layout/grove/index.ts +14 -0
  88. package/src/l0-layout/index.ts +45 -0
  89. package/src/l0-layout/kanban/KanbanBoard.svelte +71 -0
  90. package/src/l0-layout/kanban/KanbanCard.svelte +276 -0
  91. package/src/l0-layout/kanban/KanbanColumn.svelte +198 -0
  92. package/src/l0-layout/kanban/KanbanViewLayout.svelte +61 -0
  93. package/src/l0-layout/workspace/WorkspaceLayout.svelte +93 -0
  94. package/src/n0-node-graph/GraphEditor.svelte +342 -0
  95. package/src/n0-node-graph/GraphHistory.svelte +122 -0
  96. package/src/n0-node-graph/Node.svelte +269 -0
  97. package/src/n0-node-graph/NodeCanvas.svelte +187 -0
  98. package/src/n0-node-graph/NodeEdge.svelte +152 -0
  99. package/src/n0-node-graph/NodePalette.svelte +318 -0
  100. package/src/n0-node-graph/NodePort.svelte +128 -0
  101. package/src/n0-node-graph/index.ts +33 -0
  102. package/src/n0-node-graph/n0-tokens.css +29 -0
  103. package/src/n0-node-graph/node-port-types.ts +19 -0
  104. package/src/n0-node-graph/types.ts +96 -0
  105. package/src/n0-node-graph/validate.test.ts +722 -0
  106. package/src/n0-node-graph/validate.ts +371 -0
  107. package/src/n1-flow-canvas/FlowCanvas.svelte +202 -0
  108. package/src/n1-flow-canvas/graph-state.svelte.ts +95 -0
  109. package/src/n1-flow-canvas/index.ts +21 -0
  110. package/src/n1-flow-canvas/layout.ts +118 -0
  111. package/src/n1-flow-canvas/layout.worker.ts +128 -0
  112. package/src/n1-flow-canvas/types.ts +102 -0
  113. package/src/n1-flow-canvas/utils.ts +146 -0
  114. package/src/n2-manifold-disk/ManifoldDisk.svelte +314 -0
  115. package/src/n2-manifold-disk/index.ts +6 -0
  116. package/src/n2-manifold-disk/poincare.ts +184 -0
  117. package/src/n3-force-graph/ForceGraph.svelte +180 -0
  118. package/src/n3-force-graph/GraphTooltip.svelte +99 -0
  119. package/src/n3-force-graph/animated-edge.ts +107 -0
  120. package/src/n3-force-graph/health-node.ts +128 -0
  121. package/src/n3-force-graph/index.ts +31 -0
  122. package/src/n3-force-graph/types.ts +94 -0
  123. package/src/n4-adaptive-manifold/AdaptiveManifold.svelte +1165 -0
  124. package/src/n4-adaptive-manifold/DetailCard.svelte +212 -0
  125. package/src/n4-adaptive-manifold/SuggestionIndicator.svelte +91 -0
  126. package/src/n4-adaptive-manifold/classifier.ts +139 -0
  127. package/src/n4-adaptive-manifold/index.ts +45 -0
  128. package/src/n4-adaptive-manifold/n4-tokens.css +155 -0
  129. package/src/n4-adaptive-manifold/orchestrator.ts +301 -0
  130. package/src/n4-adaptive-manifold/phase-theme.ts +92 -0
  131. package/src/n4-adaptive-manifold/pinnacle-events.ts +135 -0
  132. package/src/n4-adaptive-manifold/providers/gravitational-provider.ts +191 -0
  133. package/src/n4-adaptive-manifold/providers/manifold-provider.ts +276 -0
  134. package/src/n4-adaptive-manifold/transition.ts +90 -0
  135. package/src/n4-adaptive-manifold/types.ts +313 -0
  136. package/src/p0-primitives/index.ts +6 -0
  137. package/src/p0-primitives/tokens/control-room.css +309 -0
  138. package/src/p0-primitives/tokens/index.ts +6 -0
  139. package/src/p0-primitives/tokens/tokens.css +140 -0
  140. package/src/p0-primitives/tokens/tokens.json +132 -0
  141. package/src/p0-primitives/types/index.ts +123 -0
  142. package/src/p0-primitives/types/plugin-types.ts +74 -0
  143. package/src/p0-primitives/utils/index.ts +39 -0
  144. package/src/platform/hud-protocol.ts +20 -0
  145. package/src/platform/index.ts +3 -0
  146. package/src/s0-state/create-store.ts +84 -0
  147. package/src/s0-state/derived.ts +41 -0
  148. package/src/s0-state/index.ts +6 -0
  149. package/src/showcase/Showcase.svelte +401 -0
  150. package/src/spatial/BspTilingCanvas.svelte +232 -0
  151. package/src/spatial/ForceCanvas.svelte +289 -0
  152. package/src/spatial/LodRenderer.svelte +334 -0
  153. package/src/spatial/SnapZoomCamera.svelte +185 -0
  154. package/src/spatial/ZoneTiler.svelte +311 -0
  155. package/src/spatial/camera-types.ts +45 -0
  156. package/src/spatial/content-proportional-bsp.ts +253 -0
  157. package/src/spatial/force-canvas-types.ts +102 -0
  158. package/src/spatial/index.ts +15 -0
  159. package/src/spatial/lod-types.ts +70 -0
  160. package/src/spatial/zone-tiler-types.ts +177 -0
  161. package/src/stores/vaultStore.svelte +14 -0
  162. package/src/t0-transport/connection.ts +34 -0
  163. package/src/t0-transport/error.ts +37 -0
  164. package/src/t0-transport/events.ts +50 -0
  165. package/src/t0-transport/index.ts +6 -0
  166. package/src/t0-transport/invoke.ts +30 -0
  167. package/src/tauri/index.ts +13 -0
  168. package/src/u0-primitives/allay-gauge/AllayGauge.svelte +90 -0
  169. package/src/u0-primitives/allay-gauge/index.ts +1 -0
  170. package/src/u0-primitives/animated-icon/AnimatedIcon.svelte +273 -0
  171. package/src/u0-primitives/animated-icon/README.md +53 -0
  172. package/src/u0-primitives/animated-icon/index.ts +31 -0
  173. package/src/u0-primitives/assay-badge/AssayBadge.svelte +121 -0
  174. package/src/u0-primitives/assay-badge/AssaySignalBar.svelte +83 -0
  175. package/src/u0-primitives/assay-badge/index.ts +2 -0
  176. package/src/u0-primitives/avatar/Avatar.svelte +177 -0
  177. package/src/u0-primitives/badge/Badge.svelte +149 -0
  178. package/src/u0-primitives/button/Button.svelte +304 -0
  179. package/src/u0-primitives/card/Card.svelte +182 -0
  180. package/src/u0-primitives/dropdown/Dropdown.svelte +305 -0
  181. package/src/u0-primitives/expert-badge/ExpertBadge.svelte +89 -0
  182. package/src/u0-primitives/expert-badge/index.ts +1 -0
  183. package/src/u0-primitives/index.ts +35 -0
  184. package/src/u0-primitives/input/Input.svelte +237 -0
  185. package/src/u0-primitives/modal/Modal.svelte +331 -0
  186. package/src/u0-primitives/modal/index.ts +1 -0
  187. package/src/u0-primitives/spinner/Spinner.svelte +129 -0
  188. package/src/u0-primitives/tabs/Tabs.svelte +225 -0
  189. package/src/u0-primitives/tabs/tabs-types.ts +11 -0
  190. package/src/u0-primitives/toggle/Toggle.svelte +161 -0
  191. package/src/u0-primitives/tooltip/Tooltip.svelte +234 -0
  192. package/src/v0-diagrams/DiagramPanel.svelte +68 -0
  193. package/src/v0-diagrams/MarkmapDiagram.svelte +56 -0
  194. package/src/v0-diagrams/MermaidDiagram.svelte +53 -0
  195. package/src/v0-diagrams/index.ts +4 -0
  196. package/src/v0-diagrams/types.ts +28 -0
  197. package/src/v0-diagrams/v0-tokens.css +6 -0
  198. package/src/vite-env.d.ts +7 -0
  199. package/src/x0-enchanted-blocks/ENCHANTED_BLOCKS_SPEC.md +457 -0
  200. package/src/x0-enchanted-blocks/components/EnchantedBlock.svelte +485 -0
  201. package/src/x0-enchanted-blocks/components/NodepadBlock.svelte +262 -0
  202. package/src/x0-enchanted-blocks/core/block-layout.ts +211 -0
  203. package/src/x0-enchanted-blocks/core/block-types.ts +227 -0
  204. package/src/x0-enchanted-blocks/index.ts +46 -0
@@ -0,0 +1,304 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
6
+ size?: 'sm' | 'md' | 'lg';
7
+ type?: 'button' | 'submit' | 'reset';
8
+ disabled?: boolean;
9
+ loading?: boolean;
10
+ class?: string;
11
+ onclick?: (e: MouseEvent) => void;
12
+ children?: Snippet;
13
+ iconLeading?: Snippet;
14
+ iconTrailing?: Snippet;
15
+ }
16
+
17
+ let {
18
+ variant = 'primary',
19
+ size = 'md',
20
+ type = 'button',
21
+ disabled = false,
22
+ loading = false,
23
+ class: className = '',
24
+ onclick,
25
+ children,
26
+ iconLeading,
27
+ iconTrailing
28
+ }: Props = $props();
29
+
30
+ let isPressed = $state(false);
31
+
32
+ function handlePointerDown() {
33
+ if (!disabled && !loading) {
34
+ isPressed = true;
35
+ }
36
+ }
37
+
38
+ function handlePointerUp() {
39
+ isPressed = false;
40
+ }
41
+
42
+ function handlePointerLeave() {
43
+ isPressed = false;
44
+ }
45
+ </script>
46
+
47
+ <button
48
+ {type}
49
+ class="ds-button variant-{variant} size-{size} {className}"
50
+ class:is-loading={loading}
51
+ class:is-pressed={isPressed}
52
+ disabled={disabled || loading}
53
+ onclick={onclick}
54
+ onpointerdown={handlePointerDown}
55
+ onpointerup={handlePointerUp}
56
+ onpointerleave={handlePointerLeave}
57
+ data-variant={variant}
58
+ data-size={size}
59
+ >
60
+ {#if loading}
61
+ <span class="ds-button-spinner"></span>
62
+ {/if}
63
+
64
+ <span class="ds-button-content" class:opacity-0={loading}>
65
+ {#if iconLeading}
66
+ <span class="ds-button-icon leading">
67
+ {@render iconLeading()}
68
+ </span>
69
+ {/if}
70
+
71
+ {#if children}
72
+ <span class="ds-button-text">
73
+ {@render children()}
74
+ </span>
75
+ {/if}
76
+
77
+ {#if iconTrailing}
78
+ <span class="ds-button-icon trailing">
79
+ {@render iconTrailing()}
80
+ </span>
81
+ {/if}
82
+ </span>
83
+
84
+ <span class="ds-button-shimmer"></span>
85
+ </button>
86
+
87
+ <style>
88
+ .ds-button {
89
+ position: relative;
90
+ display: inline-flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ font-family: var(--font-sans);
94
+ font-weight: 500;
95
+ border: none;
96
+ border-radius: var(--radius-md);
97
+ cursor: pointer;
98
+ overflow: hidden;
99
+ user-select: none;
100
+ text-decoration: none;
101
+ transition: all var(--transition-fast);
102
+ transform: translateZ(0); /* Force GPU acceleration for smooth shimmer */
103
+ }
104
+
105
+ /* Focus */
106
+ .ds-button:focus-visible {
107
+ outline: 2px solid var(--color-accent);
108
+ outline-offset: 2px;
109
+ }
110
+
111
+ /* Disabled State */
112
+ .ds-button:disabled {
113
+ cursor: not-allowed;
114
+ opacity: 0.5;
115
+ }
116
+
117
+ .ds-button:disabled::after {
118
+ display: none;
119
+ }
120
+
121
+ /* Content Wrapper */
122
+ .ds-button-content {
123
+ display: inline-flex;
124
+ align-items: center;
125
+ justify-content: center;
126
+ gap: var(--space-2);
127
+ position: relative;
128
+ z-index: 1;
129
+ transition: opacity var(--transition-fast);
130
+ }
131
+
132
+ .opacity-0 {
133
+ opacity: 0 !important;
134
+ }
135
+
136
+ /* Icons */
137
+ .ds-button-icon {
138
+ display: inline-flex;
139
+ align-items: center;
140
+ justify-content: center;
141
+ }
142
+
143
+ .ds-button-icon :global(svg) {
144
+ width: 1.25em;
145
+ height: 1.25em;
146
+ }
147
+
148
+ /* Text */
149
+ .ds-button-text {
150
+ white-space: nowrap;
151
+ overflow: hidden;
152
+ text-overflow: ellipsis;
153
+ }
154
+
155
+ /* Shimmer Effect (Subtle highlight that crosses the button) */
156
+ .ds-button-shimmer {
157
+ position: absolute;
158
+ inset: 0;
159
+ pointer-events: none;
160
+ background: linear-gradient(
161
+ 90deg,
162
+ transparent,
163
+ rgba(255, 255, 255, 0.1),
164
+ transparent
165
+ );
166
+ transform: translateX(-100%);
167
+ z-index: 0;
168
+ }
169
+
170
+ .ds-button:hover:not(:disabled):not(.is-loading) .ds-button-shimmer {
171
+ transform: translateX(100%);
172
+ transition: transform var(--transition-slow) ease-in-out;
173
+ }
174
+
175
+ /* --- Variants --- */
176
+
177
+ /* Primary - Solid Brass/Copper */
178
+ .variant-primary {
179
+ background: var(--color-accent);
180
+ color: #111;
181
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2), inset 0 1px 1px rgba(255,255,255,0.2);
182
+ }
183
+
184
+ .variant-primary:hover:not(:disabled) {
185
+ background: var(--color-accent-hover);
186
+ box-shadow: var(--shadow-glow), inset 0 1px 1px rgba(255,255,255,0.3);
187
+ }
188
+
189
+ .variant-primary.is-pressed:not(:disabled) {
190
+ background: var(--color-accent-active);
191
+ box-shadow: inset 0 2px 4px rgba(0,0,0,0.2);
192
+ transform: translateY(1px);
193
+ }
194
+
195
+ /* Secondary - Outline */
196
+ .variant-secondary {
197
+ background: transparent;
198
+ color: var(--color-text-primary);
199
+ border: 1px solid var(--border-default);
200
+ }
201
+
202
+ .variant-secondary:hover:not(:disabled) {
203
+ background: var(--color-bg-panel-elevated);
204
+ border-color: var(--color-accent-subtle);
205
+ color: var(--color-accent-hover);
206
+ }
207
+
208
+ .variant-secondary.is-pressed:not(:disabled) {
209
+ background: rgba(0,0,0,0.2);
210
+ border-color: var(--color-accent-active);
211
+ transform: translateY(1px);
212
+ }
213
+
214
+ /* Ghost - Transparent until hover */
215
+ .variant-ghost {
216
+ background: transparent;
217
+ color: var(--color-text-secondary);
218
+ }
219
+
220
+ .variant-ghost:hover:not(:disabled) {
221
+ background: var(--color-bg-panel);
222
+ color: var(--color-text-primary);
223
+ }
224
+
225
+ .variant-ghost.is-pressed:not(:disabled) {
226
+ background: rgba(0,0,0,0.2);
227
+ transform: translateY(1px);
228
+ }
229
+
230
+ /* Danger */
231
+ .variant-danger {
232
+ background: rgba(166, 94, 94, 0.1);
233
+ color: var(--color-error);
234
+ border: 1px solid rgba(166, 94, 94, 0.3);
235
+ }
236
+
237
+ .variant-danger:hover:not(:disabled) {
238
+ background: rgba(166, 94, 94, 0.2);
239
+ border-color: var(--color-error);
240
+ box-shadow: 0 0 12px rgba(166, 94, 94, 0.3);
241
+ }
242
+
243
+ .variant-danger.is-pressed:not(:disabled) {
244
+ background: rgba(166, 94, 94, 0.3);
245
+ transform: translateY(1px);
246
+ }
247
+
248
+ /* --- Sizes --- */
249
+
250
+ .size-sm {
251
+ height: 1.75rem;
252
+ padding: 0 var(--space-3);
253
+ font-size: var(--text-xs);
254
+ border-radius: var(--radius-sm);
255
+ }
256
+
257
+ .size-md {
258
+ height: 2.25rem;
259
+ padding: 0 var(--space-4);
260
+ font-size: var(--text-sm);
261
+ }
262
+
263
+ .size-lg {
264
+ height: 2.75rem;
265
+ padding: 0 var(--space-6);
266
+ font-size: var(--text-base);
267
+ border-radius: var(--radius-lg);
268
+ }
269
+
270
+ /* --- Loading Spinner --- */
271
+ .ds-button-spinner {
272
+ position: absolute;
273
+ left: 50%;
274
+ top: 50%;
275
+ transform: translate(-50%, -50%);
276
+ width: 1.2em;
277
+ height: 1.2em;
278
+ border: 2px solid currentColor;
279
+ border-right-color: transparent;
280
+ border-radius: 50%;
281
+ animation: ds-button-spin 0.75s linear infinite;
282
+ z-index: 2;
283
+ }
284
+
285
+ .variant-primary .ds-button-spinner {
286
+ border-color: rgba(0,0,0,0.2);
287
+ border-right-color: #111;
288
+ }
289
+
290
+ @keyframes ds-button-spin {
291
+ from { transform: translate(-50%, -50%) rotate(0deg); }
292
+ to { transform: translate(-50%, -50%) rotate(360deg); }
293
+ }
294
+
295
+ @media (prefers-reduced-motion: reduce) {
296
+ .ds-button-shimmer { display: none; }
297
+ .ds-button { transition: none; }
298
+ .is-pressed:not(:disabled) { transform: none; }
299
+ @keyframes ds-button-spin {
300
+ from { transform: translate(-50%, -50%) rotate(0deg); }
301
+ to { transform: translate(-50%, -50%) rotate(360deg); }
302
+ }
303
+ }
304
+ </style>
@@ -0,0 +1,182 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ variant?: 'elevated' | 'flat' | 'interactive' | 'glass';
6
+ selected?: boolean;
7
+ class?: string;
8
+ onclick?: (e: MouseEvent) => void;
9
+ onkeydown?: (e: KeyboardEvent) => void;
10
+ children?: Snippet;
11
+ header?: Snippet;
12
+ footer?: Snippet;
13
+ }
14
+
15
+ let {
16
+ variant = 'elevated',
17
+ selected = false,
18
+ class: className = '',
19
+ onclick,
20
+ onkeydown,
21
+ children,
22
+ header,
23
+ footer
24
+ }: Props = $props();
25
+
26
+ let isInteractive = $derived(variant === 'interactive' || !!onclick);
27
+
28
+ function handleKeydown(e: KeyboardEvent) {
29
+ if (isInteractive && (e.key === 'Enter' || e.key === ' ')) {
30
+ e.preventDefault();
31
+ onclick?.(new MouseEvent('click'));
32
+ }
33
+ onkeydown?.(e);
34
+ }
35
+ </script>
36
+
37
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
38
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
39
+ <div
40
+ class="ds-card variant-{variant} {className}"
41
+ class:is-interactive={isInteractive}
42
+ class:is-selected={selected}
43
+ role={isInteractive ? "button" : "region"}
44
+ tabindex={isInteractive ? 0 : undefined}
45
+ onclick={isInteractive ? onclick : undefined}
46
+ onkeydown={isInteractive ? handleKeydown : undefined}
47
+ aria-pressed={isInteractive ? selected : undefined}
48
+ >
49
+ {#if header}
50
+ <header class="ds-card-header">
51
+ {@render header()}
52
+ </header>
53
+ {/if}
54
+
55
+ {#if children}
56
+ <div class="ds-card-content">
57
+ {@render children()}
58
+ </div>
59
+ {/if}
60
+
61
+ {#if footer}
62
+ <footer class="ds-card-footer">
63
+ {@render footer()}
64
+ </footer>
65
+ {/if}
66
+
67
+ <!-- Interactive Glow Effect -->
68
+ {#if isInteractive || selected}
69
+ <div class="ds-card-ring"></div>
70
+ {/if}
71
+ </div>
72
+
73
+ <style>
74
+ .ds-card {
75
+ position: relative;
76
+ display: flex;
77
+ flex-direction: column;
78
+ border-radius: var(--radius-lg);
79
+ background: var(--color-card-bg);
80
+ color: var(--color-card-text-primary);
81
+ overflow: hidden;
82
+ transition: all var(--transition-normal);
83
+ font-family: var(--font-sans);
84
+ }
85
+
86
+ /* Structural Elements */
87
+ .ds-card-header {
88
+ padding: var(--space-4) var(--space-5);
89
+ border-bottom: 1px solid var(--border-subtle);
90
+ font-weight: 600;
91
+ }
92
+
93
+ .ds-card-content {
94
+ padding: var(--space-5);
95
+ flex: 1;
96
+ }
97
+
98
+ .ds-card-footer {
99
+ padding: var(--space-4) var(--space-5);
100
+ border-top: 1px solid var(--border-subtle);
101
+ background: rgba(0,0,0,0.02);
102
+ }
103
+
104
+ /* --- Variants --- */
105
+
106
+ /* Elevated (Default) */
107
+ .variant-elevated {
108
+ box-shadow: var(--shadow-card);
109
+ border: 1px solid var(--border-default);
110
+ }
111
+
112
+ /* Flat */
113
+ .variant-flat {
114
+ background: var(--color-bg-panel);
115
+ color: var(--color-text-primary);
116
+ border: 1px solid var(--border-subtle);
117
+ box-shadow: none;
118
+ }
119
+
120
+ /* Glassmorphic */
121
+ .variant-glass {
122
+ background: rgba(42, 42, 42, 0.4);
123
+ backdrop-filter: blur(16px);
124
+ -webkit-backdrop-filter: blur(16px);
125
+ border: 1px solid rgba(255, 255, 255, 0.08);
126
+ color: var(--color-text-primary);
127
+ box-shadow: var(--shadow-lg);
128
+ }
129
+
130
+ /* Interactive (Hoverable) */
131
+ .is-interactive {
132
+ cursor: pointer;
133
+ background: var(--color-card-bg-warm);
134
+ border: 1px solid var(--border-default);
135
+ box-shadow: var(--shadow-card);
136
+ }
137
+
138
+ .is-interactive:hover {
139
+ transform: translateY(-2px);
140
+ box-shadow: var(--shadow-card-hover);
141
+ border-color: var(--border-hover);
142
+ }
143
+
144
+ .is-interactive:active {
145
+ transform: translateY(0);
146
+ box-shadow: var(--shadow-card);
147
+ }
148
+
149
+ .is-interactive:focus-visible {
150
+ outline: none;
151
+ }
152
+
153
+ /* Selection Ring */
154
+ .ds-card-ring {
155
+ position: absolute;
156
+ inset: 0;
157
+ pointer-events: none;
158
+ border-radius: inherit;
159
+ border: 2px solid transparent;
160
+ transition: all var(--transition-fast);
161
+ }
162
+
163
+ .is-interactive:focus-visible .ds-card-ring,
164
+ .is-selected .ds-card-ring {
165
+ border-color: var(--color-accent);
166
+ }
167
+
168
+ .is-selected {
169
+ box-shadow: var(--shadow-glow), var(--shadow-card-hover);
170
+ transform: translateY(-1px);
171
+ background: var(--color-bg-panel-elevated);
172
+ color: var(--color-text-primary);
173
+ }
174
+
175
+ @media (prefers-reduced-motion: reduce) {
176
+ .ds-card { transition: none; }
177
+ .is-interactive:hover { transform: none; }
178
+ .is-interactive:active { transform: none; }
179
+ .is-selected { transform: none; }
180
+ .ds-card-ring { transition: none; }
181
+ }
182
+ </style>