@marianmeres/stuic 2.66.0 → 3.0.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 (171) hide show
  1. package/README.md +292 -4
  2. package/dist/README.md +41 -18
  3. package/dist/actions/popover/README.md +19 -0
  4. package/dist/actions/popover/index.css +6 -9
  5. package/dist/actions/popover/popover.svelte.js +2 -2
  6. package/dist/actions/tooltip/README.md +18 -0
  7. package/dist/actions/tooltip/index.css +5 -8
  8. package/dist/actions/tooltip/tooltip.svelte.js +1 -1
  9. package/dist/components/AlertConfirmPrompt/AlertConfirmPrompt.svelte +9 -10
  10. package/dist/components/AlertConfirmPrompt/AlertConfirmPrompt.svelte.d.ts +3 -3
  11. package/dist/components/AlertConfirmPrompt/Current.svelte +15 -17
  12. package/dist/components/AlertConfirmPrompt/Current.svelte.d.ts +5 -3
  13. package/dist/components/AlertConfirmPrompt/acp-icons.js +5 -4
  14. package/dist/components/AlertConfirmPrompt/index.css +62 -0
  15. package/dist/components/AssetsPreview/AssetsPreview.svelte +92 -73
  16. package/dist/components/AssetsPreview/AssetsPreview.svelte.d.ts +1 -0
  17. package/dist/components/AssetsPreview/index.css +59 -0
  18. package/dist/components/Avatar/Avatar.svelte +32 -18
  19. package/dist/components/Avatar/Avatar.svelte.d.ts +1 -0
  20. package/dist/components/Avatar/README.md +166 -0
  21. package/dist/components/Avatar/index.css +128 -0
  22. package/dist/components/Backdrop/Backdrop.svelte +8 -2
  23. package/dist/components/Backdrop/Backdrop.svelte.d.ts +1 -0
  24. package/dist/components/Backdrop/README.md +71 -6
  25. package/dist/components/Backdrop/index.css +29 -0
  26. package/dist/components/Button/Button.svelte +117 -124
  27. package/dist/components/Button/Button.svelte.d.ts +35 -23
  28. package/dist/components/Button/README.md +87 -21
  29. package/dist/components/Button/index.css +473 -9
  30. package/dist/components/Button/index.d.ts +1 -1
  31. package/dist/components/Button/index.js +1 -1
  32. package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte +7 -38
  33. package/dist/components/ButtonGroupRadio/README.md +82 -4
  34. package/dist/components/ButtonGroupRadio/index.css +152 -14
  35. package/dist/components/Collapsible/Collapsible.svelte +7 -7
  36. package/dist/components/Collapsible/Collapsible.svelte.d.ts +2 -2
  37. package/dist/components/Collapsible/README.md +34 -2
  38. package/dist/components/Collapsible/index.css +38 -0
  39. package/dist/components/CommandMenu/CommandMenu.svelte +13 -24
  40. package/dist/components/CommandMenu/README.md +39 -0
  41. package/dist/components/CommandMenu/index.css +45 -2
  42. package/dist/components/DismissibleMessage/DismissibleMessage.svelte +53 -50
  43. package/dist/components/DismissibleMessage/DismissibleMessage.svelte.d.ts +6 -5
  44. package/dist/components/DismissibleMessage/README.md +93 -11
  45. package/dist/components/DismissibleMessage/index.css +122 -8
  46. package/dist/components/DismissibleMessage/index.d.ts +1 -1
  47. package/dist/components/DropdownMenu/DropdownMenu.svelte +14 -50
  48. package/dist/components/DropdownMenu/DropdownMenu.svelte.d.ts +6 -6
  49. package/dist/components/DropdownMenu/README.md +132 -0
  50. package/dist/components/DropdownMenu/index.css +231 -27
  51. package/dist/components/Input/FieldAssets.svelte +8 -5
  52. package/dist/components/Input/FieldCheckbox.svelte +7 -44
  53. package/dist/components/Input/FieldFile.svelte +1 -6
  54. package/dist/components/Input/FieldInput.svelte +1 -1
  55. package/dist/components/Input/FieldOptions.svelte +41 -38
  56. package/dist/components/Input/FieldRadios.svelte +7 -16
  57. package/dist/components/Input/FieldSelect.svelte +1 -1
  58. package/dist/components/Input/FieldSwitch.svelte +1 -5
  59. package/dist/components/Input/FieldTextarea.svelte +1 -1
  60. package/dist/components/Input/README.md +194 -0
  61. package/dist/components/Input/_internal/FieldRadioInternal.svelte +2 -40
  62. package/dist/components/Input/_internal/InputWrap.svelte +8 -48
  63. package/dist/components/Input/index.css +522 -127
  64. package/dist/components/ListItemButton/ListItemButton.svelte +37 -73
  65. package/dist/components/ListItemButton/ListItemButton.svelte.d.ts +1 -9
  66. package/dist/components/ListItemButton/README.md +100 -45
  67. package/dist/components/ListItemButton/index.css +175 -56
  68. package/dist/components/ListItemButton/index.d.ts +1 -1
  69. package/dist/components/ListItemButton/index.js +1 -1
  70. package/dist/components/Modal/Modal.svelte +2 -8
  71. package/dist/components/Modal/Modal.svelte.d.ts +1 -0
  72. package/dist/components/Modal/README.md +29 -0
  73. package/dist/components/Modal/index.css +36 -0
  74. package/dist/components/ModalDialog/ModalDialog.svelte +2 -21
  75. package/dist/components/ModalDialog/README.md +35 -0
  76. package/dist/components/ModalDialog/index.css +57 -0
  77. package/dist/components/Notifications/Notifications.svelte +44 -128
  78. package/dist/components/Notifications/Notifications.svelte.d.ts +9 -17
  79. package/dist/components/Notifications/README.md +186 -70
  80. package/dist/components/Notifications/index.css +212 -15
  81. package/dist/components/Progress/README.md +15 -0
  82. package/dist/components/Progress/_internal/Bar.svelte +2 -2
  83. package/dist/components/Progress/index.css +4 -4
  84. package/dist/components/Skeleton/Skeleton.svelte +3 -2
  85. package/dist/components/Skeleton/index.css +11 -14
  86. package/dist/components/Spinner/Spinner.svelte +2 -2
  87. package/dist/components/Spinner/SpinnerCircle.svelte +1 -1
  88. package/dist/components/Switch/README.md +15 -0
  89. package/dist/components/Switch/Switch.svelte +4 -7
  90. package/dist/components/Switch/Switch.svelte.d.ts +1 -1
  91. package/dist/components/Switch/SwitchButton.svelte +4 -5
  92. package/dist/components/Switch/index.css +3 -4
  93. package/dist/components/TabbedMenu/README.md +26 -21
  94. package/dist/components/TabbedMenu/TabbedMenu.svelte +5 -5
  95. package/dist/components/TabbedMenu/index.css +7 -22
  96. package/dist/components/ThemePreview/README.md +289 -0
  97. package/dist/components/ThemePreview/ThemePreview.svelte +341 -0
  98. package/dist/components/ThemePreview/ThemePreview.svelte.d.ts +33 -0
  99. package/dist/components/ThemePreview/index.css +493 -0
  100. package/dist/components/ThemePreview/index.d.ts +1 -0
  101. package/dist/components/ThemePreview/index.js +1 -0
  102. package/dist/components/TwCheck/TwCheck.svelte +4 -4
  103. package/dist/components/TwCheck/index.css +3 -2
  104. package/dist/components/TypeaheadInput/TypeaheadInput.svelte +1 -1
  105. package/dist/components/X/X.svelte +12 -5
  106. package/dist/components/X/X.svelte.d.ts +1 -0
  107. package/dist/icons/index.d.ts +1 -0
  108. package/dist/icons/index.js +1 -0
  109. package/dist/index.css +31 -16
  110. package/dist/index.d.ts +1 -0
  111. package/dist/index.js +1 -0
  112. package/dist/themes/blue-orange.css +163 -0
  113. package/dist/themes/blue-orange.d.ts +6 -0
  114. package/dist/themes/blue-orange.js +151 -0
  115. package/dist/themes/cyan-red.css +163 -0
  116. package/dist/themes/cyan-red.d.ts +6 -0
  117. package/dist/themes/cyan-red.js +151 -0
  118. package/dist/themes/cyan-slate.css +163 -0
  119. package/dist/themes/cyan-slate.d.ts +6 -0
  120. package/dist/themes/cyan-slate.js +151 -0
  121. package/dist/themes/emerald-pink.css +163 -0
  122. package/dist/themes/emerald-pink.d.ts +6 -0
  123. package/dist/themes/emerald-pink.js +151 -0
  124. package/dist/themes/fuchsia-emerald.css +163 -0
  125. package/dist/themes/fuchsia-emerald.d.ts +6 -0
  126. package/dist/themes/fuchsia-emerald.js +151 -0
  127. package/dist/themes/gray.css +163 -0
  128. package/dist/themes/gray.d.ts +6 -0
  129. package/dist/themes/gray.js +151 -0
  130. package/dist/themes/indigo-amber.css +163 -0
  131. package/dist/themes/indigo-amber.d.ts +6 -0
  132. package/dist/themes/indigo-amber.js +151 -0
  133. package/dist/themes/neutral.css +163 -0
  134. package/dist/themes/neutral.d.ts +6 -0
  135. package/dist/themes/neutral.js +151 -0
  136. package/dist/themes/pink-emerald.css +163 -0
  137. package/dist/themes/pink-emerald.d.ts +6 -0
  138. package/dist/themes/pink-emerald.js +151 -0
  139. package/dist/themes/purple-yellow.css +163 -0
  140. package/dist/themes/purple-yellow.d.ts +6 -0
  141. package/dist/themes/purple-yellow.js +151 -0
  142. package/dist/themes/rainbow.css +163 -0
  143. package/dist/themes/rainbow.d.ts +6 -0
  144. package/dist/themes/rainbow.js +156 -0
  145. package/dist/themes/red-blue.css +163 -0
  146. package/dist/themes/red-blue.d.ts +6 -0
  147. package/dist/themes/red-blue.js +151 -0
  148. package/dist/themes/red-cyan.css +163 -0
  149. package/dist/themes/red-cyan.d.ts +6 -0
  150. package/dist/themes/red-cyan.js +151 -0
  151. package/dist/themes/rose-teal.css +163 -0
  152. package/dist/themes/rose-teal.d.ts +6 -0
  153. package/dist/themes/rose-teal.js +151 -0
  154. package/dist/themes/sky-amber.css +163 -0
  155. package/dist/themes/sky-amber.d.ts +6 -0
  156. package/dist/themes/sky-amber.js +151 -0
  157. package/dist/themes/slate-cyan.css +163 -0
  158. package/dist/themes/slate-cyan.d.ts +6 -0
  159. package/dist/themes/slate-cyan.js +151 -0
  160. package/dist/themes/tailwind-color-pairs.md +31 -0
  161. package/dist/themes/teal-rose.css +163 -0
  162. package/dist/themes/teal-rose.d.ts +6 -0
  163. package/dist/themes/teal-rose.js +151 -0
  164. package/dist/themes/violet-lime.css +163 -0
  165. package/dist/themes/violet-lime.d.ts +6 -0
  166. package/dist/themes/violet-lime.js +151 -0
  167. package/dist/utils/design-tokens.d.ts +43 -0
  168. package/dist/utils/design-tokens.js +100 -0
  169. package/dist/utils/index.d.ts +1 -0
  170. package/dist/utils/index.js +1 -0
  171. package/package.json +22 -2
@@ -1,14 +1,478 @@
1
+ /* ============================================================================
2
+ BUTTON COMPONENT TOKENS
3
+ Override globally: :root { --stuic-button-radius: 0; }
4
+ Override locally: <Button style="--stuic-button-radius: 9999px;">
5
+ ============================================================================ */
6
+
1
7
  /* prettier-ignore */
2
- @theme inline {
3
- --color-button-bg: var(--color-button-bg, var(--color-neutral-200));
4
- --color-button-bg-dark: var(--color-button-bg-dark, var(--color-neutral-500));
8
+ :root {
9
+ /* Component-level customization tokens */
10
+ --stuic-button-radius: var(--radius-md);
11
+ --stuic-button-font-family: var(--font-sans);
12
+ --stuic-button-font-weight: var(--font-weight-medium);
13
+ --stuic-button-transition: 150ms;
14
+
15
+ /* Focus ring */
16
+ --stuic-button-ring-width: 3px;
17
+ --stuic-button-ring-offset: 0px;
18
+ --stuic-button-ring-color: var(--stuic-color-ring);
19
+
20
+ /* Size: sm (min 44px touch target) */
21
+ --stuic-button-padding-x-sm: 0.75rem;
22
+ --stuic-button-padding-y-sm: 0.5rem;
23
+ --stuic-button-font-size-sm: 0.875rem;
24
+ --stuic-button-min-height-sm: 2.75rem; /* 44px - Apple HIG minimum */
25
+
26
+ /* Size: md (min 44px touch target) */
27
+ --stuic-button-padding-x-md: 1rem;
28
+ --stuic-button-padding-y-md: 0.625rem;
29
+ --stuic-button-font-size-md: 1rem;
30
+ --stuic-button-min-height-md: 2.75rem; /* 44px */
31
+
32
+ /* Size: lg */
33
+ --stuic-button-padding-x-lg: 1.25rem;
34
+ --stuic-button-padding-y-lg: 0.625rem;
35
+ --stuic-button-font-size-lg: 1.125rem;
36
+ --stuic-button-min-height-lg: 3rem;
37
+
38
+ /* Size: xl */
39
+ --stuic-button-padding-x-xl: 1.5rem;
40
+ --stuic-button-padding-y-xl: 0.75rem;
41
+ --stuic-button-font-size-xl: 1.25rem;
42
+ --stuic-button-min-height-xl: 3.5rem;
43
+
44
+ /* Raised (3D push) effect */
45
+ --stuic-button-raised-offset: 2px;
46
+ --stuic-button-raised-color: rgb(0 0 0 / 0.8);
47
+ }
48
+
49
+ /* ============================================================================
50
+ BASE STYLES
51
+ ============================================================================ */
52
+
53
+ .stuic-button {
54
+ /* Layout */
55
+ display: inline-flex;
56
+ align-items: center;
57
+ justify-content: center;
58
+ gap: 0.5rem;
59
+
60
+ /* Typography */
61
+ font-family: var(--stuic-button-font-family);
62
+ font-weight: var(--stuic-button-font-weight);
63
+ line-height: 1;
64
+ text-align: center;
65
+ text-decoration: none;
66
+ white-space: nowrap;
67
+
68
+ /* Box model */
69
+ border-width: 1px;
70
+ border-style: solid;
71
+ border-radius: var(--stuic-button-radius);
72
+
73
+ /* Interaction */
74
+ cursor: pointer;
75
+ user-select: none;
76
+ -webkit-tap-highlight-color: transparent; /* Remove iOS tap highlight */
77
+ touch-action: manipulation; /* Disable double-tap zoom for faster response */
78
+ transition:
79
+ background var(--stuic-button-transition),
80
+ color var(--stuic-button-transition),
81
+ border-color var(--stuic-button-transition),
82
+ opacity var(--stuic-button-transition);
83
+
84
+ /* Colors - use internal vars set by intent/variant */
85
+ background: var(--_bg);
86
+ color: var(--_text);
87
+ border-color: var(--_border);
88
+ }
89
+
90
+ /* Focus styles */
91
+ .stuic-button:focus-visible {
92
+ outline: var(--stuic-button-ring-width) solid
93
+ var(--_ring, var(--stuic-button-ring-color));
94
+ outline-offset: var(--stuic-button-ring-offset);
95
+ }
96
+
97
+ /* Hover state */
98
+ .stuic-button:hover:not(:disabled) {
99
+ background: var(--_bg-hover);
100
+ color: var(--_text-hover);
101
+ border-color: var(--_border-hover);
102
+ }
103
+
104
+ /* Active state */
105
+ .stuic-button:active:not(:disabled) {
106
+ background: var(--_bg-active);
107
+ color: var(--_text-active);
108
+ border-color: var(--_border-active);
109
+ }
110
+
111
+ /* Disabled state */
112
+ .stuic-button:disabled {
113
+ opacity: 0.5;
114
+ cursor: not-allowed;
115
+ }
116
+
117
+ /* ============================================================================
118
+ SIZE VARIANTS
119
+ ============================================================================ */
120
+
121
+ .stuic-button[data-size="sm"] {
122
+ padding: var(--stuic-button-padding-y-sm) var(--stuic-button-padding-x-sm);
123
+ font-size: var(--stuic-button-font-size-sm);
124
+ min-height: var(--stuic-button-min-height-sm);
125
+ }
126
+
127
+ .stuic-button[data-size="md"] {
128
+ padding: var(--stuic-button-padding-y-md) var(--stuic-button-padding-x-md);
129
+ font-size: var(--stuic-button-font-size-md);
130
+ min-height: var(--stuic-button-min-height-md);
131
+ }
132
+
133
+ .stuic-button[data-size="lg"] {
134
+ padding: var(--stuic-button-padding-y-lg) var(--stuic-button-padding-x-lg);
135
+ font-size: var(--stuic-button-font-size-lg);
136
+ min-height: var(--stuic-button-min-height-lg);
137
+ }
138
+
139
+ .stuic-button[data-size="xl"] {
140
+ padding: var(--stuic-button-padding-y-xl) var(--stuic-button-padding-x-xl);
141
+ font-size: var(--stuic-button-font-size-xl);
142
+ min-height: var(--stuic-button-min-height-xl);
143
+ }
144
+
145
+ /* ============================================================================
146
+ INTENT COLOR MAPPING
147
+ Each intent sets the color palette via internal CSS vars.
148
+ ============================================================================ */
149
+
150
+ /* Default (no intent): neutral gray (no blue undertone) */
151
+ .stuic-button:not([data-intent]) {
152
+ --_color: var(--color-neutral-200);
153
+ --_color-hover: var(--color-neutral-300);
154
+ --_color-active: var(--color-neutral-400);
155
+ --_fg: var(--color-neutral-800);
156
+ --_fg-hover: var(--color-neutral-800);
157
+ --_fg-active: var(--color-neutral-800);
158
+ }
159
+
160
+ .stuic-button[data-intent="primary"] {
161
+ --_color: var(--stuic-color-primary);
162
+ --_color-hover: var(--stuic-color-primary-hover);
163
+ --_color-active: var(--stuic-color-primary-active);
164
+ --_fg: var(--stuic-color-primary-foreground);
165
+ --_fg-hover: var(--stuic-color-primary-foreground-hover);
166
+ --_fg-active: var(--stuic-color-primary-foreground-active);
167
+ }
168
+
169
+ .stuic-button[data-intent="accent"] {
170
+ --_color: var(--stuic-color-accent);
171
+ --_color-hover: var(--stuic-color-accent-hover);
172
+ --_color-active: var(--stuic-color-accent-active);
173
+ --_fg: var(--stuic-color-accent-foreground);
174
+ --_fg-hover: var(--stuic-color-accent-foreground-hover);
175
+ --_fg-active: var(--stuic-color-accent-foreground-active);
176
+ }
177
+
178
+ .stuic-button[data-intent="destructive"] {
179
+ --_color: var(--stuic-color-destructive);
180
+ --_color-hover: var(--stuic-color-destructive-hover);
181
+ --_color-active: var(--stuic-color-destructive-active);
182
+ --_fg: var(--stuic-color-destructive-foreground);
183
+ --_fg-hover: var(--stuic-color-destructive-foreground-hover);
184
+ --_fg-active: var(--stuic-color-destructive-foreground-active);
185
+ }
186
+
187
+ .stuic-button[data-intent="warning"] {
188
+ --_color: var(--stuic-color-warning);
189
+ --_color-hover: var(--stuic-color-warning-hover);
190
+ --_color-active: var(--stuic-color-warning-active);
191
+ --_fg: var(--stuic-color-warning-foreground);
192
+ --_fg-hover: var(--stuic-color-warning-foreground-hover);
193
+ --_fg-active: var(--stuic-color-warning-foreground-active);
194
+ }
195
+
196
+ .stuic-button[data-intent="success"] {
197
+ --_color: var(--stuic-color-success);
198
+ --_color-hover: var(--stuic-color-success-hover);
199
+ --_color-active: var(--stuic-color-success-active);
200
+ --_fg: var(--stuic-color-success-foreground);
201
+ --_fg-hover: var(--stuic-color-success-foreground-hover);
202
+ --_fg-active: var(--stuic-color-success-foreground-active);
203
+ }
204
+
205
+ .stuic-button[data-intent="info"] {
206
+ --_color: var(--stuic-color-info);
207
+ --_color-hover: var(--stuic-color-info-hover);
208
+ --_color-active: var(--stuic-color-info-active);
209
+ --_fg: var(--stuic-color-info-foreground);
210
+ --_fg-hover: var(--stuic-color-info-foreground-hover);
211
+ --_fg-active: var(--stuic-color-info-foreground-active);
212
+ }
213
+
214
+ /* ============================================================================
215
+ VARIANT STYLES
216
+ Each variant determines HOW the color palette is applied.
217
+ ============================================================================ */
218
+
219
+ /* Solid (default): filled background with contrasting text */
220
+ .stuic-button[data-variant="solid"],
221
+ .stuic-button:not([data-variant]) {
222
+ --_bg: var(--_color);
223
+ --_bg-hover: var(--_color-hover);
224
+ --_bg-active: var(--_color-active);
225
+ --_text: var(--_fg);
226
+ --_text-hover: var(--_fg-hover);
227
+ --_text-active: var(--_fg-active);
228
+ --_border: var(--_color);
229
+ --_border-hover: var(--_color-hover);
230
+ --_border-active: var(--_color-active);
231
+ }
232
+
233
+ /* Outline: transparent background with colored border and text */
234
+ .stuic-button[data-variant="outline"] {
235
+ --_bg: transparent;
236
+ --_bg-hover: color-mix(in srgb, var(--_color) 10%, transparent);
237
+ --_bg-active: color-mix(in srgb, var(--_color) 20%, transparent);
238
+ --_text: var(--_color);
239
+ --_text-hover: var(--_color-hover);
240
+ --_text-active: var(--_color-active);
241
+ --_border: var(--_color);
242
+ --_border-hover: var(--_color-hover);
243
+ --_border-active: var(--_color-active);
244
+ }
245
+
246
+ /* Ghost: transparent background, colored text, subtle hover state */
247
+ .stuic-button[data-variant="ghost"] {
248
+ --_bg: transparent;
249
+ --_bg-hover: color-mix(in srgb, var(--_color) 10%, transparent);
250
+ --_bg-active: color-mix(in srgb, var(--_color) 20%, transparent);
251
+ --_text: var(--_color);
252
+ --_text-hover: var(--_color-hover);
253
+ --_text-active: var(--_color-active);
254
+ --_border: transparent;
255
+ --_border-hover: transparent;
256
+ --_border-active: transparent;
257
+ }
258
+
259
+ /* Soft: muted/translucent background tint with colored text */
260
+ .stuic-button[data-variant="soft"] {
261
+ --_bg: color-mix(in srgb, var(--_color) 15%, transparent);
262
+ --_bg-hover: color-mix(in srgb, var(--_color) 25%, transparent);
263
+ --_bg-active: color-mix(in srgb, var(--_color) 35%, transparent);
264
+ --_text: var(--_color);
265
+ --_text-hover: var(--_color-hover);
266
+ --_text-active: var(--_color-active);
267
+ --_border: transparent;
268
+ --_border-hover: transparent;
269
+ --_border-active: transparent;
270
+ }
271
+
272
+ /* Link: appears as inline text link, minimal styling */
273
+ .stuic-button[data-variant="link"] {
274
+ --_bg: transparent;
275
+ --_bg-hover: transparent;
276
+ --_bg-active: transparent;
277
+ --_text: var(--_color);
278
+ --_text-hover: var(--_color-hover);
279
+ --_text-active: var(--_color-active);
280
+ --_border: transparent;
281
+ --_border-hover: transparent;
282
+ --_border-active: transparent;
283
+ text-decoration: underline;
284
+ text-underline-offset: 2px;
285
+ min-height: auto;
286
+ padding: 0;
287
+ position: relative;
288
+ }
289
+
290
+ .stuic-button[data-variant="link"]::before {
291
+ content: "";
292
+ position: absolute;
293
+ inset: -12px -8px; /* expands tap area */
294
+ }
295
+
296
+ /* ============================================================================
297
+ DEFAULT INTENT OVERRIDES
298
+ The default intent uses light surface colors - we need visible borders
299
+ for solid and visible text/borders for other variants.
300
+ ============================================================================ */
301
+
302
+ /* Default solid: add visible border */
303
+ .stuic-button:not([data-intent])[data-variant="solid"],
304
+ .stuic-button:not([data-intent]):not([data-variant]) {
305
+ --_border: var(--color-neutral-400);
306
+ --_border-hover: var(--color-neutral-500);
307
+ --_border-active: var(--color-neutral-500);
308
+ }
309
+
310
+ .stuic-button:not([data-intent])[data-variant="outline"] {
311
+ --_text: var(--color-neutral-800);
312
+ --_text-hover: var(--color-neutral-900);
313
+ --_text-active: var(--color-neutral-900);
314
+ --_border: var(--color-neutral-400);
315
+ --_border-hover: var(--color-neutral-400);
316
+ --_border-active: var(--color-neutral-500);
317
+ --_bg-hover: var(--color-neutral-100);
318
+ --_bg-active: var(--color-neutral-200);
319
+ }
320
+
321
+ .stuic-button:not([data-intent])[data-variant="ghost"] {
322
+ --_text: var(--color-neutral-800);
323
+ --_text-hover: var(--color-neutral-900);
324
+ --_text-active: var(--color-neutral-900);
325
+ --_bg-hover: var(--color-neutral-100);
326
+ --_bg-active: var(--color-neutral-200);
327
+ }
328
+
329
+ .stuic-button:not([data-intent])[data-variant="soft"] {
330
+ --_text: var(--color-neutral-800);
331
+ --_text-hover: var(--color-neutral-900);
332
+ --_text-active: var(--color-neutral-900);
333
+ --_bg: var(--color-neutral-100);
334
+ --_bg-hover: var(--color-neutral-200);
335
+ --_bg-active: var(--color-neutral-300);
336
+ }
337
+
338
+ .stuic-button:not([data-intent])[data-variant="link"] {
339
+ --_text: var(--color-neutral-700);
340
+ --_text-hover: var(--color-neutral-900);
341
+ --_text-active: var(--color-neutral-900);
342
+ }
5
343
 
6
- --color-button-text: var(--color-button-text, var(--color-black));
7
- --color-button-text-dark: var(--color-button-text-dark, var(--color-white));
344
+ /* ============================================================================
345
+ DEFAULT INTENT DARK MODE OVERRIDES
346
+ ============================================================================ */
347
+
348
+ /* Default (no intent) in dark mode: lighter neutral colors */
349
+ .dark .stuic-button:not([data-intent]) {
350
+ --_color: var(--color-neutral-700);
351
+ --_color-hover: var(--color-neutral-600);
352
+ --_color-active: var(--color-neutral-500);
353
+ --_fg: var(--color-neutral-100);
354
+ --_fg-hover: var(--color-neutral-100);
355
+ --_fg-active: var(--color-neutral-100);
356
+ }
357
+
358
+ /* Default solid in dark mode */
359
+ .dark .stuic-button:not([data-intent])[data-variant="solid"],
360
+ .dark .stuic-button:not([data-intent]):not([data-variant]) {
361
+ --_border: var(--color-neutral-500);
362
+ --_border-hover: var(--color-neutral-400);
363
+ --_border-active: var(--color-neutral-400);
364
+ }
365
+
366
+ /* Default outline in dark mode */
367
+ .dark .stuic-button:not([data-intent])[data-variant="outline"] {
368
+ --_text: var(--color-neutral-200);
369
+ --_text-hover: var(--color-neutral-100);
370
+ --_text-active: var(--color-neutral-100);
371
+ --_border: var(--color-neutral-500);
372
+ --_border-hover: var(--color-neutral-400);
373
+ --_border-active: var(--color-neutral-400);
374
+ --_bg-hover: var(--color-neutral-800);
375
+ --_bg-active: var(--color-neutral-700);
376
+ }
8
377
 
9
- --color-button-border: var(--color-button-border, var(--color-neutral-400));
10
- --color-button-border-dark: var(--color-button-border-dark, var(--color-neutral-600));
378
+ /* Default ghost in dark mode */
379
+ .dark .stuic-button:not([data-intent])[data-variant="ghost"] {
380
+ --_text: var(--color-neutral-200);
381
+ --_text-hover: var(--color-neutral-100);
382
+ --_text-active: var(--color-neutral-100);
383
+ --_bg-hover: var(--color-neutral-800);
384
+ --_bg-active: var(--color-neutral-700);
385
+ }
386
+
387
+ /* Default soft in dark mode */
388
+ .dark .stuic-button:not([data-intent])[data-variant="soft"] {
389
+ --_text: var(--color-neutral-200);
390
+ --_text-hover: var(--color-neutral-100);
391
+ --_text-active: var(--color-neutral-100);
392
+ --_bg: var(--color-neutral-800);
393
+ --_bg-hover: var(--color-neutral-700);
394
+ --_bg-active: var(--color-neutral-600);
395
+ }
396
+
397
+ /* Default link in dark mode */
398
+ .dark .stuic-button:not([data-intent])[data-variant="link"] {
399
+ --_text: var(--color-neutral-300);
400
+ --_text-hover: var(--color-neutral-100);
401
+ --_text-active: var(--color-neutral-100);
402
+ }
403
+
404
+ /* ============================================================================
405
+ MUTED MODIFIER
406
+ ============================================================================ */
407
+
408
+ .stuic-button[data-muted="true"] {
409
+ opacity: 0.7;
410
+ }
411
+
412
+ .stuic-button[data-muted="true"]:hover:not(:disabled) {
413
+ opacity: 0.85;
414
+ }
415
+
416
+ /* ============================================================================
417
+ RAISED (3D PUSH) EFFECT
418
+ Override globally: :root { --stuic-button-raised-offset: 4px; }
419
+ Override locally: <Button raised style="--stuic-button-raised-offset: 5px;">
420
+ ============================================================================ */
421
+
422
+ .stuic-button[data-raised] {
423
+ box-shadow: var(--stuic-button-raised-offset) var(--stuic-button-raised-offset) 0 0
424
+ var(--stuic-button-raised-color);
425
+ transition:
426
+ background var(--stuic-button-transition),
427
+ color var(--stuic-button-transition),
428
+ border-color var(--stuic-button-transition),
429
+ opacity var(--stuic-button-transition),
430
+ box-shadow var(--stuic-button-transition),
431
+ transform var(--stuic-button-transition);
432
+ }
433
+
434
+ .stuic-button[data-raised]:active:not(:disabled) {
435
+ box-shadow: none;
436
+ transform: translate(
437
+ var(--stuic-button-raised-offset),
438
+ var(--stuic-button-raised-offset)
439
+ );
440
+ }
441
+
442
+ /* ============================================================================
443
+ ROUNDED FULL
444
+ ============================================================================ */
445
+ .stuic-button[data-rounded-full] {
446
+ --stuic-button-radius: 9999px;
447
+ }
448
+
449
+ /* ============================================================================
450
+ SQUARED (1:1 aspect ratio)
451
+ ============================================================================ */
452
+ .stuic-button[data-aspect1] {
453
+ aspect-ratio: 1;
454
+ }
455
+
456
+ .stuic-button[data-aspect1][data-size="sm"] {
457
+ min-width: var(--stuic-button-min-height-sm);
458
+ min-height: var(--stuic-button-min-height-sm);
459
+ padding: var(--stuic-button-padding-y-sm);
460
+ }
461
+
462
+ .stuic-button[data-aspect1][data-size="md"] {
463
+ min-width: var(--stuic-button-min-height-md);
464
+ min-height: var(--stuic-button-min-height-md);
465
+ padding: var(--stuic-button-padding-y-md);
466
+ }
467
+
468
+ .stuic-button[data-aspect1][data-size="lg"] {
469
+ min-width: var(--stuic-button-min-height-lg);
470
+ min-height: var(--stuic-button-min-height-lg);
471
+ padding: var(--stuic-button-padding-y-lg);
472
+ }
11
473
 
12
- --color-button-border-focus: var(--color-button-border-focus, var(--color-neutral-500));
13
- --color-button-border-focus-dark: var(--color-button-border-focus-dark, var(--color-neutral-500));
474
+ .stuic-button[data-aspect1][data-size="xl"] {
475
+ min-width: var(--stuic-button-min-height-xl);
476
+ min-height: var(--stuic-button-min-height-xl);
477
+ padding: var(--stuic-button-padding-y-xl);
14
478
  }
@@ -1 +1 @@
1
- export { default as Button, type Props as ButtonProps } from "./Button.svelte";
1
+ export { default as Button, type Props as ButtonProps, type ButtonVariant, type ButtonSize, } from "./Button.svelte";
@@ -1 +1 @@
1
- export { default as Button } from "./Button.svelte";
1
+ export { default as Button, } from "./Button.svelte";
@@ -88,42 +88,11 @@
88
88
  });
89
89
  });
90
90
 
91
- const rounded = "rounded-md";
92
- //
93
- const CLS = `
94
- stuic-button-group
95
- ${rounded}
96
- w-full
97
- py-1.5 px-1.5 inline-block space-x-1
98
- bg-button-group-bg text-button-group-text
99
- dark:bg-button-group-bg-dark dark:text-button-group-text-dark
100
- border-1
101
- border-button-group-border dark:border-button-group-border-dark
102
- flex justify-between
103
-
104
- focus-within:border-button-group-accent focus-within:dark:border-button-group-accent-dark
105
- focus-within:ring-button-group-accent/20 focus-within:dark:ring-button-group-accent-dark/20
106
- focus-within:ring-4
107
- `;
108
-
109
- const CLS_BUTTON = `
110
- ${rounded}
111
- w-full inline-block
112
- bg-transparent text-button-group-text dark:text-button-group-text-dark
113
- hover:bg-transparent hover:text-button-group-text hover:dark:text-button-group-text-dark
114
- outline-none focus:outline-none
115
- `;
91
+ // Base class - structural styling handled by CSS
92
+ const CLS = `stuic-button-group`;
116
93
 
117
- // we need some active indication by default... use just something subtle here, in the wild
118
- // this will be styled with classButtonActive
119
- const CLS_BUTTON_ACTIVE = `
120
- shadow-none
121
- bg-button-group-bg-active dark:bg-button-group-bg-active-dark
122
- text-button-group-text-active dark:text-button-group-text-active-dark
123
- hover:bg-button-group-bg-active hover:dark:bg-button-group-bg-active
124
- hover:text-button-group-text-active hover:dark:text-button-group-text-active-dark
125
- `;
126
- // shadow-[0px_0px_1px_1px_rgba(0_0_0_/_.6)]
94
+ // Button class - styling handled by CSS via aria-checked attribute
95
+ const CLS_BUTTON = `stuic-button-group-button`;
127
96
 
128
97
  let els = $state<Record<number, HTMLButtonElement>>({});
129
98
 
@@ -138,6 +107,7 @@
138
107
  {#if coll.size}
139
108
  <div
140
109
  class={twMerge(CLS, classProp)}
110
+ data-size={size}
141
111
  {style}
142
112
  role="radiogroup"
143
113
  aria-labelledby={$coll?.active?.id || ""}
@@ -145,15 +115,14 @@
145
115
  >
146
116
  {#each coll.items as item, i}
147
117
  <Button
118
+ unstyled
148
119
  tabindex={$coll.activeIndex === i ? tabindex : -1}
149
120
  class={twMerge(
150
- "border-none shadow-none",
151
121
  CLS_BUTTON,
152
122
  classButton,
153
- $coll.activeIndex === i && [CLS_BUTTON_ACTIVE, classButtonActive].join(" ")
123
+ $coll.activeIndex === i && classButtonActive
154
124
  )}
155
125
  {disabled}
156
- {size}
157
126
  type="button"
158
127
  role="radio"
159
128
  aria-checked={$coll.activeIndex === i}
@@ -12,12 +12,13 @@ A radio button group styled as a segmented button toggle. Supports keyboard navi
12
12
  | `size` | `"sm" \| "md" \| "lg" \| string` | `"md"` | Button size |
13
13
  | `disabled` | `boolean` | `false` | Disable all buttons |
14
14
  | `tabindex` | `number` | `0` | Tab index for active button |
15
- | `class` | `string` | - | CSS for container |
16
- | `classButton` | `string` | - | CSS for all buttons |
17
- | `classButtonActive` | `string` | - | CSS for active button |
15
+ | `class` | `string` | - | CSS classes for container |
16
+ | `classButton` | `string` | - | CSS classes for all buttons |
17
+ | `classButtonActive` | `string` | - | CSS classes for active button |
18
18
  | `style` | `string` | - | Inline styles for container |
19
19
  | `onButtonClick` | `(index, coll) => Promise<boolean> \| boolean` | - | Async validation hook (return `false` to prevent) |
20
20
  | `buttonProps` | `(index, coll) => Record<string, any>` | - | Dynamic props per button |
21
+ | `tooltip` | `TooltipConfig` | - | Tooltip configuration |
21
22
 
22
23
  ## Option Format
23
24
 
@@ -38,7 +39,7 @@ A radio button group styled as a segmented button toggle. Supports keyboard navi
38
39
 
39
40
  ```svelte
40
41
  <script lang="ts">
41
- import { ButtonGroupRadio } from 'stuic';
42
+ import { ButtonGroupRadio } from '@marianmeres/stuic';
42
43
 
43
44
  let selected = $state('monthly');
44
45
  </script>
@@ -88,7 +89,84 @@ A radio button group styled as a segmented button toggle. Supports keyboard navi
88
89
  />
89
90
  ```
90
91
 
92
+ ### Custom Styling
93
+
94
+ ```svelte
95
+ <!-- Override component tokens inline -->
96
+ <ButtonGroupRadio
97
+ options={['One', 'Two', 'Three']}
98
+ style="--stuic-button-group-radius: 9999px;"
99
+ />
100
+
101
+ <!-- Override via Tailwind classes -->
102
+ <ButtonGroupRadio
103
+ options={['One', 'Two', 'Three']}
104
+ class="bg-slate-100 border-slate-300"
105
+ classButton="font-semibold"
106
+ classButtonActive="bg-indigo-600 text-white"
107
+ />
108
+ ```
109
+
91
110
  ## Keyboard Navigation
92
111
 
93
112
  - **Arrow Left/Up**: Select previous option
94
113
  - **Arrow Right/Down**: Select next option
114
+
115
+ ## CSS Variables
116
+
117
+ ### Component Tokens
118
+
119
+ | Variable | Default | Description |
120
+ |----------|---------|-------------|
121
+ | `--stuic-button-group-radius` | `var(--radius-md)` | Border radius for container and buttons |
122
+ | `--stuic-button-group-padding` | `0.375rem` | Container padding |
123
+ | `--stuic-button-group-gap` | `0.25rem` | Gap between buttons |
124
+ | `--stuic-button-group-border-width` | `1px` | Container border width |
125
+ | `--stuic-button-group-transition` | `150ms` | Transition duration |
126
+ | `--stuic-button-group-ring-width` | `3px` | Focus ring width |
127
+ | `--stuic-button-group-ring-color` | `var(--stuic-color-ring)` | Focus ring color |
128
+ | `--stuic-button-group-button-padding-x` | `0.75rem` | Button horizontal padding |
129
+ | `--stuic-button-group-button-padding-y` | `0.5rem` | Button vertical padding |
130
+ | `--stuic-button-group-button-min-height` | `2.75rem` | Button min height (44px touch target) |
131
+
132
+ ### Color Tokens
133
+
134
+ | Variable | Default | Description |
135
+ |----------|---------|-------------|
136
+ | `--stuic-button-group-bg` | `var(--stuic-color-surface)` | Container background |
137
+ | `--stuic-button-group-text` | `var(--stuic-color-foreground)` | Container text color |
138
+ | `--stuic-button-group-border` | `var(--stuic-color-border)` | Container border color |
139
+ | `--stuic-button-group-button-bg` | `transparent` | Inactive button background |
140
+ | `--stuic-button-group-button-text` | `var(--stuic-color-foreground)` | Inactive button text |
141
+ | `--stuic-button-group-button-bg-hover` | `var(--stuic-color-muted)` | Inactive button hover background |
142
+ | `--stuic-button-group-button-bg-active` | `var(--stuic-color-primary)` | Active button background |
143
+ | `--stuic-button-group-button-text-active` | `var(--stuic-color-primary-foreground)` | Active button text |
144
+ | `--stuic-button-group-button-bg-active-hover` | `var(--stuic-color-primary-hover)` | Active button hover background |
145
+
146
+ ### Customization Examples
147
+
148
+ ```css
149
+ /* Global override */
150
+ :root {
151
+ --stuic-button-group-radius: 0;
152
+ --stuic-button-group-button-bg-active: var(--stuic-color-accent);
153
+ --stuic-button-group-button-text-active: var(--stuic-color-accent-foreground);
154
+ }
155
+ ```
156
+
157
+ ```svelte
158
+ <!-- Local override via style prop -->
159
+ <ButtonGroupRadio
160
+ options={['A', 'B', 'C']}
161
+ style="--stuic-button-group-radius: 9999px; --stuic-button-group-button-bg-active: #10b981;"
162
+ />
163
+ ```
164
+
165
+ ## Data Attributes
166
+
167
+ The container uses:
168
+ - `data-size` - The size value (`sm`, `md`, `lg`)
169
+
170
+ The inner buttons use:
171
+ - `aria-checked` - `true` when selected (used for active state styling)
172
+ - `role="radio"` - Accessibility role