@marianmeres/stuic 2.66.0 → 3.0.1

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 (208) hide show
  1. package/README.md +292 -4
  2. package/dist/README.md +41 -18
  3. package/dist/actions/index.d.ts +1 -0
  4. package/dist/actions/index.js +1 -0
  5. package/dist/actions/popover/README.md +19 -0
  6. package/dist/actions/popover/index.css +6 -9
  7. package/dist/actions/popover/popover.svelte.js +2 -2
  8. package/dist/actions/tooltip/README.md +18 -0
  9. package/dist/actions/tooltip/index.css +5 -8
  10. package/dist/actions/tooltip/tooltip.svelte.js +1 -1
  11. package/dist/actions/typeahead.svelte.d.ts +53 -0
  12. package/dist/actions/typeahead.svelte.js +328 -0
  13. package/dist/base.css +17 -0
  14. package/dist/components/AlertConfirmPrompt/AlertConfirmPrompt.svelte +10 -10
  15. package/dist/components/AlertConfirmPrompt/AlertConfirmPrompt.svelte.d.ts +4 -3
  16. package/dist/components/AlertConfirmPrompt/Current.svelte +15 -18
  17. package/dist/components/AlertConfirmPrompt/Current.svelte.d.ts +4 -3
  18. package/dist/components/AlertConfirmPrompt/acp-icons.js +5 -4
  19. package/dist/components/AlertConfirmPrompt/index.css +66 -0
  20. package/dist/components/AssetsPreview/AssetsPreview.svelte +91 -73
  21. package/dist/components/AssetsPreview/index.css +61 -0
  22. package/dist/components/Avatar/Avatar.svelte +31 -18
  23. package/dist/components/Avatar/README.md +166 -0
  24. package/dist/components/Avatar/index.css +130 -0
  25. package/dist/components/Backdrop/Backdrop.svelte +7 -2
  26. package/dist/components/Backdrop/README.md +71 -6
  27. package/dist/components/Backdrop/index.css +31 -0
  28. package/dist/components/Button/Button.svelte +116 -124
  29. package/dist/components/Button/Button.svelte.d.ts +35 -24
  30. package/dist/components/Button/README.md +87 -21
  31. package/dist/components/Button/index.css +475 -9
  32. package/dist/components/Button/index.d.ts +1 -1
  33. package/dist/components/Button/index.js +1 -1
  34. package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte +7 -39
  35. package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte.d.ts +0 -1
  36. package/dist/components/ButtonGroupRadio/README.md +82 -4
  37. package/dist/components/ButtonGroupRadio/index.css +158 -14
  38. package/dist/components/Collapsible/Collapsible.svelte +7 -7
  39. package/dist/components/Collapsible/Collapsible.svelte.d.ts +2 -2
  40. package/dist/components/Collapsible/README.md +34 -2
  41. package/dist/components/Collapsible/index.css +40 -0
  42. package/dist/components/CommandMenu/CommandMenu.svelte +18 -26
  43. package/dist/components/CommandMenu/CommandMenu.svelte.d.ts +0 -1
  44. package/dist/components/CommandMenu/README.md +39 -0
  45. package/dist/components/CommandMenu/index.css +47 -2
  46. package/dist/components/DismissibleMessage/DismissibleMessage.svelte +53 -51
  47. package/dist/components/DismissibleMessage/DismissibleMessage.svelte.d.ts +6 -6
  48. package/dist/components/DismissibleMessage/README.md +93 -11
  49. package/dist/components/DismissibleMessage/index.css +128 -8
  50. package/dist/components/DismissibleMessage/index.d.ts +1 -1
  51. package/dist/components/DropdownMenu/DropdownMenu.svelte +14 -51
  52. package/dist/components/DropdownMenu/DropdownMenu.svelte.d.ts +6 -7
  53. package/dist/components/DropdownMenu/README.md +132 -0
  54. package/dist/components/DropdownMenu/index.css +258 -52
  55. package/dist/components/Input/FieldAssets.svelte +8 -5
  56. package/dist/components/Input/FieldCheckbox.svelte +7 -44
  57. package/dist/components/Input/FieldFile.svelte +1 -6
  58. package/dist/components/Input/FieldInput.svelte +9 -1
  59. package/dist/components/Input/FieldInput.svelte.d.ts +2 -0
  60. package/dist/components/Input/FieldOptions.svelte +42 -39
  61. package/dist/components/Input/FieldRadios.svelte +7 -16
  62. package/dist/components/Input/FieldSelect.svelte +1 -1
  63. package/dist/components/Input/FieldSwitch.svelte +1 -5
  64. package/dist/components/Input/FieldTextarea.svelte +1 -1
  65. package/dist/components/Input/README.md +194 -0
  66. package/dist/components/Input/_internal/FieldRadioInternal.svelte +2 -40
  67. package/dist/components/Input/_internal/InputWrap.svelte +8 -48
  68. package/dist/components/Input/index.css +524 -116
  69. package/dist/components/KbdShortcut/KbdShortcut.svelte +4 -12
  70. package/dist/components/KbdShortcut/README.md +34 -0
  71. package/dist/components/KbdShortcut/index.css +55 -0
  72. package/dist/components/ListItemButton/ListItemButton.svelte +37 -74
  73. package/dist/components/ListItemButton/ListItemButton.svelte.d.ts +1 -10
  74. package/dist/components/ListItemButton/README.md +100 -45
  75. package/dist/components/ListItemButton/index.css +173 -52
  76. package/dist/components/ListItemButton/index.d.ts +1 -1
  77. package/dist/components/ListItemButton/index.js +1 -1
  78. package/dist/components/Modal/Modal.svelte +1 -8
  79. package/dist/components/Modal/README.md +29 -0
  80. package/dist/components/Modal/index.css +38 -0
  81. package/dist/components/ModalDialog/ModalDialog.svelte +2 -21
  82. package/dist/components/ModalDialog/README.md +35 -0
  83. package/dist/components/ModalDialog/index.css +59 -0
  84. package/dist/components/Nav/Nav.svelte +732 -0
  85. package/dist/components/Nav/Nav.svelte.d.ts +110 -0
  86. package/dist/components/Nav/README.md +334 -0
  87. package/dist/components/Nav/index.css +318 -0
  88. package/dist/components/Nav/index.d.ts +1 -0
  89. package/dist/components/Nav/index.js +1 -0
  90. package/dist/components/Notifications/Notifications.svelte +44 -129
  91. package/dist/components/Notifications/Notifications.svelte.d.ts +9 -18
  92. package/dist/components/Notifications/README.md +186 -70
  93. package/dist/components/Notifications/index.css +212 -15
  94. package/dist/components/Notifications/notifications-stack.svelte.d.ts +4 -0
  95. package/dist/components/Notifications/notifications-stack.svelte.js +8 -0
  96. package/dist/components/Progress/Progress.svelte +4 -2
  97. package/dist/components/Progress/Progress.svelte.d.ts +1 -0
  98. package/dist/components/Progress/README.md +97 -11
  99. package/dist/components/Progress/_internal/Bar.svelte +4 -15
  100. package/dist/components/Progress/_internal/Bar.svelte.d.ts +1 -1
  101. package/dist/components/Progress/_internal/Circle.svelte +30 -2
  102. package/dist/components/Progress/_internal/Circle.svelte.d.ts +1 -0
  103. package/dist/components/Progress/index.css +50 -4
  104. package/dist/components/Skeleton/README.md +152 -0
  105. package/dist/components/Skeleton/Skeleton.svelte +9 -9
  106. package/dist/components/Skeleton/Skeleton.svelte.d.ts +0 -1
  107. package/dist/components/Skeleton/index.css +72 -45
  108. package/dist/components/Spinner/README.md +149 -37
  109. package/dist/components/Spinner/Spinner.svelte +14 -38
  110. package/dist/components/Spinner/Spinner.svelte.d.ts +2 -1
  111. package/dist/components/Spinner/SpinnerCircle.svelte +6 -34
  112. package/dist/components/Spinner/SpinnerCircle.svelte.d.ts +1 -0
  113. package/dist/components/Spinner/SpinnerCircleOscillate.svelte +10 -5
  114. package/dist/components/Spinner/SpinnerUnicode.svelte +3 -1
  115. package/dist/components/Spinner/SpinnerUnicode.svelte.d.ts +1 -0
  116. package/dist/components/Spinner/index.css +104 -0
  117. package/dist/components/Switch/README.md +45 -14
  118. package/dist/components/Switch/Switch.svelte +23 -48
  119. package/dist/components/Switch/Switch.svelte.d.ts +4 -2
  120. package/dist/components/Switch/index.css +121 -4
  121. package/dist/components/Switch/index.d.ts +1 -2
  122. package/dist/components/Switch/index.js +1 -2
  123. package/dist/components/TabbedMenu/README.md +37 -21
  124. package/dist/components/TabbedMenu/TabbedMenu.svelte +5 -46
  125. package/dist/components/TabbedMenu/TabbedMenu.svelte.d.ts +0 -1
  126. package/dist/components/TabbedMenu/index.css +84 -17
  127. package/dist/components/ThemePreview/README.md +289 -0
  128. package/dist/components/ThemePreview/ThemePreview.svelte +394 -0
  129. package/dist/components/ThemePreview/ThemePreview.svelte.d.ts +35 -0
  130. package/dist/components/ThemePreview/index.css +509 -0
  131. package/dist/components/ThemePreview/index.d.ts +1 -0
  132. package/dist/components/ThemePreview/index.js +1 -0
  133. package/dist/components/TwCheck/README.md +32 -13
  134. package/dist/components/TwCheck/TwCheck.svelte +11 -9
  135. package/dist/components/TwCheck/TwCheck.svelte.d.ts +0 -1
  136. package/dist/components/TwCheck/index.css +17 -2
  137. package/dist/components/TypeaheadInput/TypeaheadInput.svelte +20 -188
  138. package/dist/components/TypeaheadInput/TypeaheadInput.svelte.d.ts +4 -2
  139. package/dist/components/X/X.svelte +12 -5
  140. package/dist/components/X/X.svelte.d.ts +1 -0
  141. package/dist/icons/index.d.ts +1 -0
  142. package/dist/icons/index.js +1 -0
  143. package/dist/index.css +46 -26
  144. package/dist/index.d.ts +2 -0
  145. package/dist/index.js +2 -0
  146. package/dist/themes/blue-orange.css +217 -0
  147. package/dist/themes/blue-orange.d.ts +6 -0
  148. package/dist/themes/blue-orange.js +175 -0
  149. package/dist/themes/cyan-red.css +217 -0
  150. package/dist/themes/cyan-red.d.ts +6 -0
  151. package/dist/themes/cyan-red.js +175 -0
  152. package/dist/themes/cyan-slate.css +217 -0
  153. package/dist/themes/cyan-slate.d.ts +6 -0
  154. package/dist/themes/cyan-slate.js +175 -0
  155. package/dist/themes/emerald-pink.css +217 -0
  156. package/dist/themes/emerald-pink.d.ts +6 -0
  157. package/dist/themes/emerald-pink.js +175 -0
  158. package/dist/themes/fuchsia-emerald.css +217 -0
  159. package/dist/themes/fuchsia-emerald.d.ts +6 -0
  160. package/dist/themes/fuchsia-emerald.js +175 -0
  161. package/dist/themes/gray.css +217 -0
  162. package/dist/themes/gray.d.ts +6 -0
  163. package/dist/themes/gray.js +175 -0
  164. package/dist/themes/indigo-amber.css +217 -0
  165. package/dist/themes/indigo-amber.d.ts +6 -0
  166. package/dist/themes/indigo-amber.js +175 -0
  167. package/dist/themes/neutral.css +217 -0
  168. package/dist/themes/neutral.d.ts +6 -0
  169. package/dist/themes/neutral.js +175 -0
  170. package/dist/themes/pink-emerald.css +217 -0
  171. package/dist/themes/pink-emerald.d.ts +6 -0
  172. package/dist/themes/pink-emerald.js +175 -0
  173. package/dist/themes/purple-yellow.css +217 -0
  174. package/dist/themes/purple-yellow.d.ts +6 -0
  175. package/dist/themes/purple-yellow.js +175 -0
  176. package/dist/themes/rainbow.css +217 -0
  177. package/dist/themes/rainbow.d.ts +6 -0
  178. package/dist/themes/rainbow.js +180 -0
  179. package/dist/themes/red-blue.css +217 -0
  180. package/dist/themes/red-blue.d.ts +6 -0
  181. package/dist/themes/red-blue.js +175 -0
  182. package/dist/themes/red-cyan.css +217 -0
  183. package/dist/themes/red-cyan.d.ts +6 -0
  184. package/dist/themes/red-cyan.js +175 -0
  185. package/dist/themes/rose-teal.css +217 -0
  186. package/dist/themes/rose-teal.d.ts +6 -0
  187. package/dist/themes/rose-teal.js +175 -0
  188. package/dist/themes/sky-amber.css +217 -0
  189. package/dist/themes/sky-amber.d.ts +6 -0
  190. package/dist/themes/sky-amber.js +175 -0
  191. package/dist/themes/slate-cyan.css +217 -0
  192. package/dist/themes/slate-cyan.d.ts +6 -0
  193. package/dist/themes/slate-cyan.js +175 -0
  194. package/dist/themes/tailwind-color-pairs.md +31 -0
  195. package/dist/themes/teal-rose.css +217 -0
  196. package/dist/themes/teal-rose.d.ts +6 -0
  197. package/dist/themes/teal-rose.js +175 -0
  198. package/dist/themes/violet-lime.css +217 -0
  199. package/dist/themes/violet-lime.d.ts +6 -0
  200. package/dist/themes/violet-lime.js +175 -0
  201. package/dist/utils/design-tokens.d.ts +43 -0
  202. package/dist/utils/design-tokens.js +127 -0
  203. package/dist/utils/index.d.ts +1 -0
  204. package/dist/utils/index.js +1 -0
  205. package/dist/utils/storage-abstraction.js +1 -1
  206. package/package.json +14 -11
  207. package/dist/components/Switch/SwitchButton.svelte +0 -135
  208. package/dist/components/Switch/SwitchButton.svelte.d.ts +0 -21
@@ -1,7 +1,7 @@
1
1
  <script lang="ts" module>
2
2
  import type { Snippet } from "svelte";
3
3
  import type { FocusTrapOptions } from "../../actions/focus-trap.js";
4
- import { BodyScroll, focusTrap as focusTrapAction, twMerge } from "../../index.js";
4
+ import { BodyScroll, focusTrap as focusTrapAction } from "../../index.js";
5
5
  import { createClog } from "@marianmeres/clog";
6
6
  import { watch } from "runed";
7
7
  import { onDestroy } from "svelte";
@@ -26,6 +26,8 @@
26
26
  </script>
27
27
 
28
28
  <script lang="ts">
29
+ import { twMerge } from "../../utils/tw-merge.js";
30
+
29
31
  const clog = createClog("Backdrop", { color: "auto" });
30
32
 
31
33
  let {
@@ -142,6 +144,9 @@
142
144
  }
143
145
  return opts;
144
146
  });
147
+
148
+ // Build class string - add base class for CSS targeting, allow user overrides
149
+ let _class = $derived(twMerge("stuic-backdrop", classProp));
145
150
  </script>
146
151
 
147
152
  {#if visible}
@@ -149,7 +154,7 @@
149
154
  bind:this={el}
150
155
  role="presentation"
151
156
  tabindex="-1"
152
- class={twMerge("fixed inset-0 flex z-10 h-dvh", classProp)}
157
+ class={_class}
153
158
  in:fade={{ duration: fadeInDuration }}
154
159
  out:fade={{ duration: fadeOutDuration }}
155
160
  use:focusTrapAction={focusTrapOptions}
@@ -12,9 +12,10 @@ A semi-transparent overlay with focus trap and body scroll locking. Commonly use
12
12
  | `fadeOutDuration` | `number` | `150` | Fade out transition duration (ms) |
13
13
  | `transitionEnabled` | `boolean` | `true` | Enable/disable transitions |
14
14
  | `onEscape` | `() => void` | - | Callback when Escape key is pressed |
15
+ | `onBackdropClick` | `() => void` | - | Callback when backdrop area is clicked |
15
16
  | `noScrollLock` | `boolean` | `false` | Disable body scroll locking |
16
17
  | `el` | `HTMLDivElement` | - | Element reference (bindable) |
17
- | `class` | `string` | - | CSS classes for backdrop |
18
+ | `class` | `string` | - | Additional CSS classes |
18
19
 
19
20
  ## Methods
20
21
 
@@ -25,13 +26,52 @@ A semi-transparent overlay with focus trap and body scroll locking. Commonly use
25
26
  | `setOpener(el)` | Set element to refocus when closed |
26
27
  | `visibility()` | Returns object with `visible` getter |
27
28
 
29
+ ## CSS Variables
30
+
31
+ Override these tokens to customize appearance:
32
+
33
+ | Variable | Default | Description |
34
+ |----------|---------|-------------|
35
+ | `--stuic-backdrop-bg` | `rgb(0 0 0 / 0.5)` | Background color |
36
+ | `--stuic-backdrop-z-index` | `10` | Stacking order |
37
+
38
+ ### Global Override
39
+
40
+ ```css
41
+ :root {
42
+ --stuic-backdrop-bg: rgb(0 0 0 / 0.7);
43
+ --stuic-backdrop-z-index: 50;
44
+ }
45
+ ```
46
+
47
+ ### Local Override
48
+
49
+ ```svelte
50
+ <Backdrop
51
+ bind:visible
52
+ style="--stuic-backdrop-bg: rgb(0 0 0 / 0.8);"
53
+ >
54
+ ...
55
+ </Backdrop>
56
+ ```
57
+
58
+ ### Tailwind Class Override
59
+
60
+ You can still override styles using Tailwind classes via the `class` prop:
61
+
62
+ ```svelte
63
+ <Backdrop bind:visible class="bg-black/25">
64
+ ...
65
+ </Backdrop>
66
+ ```
67
+
28
68
  ## Usage
29
69
 
30
70
  ### Basic Backdrop
31
71
 
32
72
  ```svelte
33
73
  <script lang="ts">
34
- import { Backdrop } from 'stuic';
74
+ import { Backdrop } from '@marianmeres/stuic';
35
75
 
36
76
  let visible = $state(false);
37
77
  </script>
@@ -41,7 +81,7 @@ A semi-transparent overlay with focus trap and body scroll locking. Commonly use
41
81
  <Backdrop
42
82
  bind:visible
43
83
  onEscape={() => visible = false}
44
- class="bg-black/50"
84
+ onBackdropClick={() => visible = false}
45
85
  >
46
86
  <div class="m-auto p-8 bg-white rounded">
47
87
  Modal content
@@ -54,7 +94,7 @@ A semi-transparent overlay with focus trap and body scroll locking. Commonly use
54
94
 
55
95
  ```svelte
56
96
  <script lang="ts">
57
- import { Backdrop } from 'stuic';
97
+ import { Backdrop } from '@marianmeres/stuic';
58
98
 
59
99
  let backdrop: Backdrop;
60
100
  </script>
@@ -64,7 +104,6 @@ A semi-transparent overlay with focus trap and body scroll locking. Commonly use
64
104
  <Backdrop
65
105
  bind:this={backdrop}
66
106
  onEscape={() => backdrop.close()}
67
- class="bg-black/50"
68
107
  >
69
108
  <div class="m-auto p-8 bg-white rounded">
70
109
  Content here
@@ -72,10 +111,36 @@ A semi-transparent overlay with focus trap and body scroll locking. Commonly use
72
111
  </Backdrop>
73
112
  ```
74
113
 
114
+ ### Custom Styling
115
+
116
+ ```svelte
117
+ <!-- Lighter overlay -->
118
+ <Backdrop bind:visible class="bg-black/25">
119
+ <div>Overlay content</div>
120
+ </Backdrop>
121
+
122
+ <!-- Blur effect with CSS variable override -->
123
+ <Backdrop
124
+ bind:visible
125
+ class="backdrop-blur-sm"
126
+ style="--stuic-backdrop-bg: rgb(0 0 0 / 0.3);"
127
+ >
128
+ <div>Blurred backdrop content</div>
129
+ </Backdrop>
130
+
131
+ <!-- Higher z-index for stacking -->
132
+ <Backdrop
133
+ bind:visible
134
+ style="--stuic-backdrop-z-index: 100;"
135
+ >
136
+ <div>High priority overlay</div>
137
+ </Backdrop>
138
+ ```
139
+
75
140
  ### Without Scroll Lock
76
141
 
77
142
  ```svelte
78
- <Backdrop bind:visible noScrollLock class="bg-black/25">
143
+ <Backdrop bind:visible noScrollLock>
79
144
  <div>Overlay content - page still scrollable</div>
80
145
  </Backdrop>
81
146
  ```
@@ -0,0 +1,31 @@
1
+ /* ============================================================================
2
+ BACKDROP COMPONENT TOKENS
3
+ Override globally: :root { --stuic-backdrop-bg: rgb(0 0 0 / 0.3); }
4
+ Override locally: <Backdrop style="--stuic-backdrop-bg: rgb(0 0 0 / 0.8);">
5
+ ============================================================================ */
6
+
7
+ :root {
8
+ /* Component-level customization tokens */
9
+ --stuic-backdrop-bg: rgb(0 0 0 / 0.5);
10
+ --stuic-backdrop-z-index: 10;
11
+ }
12
+
13
+ @layer components {
14
+ /* ============================================================================
15
+ BASE STYLES
16
+ ============================================================================ */
17
+
18
+ .stuic-backdrop {
19
+ /* Layout */
20
+ position: fixed;
21
+ inset: 0;
22
+ display: flex;
23
+ height: 100dvh;
24
+
25
+ /* Stacking */
26
+ z-index: var(--stuic-backdrop-z-index);
27
+
28
+ /* Colors */
29
+ background: var(--stuic-backdrop-bg);
30
+ }
31
+ }
@@ -1,121 +1,79 @@
1
1
  <script lang="ts" module>
2
+ import type { HTMLButtonAttributes, HTMLAnchorAttributes } from "svelte/elements";
2
3
  import type { Snippet } from "svelte";
3
- import type { HTMLButtonAttributes } from "svelte/elements";
4
+ import type { IntentColorKey } from "../../utils/design-tokens.js";
5
+
6
+ export type ButtonVariant = "solid" | "outline" | "ghost" | "soft" | "link";
7
+ export type ButtonSize = "sm" | "md" | "lg" | "xl";
4
8
 
5
9
  export interface Props extends Omit<HTMLButtonAttributes, "children"> {
6
- /** Style variant (primary = accent colors, secondary = neutral) */
7
- variant?: "primary" | "secondary" | string;
8
- size?: "sm" | "md" | "lg" | string;
9
- /** Reduce text contrast for less emphasis */
10
+ /** Color intent (semantic meaning) */
11
+ intent?: IntentColorKey;
12
+ /** Visual variant (how colors are applied) */
13
+ variant?: ButtonVariant | string;
14
+ /** Size preset */
15
+ size?: ButtonSize | string;
16
+ /** Reduce emphasis */
10
17
  muted?: boolean;
11
- noshadow?: boolean;
12
- noborder?: boolean;
18
+ /** 3D push effect */
19
+ raised?: boolean;
13
20
  /** Skip all default styling, use only custom classes */
14
21
  unstyled?: boolean;
15
- /** Transparent bg, styled on hover */
16
- inverse?: boolean;
22
+ /** Render as rounded-full */
23
+ roundedFull?: boolean;
24
+ /** Render as aspect ratio 1 */
25
+ aspect1?: boolean;
26
+ /** Additional CSS classes */
17
27
  class?: string;
18
28
  /** Render as anchor tag instead of button */
19
29
  href?: string;
30
+ /** Content snippet */
20
31
  children?: Snippet<[{ checked?: boolean }]>;
21
- /** Enable switch/toggle behavior (uses aria-checked) */
22
- roleSwitch?: boolean;
32
+ /** Toggle state for switch behavior */
23
33
  checked?: boolean;
24
- el?: Element;
25
- /* */
26
- tooltip?: TooltipConfig;
27
- }
28
-
29
- export interface ButtonPresetClasses {
30
- size: Record<string, string>;
31
- variant: Record<string, string>;
32
- muted: string;
33
- shadow: string;
34
- inverse: string;
34
+ /** Enable switch/toggle behavior */
35
+ roleSwitch?: boolean;
36
+ /** Bindable element reference */
37
+ el?: HTMLElement;
38
+ /** Optional tooltip configuration or direct content */
39
+ tooltip?: string | TooltipConfig;
40
+ /** Is this button a "X" button (this is a pragmatic convenience) */
41
+ x?: boolean | XProps;
42
+ /** Optional out-of-the-box spinner support */
43
+ spinner?: boolean | THC;
44
+ /** Show only spinner when spinner? */
45
+ spinnerOnly?: boolean;
35
46
  }
36
-
37
- export const BUTTON_STUIC_BASE_CLASSES = `
38
- bg-button-bg text-button-text
39
- dark:bg-button-bg-dark dark:text-button-text-dark
40
- text-base text-center
41
- leading-none
42
- border-1
43
- border-button-border dark:border-button-border-dark
44
- rounded-lg
45
- inline-flex items-center justify-center gap-x-2
46
- px-4 py-3
47
- select-none
48
- min-h-[44px] min-w-[44px]
49
-
50
- hover:brightness-105
51
- active:brightness-95
52
- disabled:hover:brightness-100 disabled:opacity-50
53
-
54
- focus:brightness-105
55
- focus:border-button-border-focus focus:dark:border-button-border-focus-dark
56
-
57
- focus:outline-4 focus:outline-black/10 focus:dark:outline-white/20
58
- focus-visible:outline-4 focus-visible:outline-black/10 focus-visible:dark:outline-white/20
59
- `;
60
-
61
- export const BUTTON_STUIC_PRESET_CLASSES: ButtonPresetClasses = {
62
- size: {
63
- sm: `text-sm rounded-md px-3 py-2 min-h-none min-w-none`,
64
- lg: `text-lg rounded-xl`,
65
- },
66
- variant: {
67
- primary: `font-medium`,
68
- secondary: `
69
- bg-neutral-100 dark:bg-neutral-600
70
- text-black/60 dark:text-white/80
71
- shadow-[1px_1px_0_0_rgba(0_0_0_/_.2)]
72
- active:shadow-none active:translate-[1px]
73
- focus:shadow-black/30
74
- `,
75
- },
76
- muted: `text-black/70 dark:text-white/70`,
77
- shadow: `
78
- shadow-[1px_1px_0_0_rgba(0_0_0_/_.4)]
79
- active:shadow-none active:translate-[1px]
80
- disabled:shadow-none disabled:active:shadow-none disabled:active:translate-none
81
- `,
82
- inverse: `
83
- bg-transparent dark:bg-transparent
84
- hover:bg-button-bg hover:dark:bg-button-bg-dark
85
- hover:brightness-100
86
- `,
87
- };
88
47
  </script>
89
48
 
90
49
  <script lang="ts">
91
50
  import { twMerge } from "../../utils/tw-merge.js";
92
- //
93
- import "./index.css";
94
- import { tooltip, type TooltipConfig } from "../../actions/index.js";
95
-
51
+ import { tooltip, type TooltipConfig } from "../../actions/tooltip/tooltip.svelte.js";
52
+ import { X, type XProps } from "../X/index.js";
53
+ import Thc, { type THC } from "../Thc/Thc.svelte";
54
+ import Spinner from "../Spinner/Spinner.svelte";
96
55
  let {
97
- variant,
98
- size,
99
56
  class: classProp,
100
- muted,
101
- noshadow,
102
- noborder,
103
- inverse,
104
- unstyled,
57
+ intent,
58
+ size = "md",
59
+ variant = "solid",
105
60
  href,
106
61
  children,
107
- //
108
- roleSwitch = false,
109
62
  checked = $bindable(false),
63
+ roleSwitch = false,
110
64
  el = $bindable(),
111
- //
112
- tooltip: tooltipConfig = () => ({ enabled: false }),
113
- //
65
+ muted = false,
66
+ raised = false,
67
+ unstyled = false,
68
+ roundedFull = false,
69
+ aspect1 = false,
70
+ tooltip: _tooltip,
71
+ x,
72
+ spinner,
73
+ spinnerOnly,
114
74
  ...rest
115
75
  }: Props = $props();
116
76
 
117
- // let button: HTMLButtonElement | undefined = $state();
118
-
119
77
  $effect(() => {
120
78
  const toggle = () => (checked = !checked);
121
79
  if (!href && roleSwitch && el) {
@@ -124,51 +82,85 @@
124
82
  return () => el?.removeEventListener("click", toggle);
125
83
  });
126
84
 
127
- const _base = BUTTON_STUIC_BASE_CLASSES;
128
- const _preset: any = BUTTON_STUIC_PRESET_CLASSES;
85
+ // Build class string - add base class for CSS targeting unless unstyled
86
+ let _class = $derived(unstyled ? classProp : twMerge("stuic-button", classProp));
87
+
88
+ let _tooltipConfig: TooltipConfig = $derived.by(() => {
89
+ if (typeof _tooltip === "string") {
90
+ return () => ({ enabled: true, content: _tooltip });
91
+ }
92
+ return _tooltip ? _tooltip : () => ({ enabled: false });
93
+ });
94
+
95
+ let _xProps: undefined | XProps = $derived.by(() => {
96
+ if (x) {
97
+ return typeof x === "boolean" ? {} : x;
98
+ }
99
+ });
129
100
 
130
- // see button.css
131
- let _class = $derived(
132
- [
133
- // "namespace" (so we can target it in css files when customizing)
134
- "stuic-button",
135
- // pass all styling props as classnames as well
136
- variant,
137
- size,
138
- muted && "muted",
139
- noshadow && "no-shadow",
140
- noborder && "border-none",
141
- inverse && "inverse",
142
- // now, attach the default tw classes (unless not explicitly forbidden)
143
- !unstyled && _base,
144
- !unstyled && size && _preset.size[size],
145
- !unstyled && variant && _preset.variant[variant],
146
- !unstyled && muted && _preset.muted,
147
- !unstyled && !noshadow && _preset.shadow,
148
- !unstyled && inverse && _preset.inverse,
149
- ]
150
- .filter(Boolean)
151
- .join(" ")
152
- );
101
+ // "x" implicitly set aspect1
102
+ let _isAspect1 = $derived(aspect1 || _xProps);
153
103
  </script>
154
104
 
155
105
  {#if href}
156
106
  <a
157
107
  {href}
158
108
  bind:this={el}
159
- class={twMerge(_class, classProp)}
160
- use:tooltip={tooltipConfig}
161
- {...rest as any}
109
+ class={_class}
110
+ data-intent={!unstyled ? intent : undefined}
111
+ data-variant={!unstyled ? variant : undefined}
112
+ data-size={!unstyled ? size : undefined}
113
+ data-muted={!unstyled && muted ? "true" : undefined}
114
+ data-raised={!unstyled && raised ? "true" : undefined}
115
+ data-checked={roleSwitch && checked ? "true" : undefined}
116
+ data-rounded-full={!unstyled && roundedFull ? "true" : undefined}
117
+ data-aspect1={!unstyled && _isAspect1 ? "true" : undefined}
118
+ use:tooltip={_tooltipConfig}
119
+ {...rest as HTMLAnchorAttributes}
162
120
  >
163
- {@render children?.({})}
121
+ {#if _xProps}
122
+ <X {..._xProps} />
123
+ {:else}
124
+ {#if spinner}
125
+ {#if typeof spinner === "boolean"}
126
+ <Spinner />
127
+ {:else}
128
+ <Thc thc={spinner} />
129
+ {/if}
130
+ {/if}
131
+ {#if !spinner || (spinner && !spinnerOnly)}
132
+ {@render children?.({ checked })}
133
+ {/if}
134
+ {/if}
164
135
  </a>
165
136
  {:else}
166
137
  <button
167
138
  bind:this={el}
168
- class={twMerge(_class, classProp)}
169
- use:tooltip={tooltipConfig}
139
+ class={_class}
140
+ data-intent={!unstyled ? intent : undefined}
141
+ data-variant={!unstyled ? variant : undefined}
142
+ data-size={!unstyled ? size : undefined}
143
+ data-muted={!unstyled && muted ? "true" : undefined}
144
+ data-raised={!unstyled && raised ? "true" : undefined}
145
+ data-checked={roleSwitch && checked ? "true" : undefined}
146
+ data-rounded-full={!unstyled && roundedFull ? "true" : undefined}
147
+ data-aspect1={!unstyled && _isAspect1 ? "true" : undefined}
148
+ use:tooltip={_tooltipConfig}
170
149
  {...rest}
171
150
  >
172
- {@render children?.({ checked })}
151
+ {#if _xProps}
152
+ <X {..._xProps} />
153
+ {:else}
154
+ {#if spinner}
155
+ {#if typeof spinner === "boolean"}
156
+ <Spinner />
157
+ {:else}
158
+ <Thc thc={spinner} />
159
+ {/if}
160
+ {/if}
161
+ {#if !spinner || (spinner && !spinnerOnly)}
162
+ {@render children?.({ checked })}
163
+ {/if}
164
+ {/if}
173
165
  </button>
174
166
  {/if}
@@ -1,40 +1,51 @@
1
- import type { Snippet } from "svelte";
2
1
  import type { HTMLButtonAttributes } from "svelte/elements";
2
+ import type { Snippet } from "svelte";
3
+ import type { IntentColorKey } from "../../utils/design-tokens.js";
4
+ export type ButtonVariant = "solid" | "outline" | "ghost" | "soft" | "link";
5
+ export type ButtonSize = "sm" | "md" | "lg" | "xl";
3
6
  export interface Props extends Omit<HTMLButtonAttributes, "children"> {
4
- /** Style variant (primary = accent colors, secondary = neutral) */
5
- variant?: "primary" | "secondary" | string;
6
- size?: "sm" | "md" | "lg" | string;
7
- /** Reduce text contrast for less emphasis */
7
+ /** Color intent (semantic meaning) */
8
+ intent?: IntentColorKey;
9
+ /** Visual variant (how colors are applied) */
10
+ variant?: ButtonVariant | string;
11
+ /** Size preset */
12
+ size?: ButtonSize | string;
13
+ /** Reduce emphasis */
8
14
  muted?: boolean;
9
- noshadow?: boolean;
10
- noborder?: boolean;
15
+ /** 3D push effect */
16
+ raised?: boolean;
11
17
  /** Skip all default styling, use only custom classes */
12
18
  unstyled?: boolean;
13
- /** Transparent bg, styled on hover */
14
- inverse?: boolean;
19
+ /** Render as rounded-full */
20
+ roundedFull?: boolean;
21
+ /** Render as aspect ratio 1 */
22
+ aspect1?: boolean;
23
+ /** Additional CSS classes */
15
24
  class?: string;
16
25
  /** Render as anchor tag instead of button */
17
26
  href?: string;
27
+ /** Content snippet */
18
28
  children?: Snippet<[{
19
29
  checked?: boolean;
20
30
  }]>;
21
- /** Enable switch/toggle behavior (uses aria-checked) */
22
- roleSwitch?: boolean;
31
+ /** Toggle state for switch behavior */
23
32
  checked?: boolean;
24
- el?: Element;
25
- tooltip?: TooltipConfig;
26
- }
27
- export interface ButtonPresetClasses {
28
- size: Record<string, string>;
29
- variant: Record<string, string>;
30
- muted: string;
31
- shadow: string;
32
- inverse: string;
33
+ /** Enable switch/toggle behavior */
34
+ roleSwitch?: boolean;
35
+ /** Bindable element reference */
36
+ el?: HTMLElement;
37
+ /** Optional tooltip configuration or direct content */
38
+ tooltip?: string | TooltipConfig;
39
+ /** Is this button a "X" button (this is a pragmatic convenience) */
40
+ x?: boolean | XProps;
41
+ /** Optional out-of-the-box spinner support */
42
+ spinner?: boolean | THC;
43
+ /** Show only spinner when spinner? */
44
+ spinnerOnly?: boolean;
33
45
  }
34
- export declare const BUTTON_STUIC_BASE_CLASSES = "\n\t\tbg-button-bg text-button-text\n\t\tdark:bg-button-bg-dark dark:text-button-text-dark\n\t\ttext-base text-center\n\t\tleading-none\n\t\tborder-1\n\t\tborder-button-border dark:border-button-border-dark\n\t\trounded-lg\n\t\tinline-flex items-center justify-center gap-x-2\n\t\tpx-4 py-3\n\t\tselect-none\n\t\tmin-h-[44px] min-w-[44px]\n\n\t\thover:brightness-105\n\t\tactive:brightness-95\n\t\tdisabled:hover:brightness-100 disabled:opacity-50\n\n\t\tfocus:brightness-105\n\t\tfocus:border-button-border-focus focus:dark:border-button-border-focus-dark\n\n\t\t focus:outline-4 focus:outline-black/10 focus:dark:outline-white/20\n\t\tfocus-visible:outline-4 focus-visible:outline-black/10 focus-visible:dark:outline-white/20\n\t";
35
- export declare const BUTTON_STUIC_PRESET_CLASSES: ButtonPresetClasses;
36
- import "./index.css";
37
- import { type TooltipConfig } from "../../actions/index.js";
46
+ import { type TooltipConfig } from "../../actions/tooltip/tooltip.svelte.js";
47
+ import { type XProps } from "../X/index.js";
48
+ import { type THC } from "../Thc/Thc.svelte";
38
49
  declare const Button: import("svelte").Component<Props, {}, "checked" | "el">;
39
50
  type Button = ReturnType<typeof Button>;
40
51
  export default Button;