@marianmeres/stuic 3.48.0 → 3.51.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 (144) hide show
  1. package/AGENTS.md +3 -4
  2. package/API.md +5 -16
  3. package/dist/actions/dim-behind/index.css +1 -2
  4. package/dist/actions/onboarding/index.css +1 -2
  5. package/dist/components/Accordion/index.css +6 -9
  6. package/dist/components/AlertConfirmPrompt/AlertConfirmPrompt.svelte +1 -1
  7. package/dist/components/AssetsPreview/index.css +3 -5
  8. package/dist/components/Avatar/index.css +3 -4
  9. package/dist/components/Book/index.css +3 -4
  10. package/dist/components/Button/index.css +11 -13
  11. package/dist/components/ButtonGroupRadio/index.css +5 -8
  12. package/dist/components/Card/Card.svelte +168 -0
  13. package/dist/components/Card/Card.svelte.d.ts +54 -0
  14. package/dist/components/Card/README.md +120 -0
  15. package/dist/components/Card/index.css +184 -0
  16. package/dist/components/Card/index.d.ts +1 -0
  17. package/dist/components/Card/index.js +1 -0
  18. package/dist/components/Carousel/index.css +5 -8
  19. package/dist/components/Cart/index.css +6 -8
  20. package/dist/components/Collapsible/index.css +1 -2
  21. package/dist/components/CommandMenu/index.css +0 -1
  22. package/dist/components/DataTable/index.css +11 -15
  23. package/dist/components/DismissibleMessage/index.css +4 -7
  24. package/dist/components/DropdownMenu/index.css +4 -6
  25. package/dist/components/IconSwap/index.css +1 -2
  26. package/dist/components/Input/index.css +19 -22
  27. package/dist/components/KbdShortcut/index.css +2 -4
  28. package/dist/components/ListItemButton/index.css +4 -6
  29. package/dist/components/Modal/index.css +1 -2
  30. package/dist/components/ModalDialog/index.css +3 -6
  31. package/dist/components/Nav/index.css +4 -6
  32. package/dist/components/Notifications/index.css +9 -12
  33. package/dist/components/Progress/index.css +1 -2
  34. package/dist/components/Skeleton/index.css +1 -2
  35. package/dist/components/TabbedMenu/index.css +2 -3
  36. package/dist/components/ThemePreview/index.css +13 -13
  37. package/dist/components/Tree/index.css +3 -5
  38. package/dist/components/WithSidePanel/index.css +2 -3
  39. package/dist/components/X/index.css +1 -2
  40. package/dist/index.css +26 -0
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.js +1 -0
  43. package/dist/mcp.js +1 -1
  44. package/dist/themes/css/blue-orange.css +132 -132
  45. package/dist/themes/css/cyan-red.css +132 -132
  46. package/dist/themes/css/cyan-slate.css +132 -132
  47. package/dist/themes/css/emerald-amber-forest.css +132 -132
  48. package/dist/themes/css/emerald-pink.css +132 -132
  49. package/dist/themes/css/fuchsia-emerald.css +132 -132
  50. package/dist/themes/css/gray.css +132 -132
  51. package/dist/themes/css/indigo-amber.css +132 -132
  52. package/dist/themes/css/lime-fuchsia-neon.css +132 -132
  53. package/dist/themes/css/monokai-cyan.css +132 -132
  54. package/dist/themes/css/monokai-green.css +132 -132
  55. package/dist/themes/css/monokai-pink.css +132 -132
  56. package/dist/themes/css/orange-pink-sunset.css +132 -132
  57. package/dist/themes/css/pink-emerald.css +132 -132
  58. package/dist/themes/css/pink-teal.css +132 -132
  59. package/dist/themes/css/purple-yellow.css +132 -132
  60. package/dist/themes/css/rainbow.css +132 -132
  61. package/dist/themes/css/red-blue.css +132 -132
  62. package/dist/themes/css/red-cyan.css +132 -132
  63. package/dist/themes/css/red-sky-slate.css +132 -132
  64. package/dist/themes/css/red-sky.css +132 -132
  65. package/dist/themes/css/rose-teal.css +132 -132
  66. package/dist/themes/css/sky-amber.css +132 -132
  67. package/dist/themes/css/slate-cyan.css +132 -132
  68. package/dist/themes/css/slate-teal-ocean.css +132 -132
  69. package/dist/themes/css/stone-orange-earth.css +132 -132
  70. package/dist/themes/css/stone.css +132 -132
  71. package/dist/themes/css/teal-rose.css +132 -132
  72. package/dist/themes/css/violet-lime.css +132 -132
  73. package/dist/themes/css/violet-rose-dusk.css +132 -132
  74. package/dist/themes/css/zinc.css +132 -132
  75. package/dist/utils/design-tokens.d.ts +2 -47
  76. package/dist/utils/design-tokens.js +2 -204
  77. package/docs/domains/theming.md +9 -10
  78. package/docs/domains/utils.md +2 -2
  79. package/package.json +3 -6
  80. package/dist/themes/blue-orange.d.ts +0 -6
  81. package/dist/themes/blue-orange.js +0 -115
  82. package/dist/themes/css/dds.css +0 -217
  83. package/dist/themes/cyan-red.d.ts +0 -6
  84. package/dist/themes/cyan-red.js +0 -115
  85. package/dist/themes/cyan-slate.d.ts +0 -6
  86. package/dist/themes/cyan-slate.js +0 -115
  87. package/dist/themes/dds.d.ts +0 -6
  88. package/dist/themes/dds.js +0 -134
  89. package/dist/themes/emerald-amber-forest.d.ts +0 -6
  90. package/dist/themes/emerald-amber-forest.js +0 -115
  91. package/dist/themes/emerald-pink.d.ts +0 -6
  92. package/dist/themes/emerald-pink.js +0 -115
  93. package/dist/themes/fuchsia-emerald.d.ts +0 -6
  94. package/dist/themes/fuchsia-emerald.js +0 -115
  95. package/dist/themes/gray.d.ts +0 -6
  96. package/dist/themes/gray.js +0 -115
  97. package/dist/themes/indigo-amber.d.ts +0 -6
  98. package/dist/themes/indigo-amber.js +0 -115
  99. package/dist/themes/lime-fuchsia-neon.d.ts +0 -6
  100. package/dist/themes/lime-fuchsia-neon.js +0 -115
  101. package/dist/themes/monokai-cyan.d.ts +0 -6
  102. package/dist/themes/monokai-cyan.js +0 -117
  103. package/dist/themes/monokai-green.d.ts +0 -6
  104. package/dist/themes/monokai-green.js +0 -117
  105. package/dist/themes/monokai-pink.d.ts +0 -6
  106. package/dist/themes/monokai-pink.js +0 -117
  107. package/dist/themes/orange-pink-sunset.d.ts +0 -6
  108. package/dist/themes/orange-pink-sunset.js +0 -115
  109. package/dist/themes/pink-emerald.d.ts +0 -6
  110. package/dist/themes/pink-emerald.js +0 -115
  111. package/dist/themes/pink-teal.d.ts +0 -6
  112. package/dist/themes/pink-teal.js +0 -115
  113. package/dist/themes/purple-yellow.d.ts +0 -6
  114. package/dist/themes/purple-yellow.js +0 -115
  115. package/dist/themes/rainbow.d.ts +0 -6
  116. package/dist/themes/rainbow.js +0 -120
  117. package/dist/themes/red-blue.d.ts +0 -6
  118. package/dist/themes/red-blue.js +0 -115
  119. package/dist/themes/red-cyan.d.ts +0 -6
  120. package/dist/themes/red-cyan.js +0 -115
  121. package/dist/themes/red-sky-slate.d.ts +0 -6
  122. package/dist/themes/red-sky-slate.js +0 -115
  123. package/dist/themes/red-sky.d.ts +0 -6
  124. package/dist/themes/red-sky.js +0 -115
  125. package/dist/themes/rose-teal.d.ts +0 -6
  126. package/dist/themes/rose-teal.js +0 -115
  127. package/dist/themes/sky-amber.d.ts +0 -6
  128. package/dist/themes/sky-amber.js +0 -115
  129. package/dist/themes/slate-cyan.d.ts +0 -6
  130. package/dist/themes/slate-cyan.js +0 -115
  131. package/dist/themes/slate-teal-ocean.d.ts +0 -6
  132. package/dist/themes/slate-teal-ocean.js +0 -115
  133. package/dist/themes/stone-orange-earth.d.ts +0 -6
  134. package/dist/themes/stone-orange-earth.js +0 -115
  135. package/dist/themes/stone.d.ts +0 -6
  136. package/dist/themes/stone.js +0 -115
  137. package/dist/themes/teal-rose.d.ts +0 -6
  138. package/dist/themes/teal-rose.js +0 -115
  139. package/dist/themes/violet-lime.d.ts +0 -6
  140. package/dist/themes/violet-lime.js +0 -115
  141. package/dist/themes/violet-rose-dusk.d.ts +0 -6
  142. package/dist/themes/violet-rose-dusk.js +0 -115
  143. package/dist/themes/zinc.d.ts +0 -6
  144. package/dist/themes/zinc.js +0 -115
package/AGENTS.md CHANGED
@@ -26,7 +26,7 @@ src/lib/
26
26
  ├── components/ # 46 UI components
27
27
  ├── actions/ # 15 Svelte actions
28
28
  ├── utils/ # 43 utility modules
29
- ├── themes/ # 29 theme definitions (.ts) + generated CSS (css/)
29
+ ├── themes/ # Generated theme CSS (css/) definitions from @marianmeres/design-tokens
30
30
  ├── icons/ # Icon re-exports
31
31
  ├── index.css # Centralized CSS imports
32
32
  └── index.ts # Main exports
@@ -92,9 +92,8 @@ src/lib/
92
92
  | -------------------------------- | ---------------------------------------------------------------------- |
93
93
  | `src/lib/index.css` | CSS entry point |
94
94
  | `src/lib/index.ts` | JS entry point |
95
- | `src/lib/utils/design-tokens.ts` | Theme types + CSS generation (`ThemeSchema`, `generateThemeCss`, etc.) |
96
- | `src/lib/themes/*.ts` | Theme definitions (29 themes, `TokenSchema`-typed) |
97
- | `src/lib/themes/css/stone.css` | Default theme (generated) |
95
+ | `src/lib/utils/design-tokens.ts` | Re-exports from `@marianmeres/design-tokens` |
96
+ | `src/lib/themes/css/stone.css` | Default theme (generated from `@marianmeres/design-tokens/themes`) |
98
97
  | `src/lib/components/Button/` | Reference component |
99
98
  | `scripts/generate-theme.ts` | CLI: `pnpm run build:theme:all` |
100
99
 
package/API.md CHANGED
@@ -1711,17 +1711,6 @@ Format a token record as a CSS selector block.
1711
1711
 
1712
1712
  **Returns:** `string` — CSS block string
1713
1713
 
1714
- #### `createDarkOverride(baseTokens, overrides)`
1715
-
1716
- Create a filtered dark mode token set from base tokens and overrides.
1717
-
1718
- **Parameters:**
1719
-
1720
- - `baseTokens` (GeneratedTokens) — Light mode tokens (used as key filter)
1721
- - `overrides` (Partial\<GeneratedTokens\>) — Dark mode values
1722
-
1723
- **Returns:** `GeneratedTokens`
1724
-
1725
1714
  ---
1726
1715
 
1727
1716
  ## Icons
@@ -1879,19 +1868,19 @@ Each component defines customization tokens. Override globally in `:root {}` or
1879
1868
  | property | `bg`, `text`, `border`, `ring`, `shadow`, `radius`, `padding` |
1880
1869
  | state | `hover`, `active`, `focus`, `disabled`, `error` |
1881
1870
 
1882
- ### Available Themes (29)
1871
+ ### Available Themes (31)
1883
1872
 
1884
- Default theme: `stone`.
1873
+ Default theme: `stone`. Theme definitions come from `@marianmeres/design-tokens`.
1885
1874
 
1886
1875
  ```ts
1887
1876
  // Import pre-built CSS
1888
1877
  import "@marianmeres/stuic/themes/css/blue-orange.css";
1889
1878
 
1890
- // Import theme definition object (to extend/customize)
1891
- import stone from "@marianmeres/stuic/themes/stone";
1879
+ // Import theme definition object (from design-tokens package)
1880
+ import { stone } from "@marianmeres/design-tokens/themes";
1892
1881
  ```
1893
1882
 
1894
- stone, gray, zinc, dds, blue-orange, cyan-red, cyan-slate, emerald-amber-forest, emerald-pink, fuchsia-emerald, indigo-amber, lime-fuchsia-neon, orange-pink-sunset, pink-emerald, pink-teal, purple-yellow, rainbow, red-blue, red-cyan, red-sky, red-sky-slate, rose-teal, sky-amber, slate-cyan, slate-teal-ocean, stone-orange-earth, teal-rose, violet-lime, violet-rose-dusk
1883
+ stone, gray, zinc, blue-orange, cyan-red, cyan-slate, emerald-amber-forest, emerald-pink, fuchsia-emerald, indigo-amber, lime-fuchsia-neon, monokai-cyan, monokai-green, monokai-pink, orange-pink-sunset, pink-emerald, pink-teal, purple-yellow, rainbow, red-blue, red-cyan, red-sky, red-sky-slate, rose-teal, sky-amber, slate-cyan, slate-teal-ocean, stone-orange-earth, teal-rose, violet-lime, violet-rose-dusk
1895
1884
 
1896
1885
  ### Dark Mode
1897
1886
 
@@ -7,7 +7,6 @@
7
7
  --stuic-dim-behind-backdrop-bg: rgb(0 0 0 / 0.5);
8
8
  --stuic-dim-behind-backdrop-z-index: 40;
9
9
  --stuic-dim-behind-element-z-index: 41;
10
- --stuic-dim-behind-transition-duration: 150ms;
11
10
  }
12
11
 
13
12
  .stuic-dim-behind-backdrop {
@@ -17,7 +16,7 @@
17
16
  background: var(--stuic-dim-behind-backdrop-bg);
18
17
  opacity: 0;
19
18
  transition-property: opacity;
20
- transition-duration: var(--stuic-dim-behind-transition-duration);
19
+ transition-duration: var(--stuic-dim-behind-transition-duration, var(--stuic-transition));
21
20
  pointer-events: auto;
22
21
 
23
22
  &.dim-visible {
@@ -5,7 +5,6 @@
5
5
  --stuic-onboarding-shell-gap: calc(var(--spacing) * 2);
6
6
  --stuic-onboarding-shell-min-width: 200px;
7
7
  --stuic-onboarding-shell-max-width: 320px;
8
- --stuic-onboarding-shell-radius: var(--radius-md, 0.375rem);
9
8
  --stuic-onboarding-title-size: var(--text-base);
10
9
  --stuic-onboarding-content-size: var(--text-sm);
11
10
  --stuic-onboarding-footer-gap: calc(var(--spacing) * 3);
@@ -21,7 +20,7 @@
21
20
  padding: var(--stuic-onboarding-shell-padding);
22
21
  min-width: var(--stuic-onboarding-shell-min-width);
23
22
  max-width: var(--stuic-onboarding-shell-max-width);
24
- border-radius: var(--stuic-onboarding-shell-radius);
23
+ border-radius: var(--stuic-onboarding-shell-radius, var(--stuic-radius));
25
24
  }
26
25
 
27
26
  .stuic-onboarding-title {
@@ -7,11 +7,8 @@
7
7
  :root {
8
8
  /* Border & shape */
9
9
  --stuic-accordion-border-color: var(--stuic-color-border);
10
- --stuic-accordion-border-width: 1px;
11
- --stuic-accordion-radius: var(--radius-md);
12
10
 
13
11
  /* Animation */
14
- --stuic-accordion-transition: 200ms;
15
12
 
16
13
  /* Trigger */
17
14
  --stuic-accordion-trigger-padding-x: 1rem;
@@ -39,8 +36,8 @@
39
36
  ============================================================================ */
40
37
 
41
38
  .stuic-accordion {
42
- border: var(--stuic-accordion-border-width) solid var(--stuic-accordion-border-color);
43
- border-radius: var(--stuic-accordion-radius);
39
+ border: var(--stuic-accordion-border-width, var(--stuic-border-width)) solid var(--stuic-accordion-border-color);
40
+ border-radius: var(--stuic-accordion-radius, var(--stuic-radius-container));
44
41
  overflow: hidden;
45
42
  }
46
43
 
@@ -49,7 +46,7 @@
49
46
  ============================================================================ */
50
47
 
51
48
  .stuic-accordion-item + .stuic-accordion-item {
52
- border-top: var(--stuic-accordion-border-width) solid
49
+ border-top: var(--stuic-accordion-border-width, var(--stuic-border-width)) solid
53
50
  var(--stuic-accordion-border-color);
54
51
  }
55
52
 
@@ -69,7 +66,7 @@
69
66
  border: none;
70
67
  cursor: pointer;
71
68
  text-align: left;
72
- transition: background var(--stuic-accordion-transition);
69
+ transition: background var(--stuic-accordion-transition, var(--stuic-transition));
73
70
  }
74
71
 
75
72
  .stuic-accordion-trigger:hover:not(:disabled) {
@@ -92,7 +89,7 @@
92
89
 
93
90
  .stuic-accordion-chevron {
94
91
  flex-shrink: 0;
95
- transition: transform var(--stuic-accordion-transition);
92
+ transition: transform var(--stuic-accordion-transition, var(--stuic-transition));
96
93
  }
97
94
 
98
95
  [data-open="true"] > .stuic-accordion-trigger > .stuic-accordion-chevron {
@@ -106,7 +103,7 @@
106
103
  .stuic-accordion-content {
107
104
  display: grid;
108
105
  grid-template-rows: 0fr;
109
- transition: grid-template-rows var(--stuic-accordion-transition);
106
+ transition: grid-template-rows var(--stuic-accordion-transition, var(--stuic-transition));
110
107
  }
111
108
 
112
109
  [data-open="true"] > .stuic-accordion-content {
@@ -97,7 +97,7 @@
97
97
  noClickOutsideClose
98
98
  type={acp?.current?.type}
99
99
  class={twMerge(
100
- "max-w-xl justify-end max-h-[62vh] h-auto border p-4 rounded-lg",
100
+ "max-w-xl justify-end max-h-[62vh] h-auto border p-4",
101
101
  // different max-h based on not/existing content
102
102
  // isTHCNotEmpty(acp?.current?.content) ? "sm:max-h-[200px]" : "sm:max-h-[150px]",
103
103
  // acp?.current?.type === PROMPT && "sm:max-h-[250px]",
@@ -10,7 +10,6 @@
10
10
  /* Labels (filename, tooltip) */
11
11
  --stuic-assets-preview-label-bg: rgba(0 0 0 / 0.6);
12
12
  --stuic-assets-preview-label-text: white;
13
- --stuic-assets-preview-label-radius: var(--radius-sm);
14
13
 
15
14
  /* Dot indicators */
16
15
  --stuic-assets-preview-dot-size: 0.75rem;
@@ -27,7 +26,6 @@
27
26
  --stuic-assets-preview-area-fill-hover: rgba(0 0 0 / 0.06);
28
27
 
29
28
  /* Transition */
30
- --stuic-assets-preview-transition: 150ms;
31
29
  }
32
30
 
33
31
  @layer components {
@@ -38,7 +36,7 @@
38
36
  .stuic-assets-preview-label {
39
37
  background: var(--stuic-assets-preview-label-bg);
40
38
  color: var(--stuic-assets-preview-label-text);
41
- border-radius: var(--stuic-assets-preview-label-radius);
39
+ border-radius: var(--stuic-assets-preview-label-radius, var(--stuic-radius));
42
40
  }
43
41
 
44
42
  /* ============================================================================
@@ -51,7 +49,7 @@
51
49
  border-radius: 9999px;
52
50
  background: var(--stuic-assets-preview-dot-bg);
53
51
  border: 1px solid var(--stuic-assets-preview-dot-border);
54
- transition: background var(--stuic-assets-preview-transition);
52
+ transition: background var(--stuic-assets-preview-transition, var(--stuic-transition));
55
53
  }
56
54
 
57
55
  .stuic-assets-preview-dot:hover {
@@ -78,7 +76,7 @@
78
76
  pointer-events: all;
79
77
  cursor: pointer;
80
78
  fill: transparent;
81
- transition: fill var(--stuic-assets-preview-transition);
79
+ transition: fill var(--stuic-assets-preview-transition, var(--stuic-transition));
82
80
  }
83
81
 
84
82
  .stuic-assets-preview-area:hover {
@@ -8,7 +8,6 @@
8
8
  /* Component-level customization tokens */
9
9
  --stuic-avatar-radius: 9999px;
10
10
  --stuic-avatar-font-weight: var(--font-weight-medium);
11
- --stuic-avatar-transition: 150ms;
12
11
 
13
12
  /* Default colors (uses muted role colors) */
14
13
  --stuic-avatar-bg: var(--stuic-color-muted);
@@ -65,9 +64,9 @@
65
64
 
66
65
  /* Transition for interactive mode */
67
66
  transition:
68
- background var(--stuic-avatar-transition),
69
- color var(--stuic-avatar-transition),
70
- opacity var(--stuic-avatar-transition);
67
+ background var(--stuic-avatar-transition, var(--stuic-transition)),
68
+ color var(--stuic-avatar-transition, var(--stuic-transition)),
69
+ opacity var(--stuic-avatar-transition, var(--stuic-transition));
71
70
  }
72
71
 
73
72
  /* ============================================================================
@@ -5,7 +5,6 @@
5
5
  --stuic-book-timing: ease-out;
6
6
  --stuic-book-page-bg: transparent; /*var(--stuic-color-surface, #fff);*/
7
7
  --stuic-book-page-shadow: none; /*0 2px 16px rgba(0, 0, 0, 0.15);*/
8
- --stuic-book-radius: var(--radius-sm, 2px);
9
8
  --stuic-book-page-width: 300px;
10
9
  --stuic-book-page-height: 400px;
11
10
  }
@@ -25,7 +24,7 @@
25
24
  vertical-align: top;
26
25
  text-align: initial;
27
26
  background: var(--stuic-book-page-bg);
28
- border-radius: var(--stuic-book-radius);
27
+ border-radius: var(--stuic-book-radius, var(--stuic-radius));
29
28
  box-shadow: var(--stuic-book-page-shadow);
30
29
  transition: width var(--stuic-book-duration) var(--stuic-book-timing);
31
30
  }
@@ -65,7 +64,7 @@
65
64
  backface-visibility: hidden;
66
65
  -webkit-backface-visibility: hidden;
67
66
  background: var(--stuic-book-page-bg);
68
- border-radius: 0 var(--stuic-book-radius) var(--stuic-book-radius) 0;
67
+ border-radius: 0 var(--stuic-book-radius, var(--stuic-radius)) var(--stuic-book-radius, var(--stuic-radius)) 0;
69
68
  overflow: hidden;
70
69
  }
71
70
 
@@ -83,7 +82,7 @@
83
82
  -webkit-backface-visibility: hidden;
84
83
  transform: rotateY(180deg);
85
84
  background: var(--stuic-book-page-bg);
86
- border-radius: var(--stuic-book-radius) 0 0 var(--stuic-book-radius);
85
+ border-radius: var(--stuic-book-radius, var(--stuic-radius)) 0 0 var(--stuic-book-radius, var(--stuic-radius));
87
86
  overflow: hidden;
88
87
  }
89
88
 
@@ -7,10 +7,8 @@
7
7
  /* prettier-ignore */
8
8
  :root {
9
9
  /* Component-level customization tokens */
10
- --stuic-button-radius: var(--radius-md);
11
10
  --stuic-button-font-family: var(--font-sans);
12
11
  --stuic-button-font-weight: var(--font-weight-medium);
13
- --stuic-button-transition: 150ms;
14
12
 
15
13
  /* Focus ring */
16
14
  --stuic-button-ring-width: 3px;
@@ -69,7 +67,7 @@
69
67
  /* Box model */
70
68
  border-width: 1px;
71
69
  border-style: solid;
72
- border-radius: var(--stuic-button-radius);
70
+ border-radius: var(--stuic-button-radius, var(--stuic-radius));
73
71
 
74
72
  /* Interaction */
75
73
  cursor: pointer;
@@ -77,10 +75,10 @@
77
75
  -webkit-tap-highlight-color: transparent; /* Remove iOS tap highlight */
78
76
  touch-action: manipulation; /* Disable double-tap zoom for faster response */
79
77
  transition:
80
- background var(--stuic-button-transition),
81
- color var(--stuic-button-transition),
82
- border-color var(--stuic-button-transition),
83
- opacity var(--stuic-button-transition);
78
+ background var(--stuic-button-transition, var(--stuic-transition)),
79
+ color var(--stuic-button-transition, var(--stuic-transition)),
80
+ border-color var(--stuic-button-transition, var(--stuic-transition)),
81
+ opacity var(--stuic-button-transition, var(--stuic-transition));
84
82
 
85
83
  /* Colors - use internal vars set by intent/variant */
86
84
  background: var(--_bg);
@@ -355,12 +353,12 @@
355
353
  box-shadow: var(--stuic-button-raised-offset) var(--stuic-button-raised-offset) 0 0
356
354
  var(--stuic-button-raised-color);
357
355
  transition:
358
- background var(--stuic-button-transition),
359
- color var(--stuic-button-transition),
360
- border-color var(--stuic-button-transition),
361
- opacity var(--stuic-button-transition),
362
- box-shadow var(--stuic-button-transition),
363
- transform var(--stuic-button-transition);
356
+ background var(--stuic-button-transition, var(--stuic-transition)),
357
+ color var(--stuic-button-transition, var(--stuic-transition)),
358
+ border-color var(--stuic-button-transition, var(--stuic-transition)),
359
+ opacity var(--stuic-button-transition, var(--stuic-transition)),
360
+ box-shadow var(--stuic-button-transition, var(--stuic-transition)),
361
+ transform var(--stuic-button-transition, var(--stuic-transition));
364
362
  }
365
363
 
366
364
  .stuic-button[data-raised]:active:not(:disabled) {
@@ -6,11 +6,8 @@
6
6
 
7
7
  :root {
8
8
  /* Structure - reference Tailwind vars where applicable */
9
- --stuic-button-group-radius: var(--radius-md);
10
9
  --stuic-button-group-padding: 0.1rem;
11
10
  --stuic-button-group-gap: 0.25rem;
12
- --stuic-button-group-border-width: 1px;
13
- --stuic-button-group-transition: 150ms;
14
11
 
15
12
  /* Button sizing - 44px is Apple HIG minimum touch target */
16
13
  --stuic-button-group-button-padding-x: 0.75rem;
@@ -56,9 +53,9 @@
56
53
 
57
54
  /* Box model */
58
55
  padding: var(--stuic-button-group-padding);
59
- border-width: var(--stuic-button-group-border-width);
56
+ border-width: var(--stuic-button-group-border-width, var(--stuic-border-width));
60
57
  border-style: solid;
61
- border-radius: var(--stuic-button-group-radius);
58
+ border-radius: var(--stuic-button-group-radius, var(--stuic-radius));
62
59
 
63
60
  /* Colors */
64
61
  background: var(--stuic-button-group-bg);
@@ -109,7 +106,7 @@
109
106
  var(--stuic-button-group-button-padding-x);
110
107
  min-height: var(--stuic-button-group-button-min-height);
111
108
  border: none;
112
- border-radius: var(--stuic-button-group-radius);
109
+ border-radius: var(--stuic-button-group-radius, var(--stuic-radius));
113
110
 
114
111
  /* Typography */
115
112
  white-space: nowrap;
@@ -128,8 +125,8 @@
128
125
 
129
126
  /* Transition */
130
127
  transition:
131
- background var(--stuic-button-group-transition),
132
- color var(--stuic-button-group-transition);
128
+ background var(--stuic-button-group-transition, var(--stuic-transition)),
129
+ color var(--stuic-button-group-transition, var(--stuic-transition));
133
130
 
134
131
  /* Focus */
135
132
  outline: none;
@@ -0,0 +1,168 @@
1
+ <script lang="ts" module>
2
+ import type { HTMLAttributes, HTMLAnchorAttributes } from "svelte/elements";
3
+ import type { Snippet } from "svelte";
4
+ import type { THC } from "../Thc/Thc.svelte";
5
+
6
+ export type CardVariant = "vertical" | "horizontal";
7
+
8
+ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children" | "title"> {
9
+ /** Image URL for the top (vertical) or side (horizontal) image area */
10
+ image?: string;
11
+ /** Alt text for the image */
12
+ imageAlt?: string;
13
+ /** Small label above the title (category, date, tag) */
14
+ eyebrow?: THC;
15
+ /** Card title */
16
+ title?: THC;
17
+ /** Short description below the title */
18
+ description?: THC;
19
+ /** Layout variant */
20
+ variant?: CardVariant;
21
+ /** When provided, the card renders as <a> */
22
+ href?: string;
23
+ /** Disabled state */
24
+ disabled?: boolean;
25
+ /** Override the entire card body */
26
+ children?: Snippet;
27
+ /** Override the image area */
28
+ renderImage?: Snippet<[{ image: string; imageAlt: string }]>;
29
+ /** Badge/overlay positioned over the image */
30
+ renderBadge?: Snippet;
31
+ /** Override the content area (eyebrow + title + description) */
32
+ renderContent?: Snippet<[{ eyebrow?: THC; title?: THC; description?: THC }]>;
33
+ /** Footer area (action buttons, metadata, etc.) */
34
+ renderFooter?: Snippet;
35
+ /** Skip all default styling */
36
+ unstyled?: boolean;
37
+ /** Additional CSS classes */
38
+ class?: string;
39
+ /** Class for the image container */
40
+ classImage?: string;
41
+ /** Class for the content area */
42
+ classContent?: string;
43
+ /** Class for the footer area */
44
+ classFooter?: string;
45
+ /** Bindable element reference */
46
+ el?: HTMLElement;
47
+ }
48
+ </script>
49
+
50
+ <script lang="ts">
51
+ import { twMerge } from "../../utils/tw-merge.js";
52
+ import Thc from "../Thc/Thc.svelte";
53
+
54
+ let {
55
+ image,
56
+ imageAlt,
57
+ eyebrow,
58
+ title,
59
+ description,
60
+ variant = "vertical",
61
+ href,
62
+ disabled = false,
63
+ children,
64
+ renderImage,
65
+ renderBadge,
66
+ renderContent,
67
+ renderFooter,
68
+ unstyled = false,
69
+ class: classProp,
70
+ classImage: classImageProp,
71
+ classContent: classContentProp,
72
+ classFooter: classFooterProp,
73
+ el = $bindable(),
74
+ onclick,
75
+ ...rest
76
+ }: Props = $props();
77
+
78
+ let _class = $derived(unstyled ? classProp : twMerge("stuic-card", classProp));
79
+ let _classImage = $derived(unstyled ? classImageProp : twMerge("stuic-card-image", classImageProp));
80
+ let _classContent = $derived(unstyled ? classContentProp : twMerge("stuic-card-content", classContentProp));
81
+ let _classFooter = $derived(unstyled ? classFooterProp : twMerge("stuic-card-footer", classFooterProp));
82
+
83
+ let _isInteractive = $derived(!!(href || onclick));
84
+ let _hasImage = $derived(!!(image || renderImage));
85
+ let _hasContent = $derived(!!(eyebrow || title || description || renderContent));
86
+ </script>
87
+
88
+ {#snippet cardInner()}
89
+ {#if children}
90
+ {@render children()}
91
+ {:else}
92
+ {#if _hasImage}
93
+ <div class={_classImage}>
94
+ {#if renderImage}
95
+ {@render renderImage({ image: image ?? "", imageAlt: imageAlt ?? "" })}
96
+ {:else if image}
97
+ <img src={image} alt={imageAlt ?? ""} />
98
+ {/if}
99
+ {#if renderBadge}
100
+ <div class={unstyled ? undefined : "stuic-card-badge"}>
101
+ {@render renderBadge()}
102
+ </div>
103
+ {/if}
104
+ </div>
105
+ {/if}
106
+ {#if renderContent}
107
+ <div class={_classContent}>
108
+ {@render renderContent({ eyebrow, title, description })}
109
+ </div>
110
+ {:else if _hasContent}
111
+ <div class={_classContent}>
112
+ {#if eyebrow}
113
+ <div class={unstyled ? undefined : "stuic-card-eyebrow"}><Thc thc={eyebrow} /></div>
114
+ {/if}
115
+ {#if title}
116
+ <div class={unstyled ? undefined : "stuic-card-title"}><Thc thc={title} /></div>
117
+ {/if}
118
+ {#if description}
119
+ <div class={unstyled ? undefined : "stuic-card-description"}><Thc thc={description} /></div>
120
+ {/if}
121
+ </div>
122
+ {/if}
123
+ {#if renderFooter}
124
+ <div class={_classFooter}>
125
+ {@render renderFooter()}
126
+ </div>
127
+ {/if}
128
+ {/if}
129
+ {/snippet}
130
+
131
+ {#if href}
132
+ <a
133
+ {href}
134
+ bind:this={el}
135
+ class={_class}
136
+ data-variant={!unstyled ? variant : undefined}
137
+ data-interactive={!unstyled ? "" : undefined}
138
+ data-disabled={!unstyled && disabled ? "" : undefined}
139
+ aria-disabled={disabled ? "true" : undefined}
140
+ {...rest as HTMLAnchorAttributes}
141
+ >
142
+ {@render cardInner()}
143
+ </a>
144
+ {:else if onclick}
145
+ <button
146
+ type="button"
147
+ bind:this={el}
148
+ class={_class}
149
+ data-variant={!unstyled ? variant : undefined}
150
+ data-interactive={!unstyled ? "" : undefined}
151
+ data-disabled={!unstyled && disabled ? "" : undefined}
152
+ {disabled}
153
+ {onclick}
154
+ {...rest as any}
155
+ >
156
+ {@render cardInner()}
157
+ </button>
158
+ {:else}
159
+ <div
160
+ bind:this={el}
161
+ class={_class}
162
+ data-variant={!unstyled ? variant : undefined}
163
+ data-disabled={!unstyled && disabled ? "" : undefined}
164
+ {...rest}
165
+ >
166
+ {@render cardInner()}
167
+ </div>
168
+ {/if}
@@ -0,0 +1,54 @@
1
+ import type { HTMLAttributes } from "svelte/elements";
2
+ import type { Snippet } from "svelte";
3
+ import type { THC } from "../Thc/Thc.svelte";
4
+ export type CardVariant = "vertical" | "horizontal";
5
+ export interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "children" | "title"> {
6
+ /** Image URL for the top (vertical) or side (horizontal) image area */
7
+ image?: string;
8
+ /** Alt text for the image */
9
+ imageAlt?: string;
10
+ /** Small label above the title (category, date, tag) */
11
+ eyebrow?: THC;
12
+ /** Card title */
13
+ title?: THC;
14
+ /** Short description below the title */
15
+ description?: THC;
16
+ /** Layout variant */
17
+ variant?: CardVariant;
18
+ /** When provided, the card renders as <a> */
19
+ href?: string;
20
+ /** Disabled state */
21
+ disabled?: boolean;
22
+ /** Override the entire card body */
23
+ children?: Snippet;
24
+ /** Override the image area */
25
+ renderImage?: Snippet<[{
26
+ image: string;
27
+ imageAlt: string;
28
+ }]>;
29
+ /** Badge/overlay positioned over the image */
30
+ renderBadge?: Snippet;
31
+ /** Override the content area (eyebrow + title + description) */
32
+ renderContent?: Snippet<[{
33
+ eyebrow?: THC;
34
+ title?: THC;
35
+ description?: THC;
36
+ }]>;
37
+ /** Footer area (action buttons, metadata, etc.) */
38
+ renderFooter?: Snippet;
39
+ /** Skip all default styling */
40
+ unstyled?: boolean;
41
+ /** Additional CSS classes */
42
+ class?: string;
43
+ /** Class for the image container */
44
+ classImage?: string;
45
+ /** Class for the content area */
46
+ classContent?: string;
47
+ /** Class for the footer area */
48
+ classFooter?: string;
49
+ /** Bindable element reference */
50
+ el?: HTMLElement;
51
+ }
52
+ declare const Card: import("svelte").Component<Props, {}, "el">;
53
+ type Card = ReturnType<typeof Card>;
54
+ export default Card;