@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
@@ -39,7 +39,6 @@
39
39
  import { twMerge } from "../../utils/tw-merge.js";
40
40
  import Button from "../Button/Button.svelte";
41
41
  //
42
- import "./index.css";
43
42
  import { tooltip, type TooltipConfig } from "../../actions/index.js";
44
43
 
45
44
  let {
@@ -88,42 +87,11 @@
88
87
  });
89
88
  });
90
89
 
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
- `;
90
+ // Base class - structural styling handled by CSS
91
+ const CLS = `stuic-button-group`;
116
92
 
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)]
93
+ // Button class - styling handled by CSS via aria-checked attribute
94
+ const CLS_BUTTON = `stuic-button-group-button`;
127
95
 
128
96
  let els = $state<Record<number, HTMLButtonElement>>({});
129
97
 
@@ -138,6 +106,7 @@
138
106
  {#if coll.size}
139
107
  <div
140
108
  class={twMerge(CLS, classProp)}
109
+ data-size={size}
141
110
  {style}
142
111
  role="radiogroup"
143
112
  aria-labelledby={$coll?.active?.id || ""}
@@ -145,15 +114,14 @@
145
114
  >
146
115
  {#each coll.items as item, i}
147
116
  <Button
117
+ unstyled
148
118
  tabindex={$coll.activeIndex === i ? tabindex : -1}
149
119
  class={twMerge(
150
- "border-none shadow-none",
151
120
  CLS_BUTTON,
152
121
  classButton,
153
- $coll.activeIndex === i && [CLS_BUTTON_ACTIVE, classButtonActive].join(" ")
122
+ $coll.activeIndex === i && classButtonActive
154
123
  )}
155
124
  {disabled}
156
- {size}
157
125
  type="button"
158
126
  role="radio"
159
127
  aria-checked={$coll.activeIndex === i}
@@ -22,7 +22,6 @@ export interface Props {
22
22
  buttonProps?: (index: number, coll: ItemColl) => undefined | Record<string, any>;
23
23
  tooltip?: TooltipConfig;
24
24
  }
25
- import "./index.css";
26
25
  import { type TooltipConfig } from "../../actions/index.js";
27
26
  declare const ButtonGroupRadio: import("svelte").Component<Props, {}, "value" | "activeIndex">;
28
27
  type ButtonGroupRadio = ReturnType<typeof ButtonGroupRadio>;
@@ -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
@@ -1,20 +1,164 @@
1
- /* prettier-ignore */
2
- @theme inline {
3
- --color-button-group-bg: var(--color-button-group-bg, var(--color-white));
4
- --color-button-group-bg-dark: var(--color-button-group-bg-dark, var(--color-neutral-600));
1
+ /* ============================================================================
2
+ BUTTON GROUP RADIO COMPONENT TOKENS
3
+ Override globally: :root { --stuic-button-group-radius: 0; }
4
+ Override locally: <ButtonGroupRadio style="--stuic-button-group-radius: 9999px;">
5
+ ============================================================================ */
5
6
 
6
- --color-button-group-text: var(--color-button-group-text, var(--color-black));
7
- --color-button-group-text-dark: var(--color-button-group-text-dark, var(--color-neutral-300));
7
+ :root {
8
+ /* Structure - reference Tailwind vars where applicable */
9
+ --stuic-button-group-radius: var(--radius-md);
10
+ --stuic-button-group-padding: 0.375rem;
11
+ --stuic-button-group-gap: 0.25rem;
12
+ --stuic-button-group-border-width: 1px;
13
+ --stuic-button-group-transition: 150ms;
8
14
 
9
- --color-button-group-border: var(--color-button-group-border, var(--color-neutral-300));
10
- --color-button-group-border-dark: var(--color-button-group-border-dark, var(--color-neutral-800));
15
+ /* Button sizing - 44px is Apple HIG minimum touch target */
16
+ --stuic-button-group-button-padding-x: 0.75rem;
17
+ --stuic-button-group-button-padding-y: 0.5rem;
18
+ --stuic-button-group-button-min-height: 2.75rem; /* 44px */
11
19
 
12
- --color-button-group-accent: var(--color-button-group-accent, var(--color-red-600));
13
- --color-button-group-accent-dark: var(--color-button-group-accent-dark, var(--color-red-400));
20
+ /* Focus ring */
21
+ --stuic-button-group-ring-width: 3px;
22
+ --stuic-button-group-ring-color: var(--stuic-color-ring);
14
23
 
15
- --color-button-group-bg-active: var(--color-button-group-bg-active, var(--color-neutral-500));
16
- --color-button-group-bg-active-dark: var(--color-button-group-bg-active-dark, var(--color-neutral-500));
24
+ /* Container colors - theme vars */
25
+ --stuic-button-group-bg: var(--stuic-color-surface);
26
+ --stuic-button-group-text: var(--stuic-color-foreground);
27
+ --stuic-button-group-border: var(--stuic-color-border);
17
28
 
18
- --color-button-group-text-active: var(--color-button-group-text-active, var(--color-white));
19
- --color-button-group-text-active-dark: var(--color-button-group-text-active-dark, var(--color-white));
29
+ /* Button colors (inactive) */
30
+ --stuic-button-group-button-bg: transparent;
31
+ --stuic-button-group-button-text: var(--stuic-color-foreground);
32
+ --stuic-button-group-button-bg-hover: var(--stuic-color-muted);
33
+ --stuic-button-group-button-text-hover: var(--stuic-color-foreground);
34
+
35
+ /* Button colors (active) */
36
+ --stuic-button-group-button-bg-active: var(--stuic-color-primary);
37
+ --stuic-button-group-button-text-active: var(--stuic-color-primary-foreground);
38
+ --stuic-button-group-button-bg-active-hover: var(--stuic-color-primary-hover);
39
+ --stuic-button-group-button-text-active-hover: var(
40
+ --stuic-color-primary-foreground-hover
41
+ );
42
+ }
43
+
44
+ @layer components {
45
+ /* ============================================================================
46
+ BASE STYLES
47
+ ============================================================================ */
48
+
49
+ .stuic-button-group {
50
+ /* Layout */
51
+ display: inline-flex;
52
+ align-items: center;
53
+ justify-content: space-between;
54
+ gap: var(--stuic-button-group-gap);
55
+ width: 100%;
56
+
57
+ /* Box model */
58
+ padding: var(--stuic-button-group-padding);
59
+ border-width: var(--stuic-button-group-border-width);
60
+ border-style: solid;
61
+ border-radius: var(--stuic-button-group-radius);
62
+
63
+ /* Colors */
64
+ background: var(--stuic-button-group-bg);
65
+ color: var(--stuic-button-group-text);
66
+ border-color: var(--stuic-button-group-border);
67
+ }
68
+
69
+ /* Focus-within state */
70
+ .stuic-button-group:focus-within {
71
+ outline: var(--stuic-button-group-ring-width) solid
72
+ var(--stuic-button-group-ring-color);
73
+ outline-offset: 0;
74
+ }
75
+
76
+ /* ============================================================================
77
+ SIZE VARIANTS
78
+ ============================================================================ */
79
+
80
+ .stuic-button-group[data-size="sm"] {
81
+ --stuic-button-group-padding: 0.25rem;
82
+ --stuic-button-group-gap: 0.125rem;
83
+ --stuic-button-group-button-padding-x: 0.5rem;
84
+ --stuic-button-group-button-padding-y: 0.375rem;
85
+ --stuic-button-group-button-min-height: 2.25rem; /* 36px - still touch-friendly */
86
+ }
87
+
88
+ .stuic-button-group[data-size="lg"] {
89
+ --stuic-button-group-padding: 0.5rem;
90
+ --stuic-button-group-gap: 0.375rem;
91
+ --stuic-button-group-button-padding-x: 1rem;
92
+ --stuic-button-group-button-padding-y: 0.625rem;
93
+ --stuic-button-group-button-min-height: 3rem; /* 48px */
94
+ }
95
+
96
+ /* ============================================================================
97
+ BUTTON STYLES
98
+ ============================================================================ */
99
+
100
+ .stuic-button-group-button {
101
+ /* Layout */
102
+ display: inline-flex;
103
+ align-items: center;
104
+ justify-content: center;
105
+ flex: 1;
106
+
107
+ /* Box model - mobile-friendly touch targets */
108
+ padding: var(--stuic-button-group-button-padding-y)
109
+ var(--stuic-button-group-button-padding-x);
110
+ min-height: var(--stuic-button-group-button-min-height);
111
+ border: none;
112
+ border-radius: var(--stuic-button-group-radius);
113
+
114
+ /* Typography */
115
+ white-space: nowrap;
116
+ line-height: 1;
117
+ text-align: center;
118
+
119
+ /* Colors */
120
+ background: var(--stuic-button-group-button-bg);
121
+ color: var(--stuic-button-group-button-text);
122
+
123
+ /* Interaction - mobile-friendly */
124
+ cursor: pointer;
125
+ user-select: none;
126
+ -webkit-tap-highlight-color: transparent;
127
+ touch-action: manipulation;
128
+
129
+ /* Transition */
130
+ transition:
131
+ background var(--stuic-button-group-transition),
132
+ color var(--stuic-button-group-transition);
133
+
134
+ /* Focus */
135
+ outline: none;
136
+ }
137
+
138
+ .stuic-button-group-button:hover:not(:disabled):not([aria-checked="true"]) {
139
+ background: var(--stuic-button-group-button-bg-hover);
140
+ color: var(--stuic-button-group-button-text-hover);
141
+ }
142
+
143
+ .stuic-button-group-button:focus-visible {
144
+ outline: none; /* Focus handled by container */
145
+ }
146
+
147
+ /* Active/selected button */
148
+ .stuic-button-group-button[aria-checked="true"] {
149
+ background: var(--stuic-button-group-button-bg-active);
150
+ color: var(--stuic-button-group-button-text-active);
151
+ box-shadow: none;
152
+ }
153
+
154
+ .stuic-button-group-button[aria-checked="true"]:hover:not(:disabled) {
155
+ background: var(--stuic-button-group-button-bg-active-hover);
156
+ color: var(--stuic-button-group-button-text-active-hover);
157
+ }
158
+
159
+ /* Disabled state */
160
+ .stuic-button-group-button:disabled {
161
+ opacity: 0.5;
162
+ cursor: not-allowed;
163
+ }
20
164
  }
@@ -23,8 +23,8 @@
23
23
  classContent?: string;
24
24
  /** Toggle button class */
25
25
  classToggle?: string;
26
- /** Opacity class for toggle button (default: "opacity-70") */
27
- toggleOpacity?: string;
26
+ /** Inline styles (for CSS variable overrides) */
27
+ style?: string;
28
28
  /** Bind reference to container element */
29
29
  el?: HTMLDivElement;
30
30
  /** Optional translate function */
@@ -39,8 +39,8 @@
39
39
  i18nSpanWrap: boolean = true
40
40
  ) {
41
41
  const m: Record<string, string> = {
42
- more: "Show more...",
43
- less: "Show less...",
42
+ more: "More...",
43
+ less: "Less...",
44
44
  };
45
45
  let out = m[k] ?? fallback ?? k;
46
46
 
@@ -58,7 +58,7 @@
58
58
  class: classProp,
59
59
  classContent,
60
60
  classToggle,
61
- toggleOpacity = "opacity-75",
61
+ style,
62
62
  el = $bindable(),
63
63
  t = t_default,
64
64
  }: Props = $props();
@@ -86,6 +86,7 @@
86
86
  bind:this={el}
87
87
  bind:clientWidth={containerWidth}
88
88
  class={twMerge("stuic-collapsible", classProp)}
89
+ {style}
89
90
  >
90
91
  <div class="flex items-end">
91
92
  <div
@@ -98,8 +99,7 @@
98
99
  <button
99
100
  type="button"
100
101
  class={twMerge(
101
- toggleOpacity,
102
- "hover:opacity-100 cursor-pointer px-2 py-1 -my-1 -mr-2",
102
+ "stuic-collapsible-toggle cursor-pointer -my-1 -mr-2",
103
103
  classToggle
104
104
  )}
105
105
  onclick={() => (expanded = !expanded)}
@@ -17,8 +17,8 @@ export interface Props {
17
17
  classContent?: string;
18
18
  /** Toggle button class */
19
19
  classToggle?: string;
20
- /** Opacity class for toggle button (default: "opacity-70") */
21
- toggleOpacity?: string;
20
+ /** Inline styles (for CSS variable overrides) */
21
+ style?: string;
22
22
  /** Bind reference to container element */
23
23
  el?: HTMLDivElement;
24
24
  /** Optional translate function */
@@ -14,8 +14,9 @@ A component that truncates content to a specified number of lines with an expand
14
14
  | `class` | `string` | - | Container element class |
15
15
  | `classContent` | `string` | - | Content wrapper class |
16
16
  | `classToggle` | `string` | - | Toggle button class |
17
- | `toggleOpacity` | `string` | `"opacity-70"`| Opacity class for toggle button |
17
+ | `style` | `string` | - | Inline styles (for CSS variable overrides) |
18
18
  | `el` | `HTMLDivElement`| - | Bind reference to container element |
19
+ | `t` | `TranslateFn` | - | Optional translate function |
19
20
 
20
21
  ## Usage
21
22
 
@@ -74,8 +75,39 @@ A component that truncates content to a specified number of lines with an expand
74
75
  class="bg-gray-100 p-4 rounded"
75
76
  classContent="text-sm text-gray-600"
76
77
  classToggle="text-blue-500 font-bold"
77
- toggleOpacity="opacity-100"
78
78
  >
79
79
  Styled collapsible content.
80
80
  </Collapsible>
81
81
  ```
82
+
83
+ ### CSS Variable Overrides
84
+
85
+ ```svelte
86
+ <!-- Local override via inline style -->
87
+ <Collapsible style="--stuic-collapsible-toggle-opacity: 1;">
88
+ Always fully visible toggle button.
89
+ </Collapsible>
90
+ ```
91
+
92
+ ## CSS Variables
93
+
94
+ Override to customize appearance:
95
+
96
+ | Variable | Default | Description |
97
+ |----------|---------|-------------|
98
+ | `--stuic-collapsible-toggle-opacity` | `0.7` | Toggle button opacity |
99
+ | `--stuic-collapsible-toggle-opacity-hover` | `1` | Hover opacity |
100
+ | `--stuic-collapsible-toggle-padding-x` | `calc(var(--spacing) * 2)` | Horizontal padding |
101
+ | `--stuic-collapsible-toggle-padding-y` | `0.25rem` | Vertical padding |
102
+ | `--stuic-collapsible-transition` | `150ms` | Transition duration |
103
+ | `--stuic-collapsible-ring-width` | `2px` | Focus ring width |
104
+ | `--stuic-collapsible-ring-color` | `--stuic-color-ring` | Focus ring color |
105
+
106
+ ### Global Override
107
+
108
+ ```css
109
+ :root {
110
+ --stuic-collapsible-toggle-opacity: 0.5;
111
+ --stuic-collapsible-toggle-opacity-hover: 0.8;
112
+ }
113
+ ```
@@ -0,0 +1,40 @@
1
+ /* ============================================================================
2
+ COLLAPSIBLE COMPONENT TOKENS
3
+ Override globally: :root { --stuic-collapsible-toggle-opacity: 0.5; }
4
+ Override locally: <Collapsible style="--stuic-collapsible-toggle-opacity: 1;">
5
+ ============================================================================ */
6
+
7
+ :root {
8
+ /* Toggle button styling */
9
+ --stuic-collapsible-toggle-opacity: 0.7;
10
+ --stuic-collapsible-toggle-opacity-hover: 1;
11
+ --stuic-collapsible-toggle-padding-x: calc(var(--spacing) * 2);
12
+ --stuic-collapsible-toggle-padding-y: calc(var(--spacing) * 1);
13
+ --stuic-collapsible-transition: 150ms;
14
+
15
+ /* Focus ring (uses theme token) */
16
+ --stuic-collapsible-ring-width: 2px;
17
+ --stuic-collapsible-ring-color: var(--stuic-color-ring);
18
+ }
19
+
20
+ @layer components {
21
+ /* ============================================================================
22
+ TOGGLE BUTTON STYLES
23
+ ============================================================================ */
24
+
25
+ .stuic-collapsible-toggle {
26
+ opacity: var(--stuic-collapsible-toggle-opacity);
27
+ padding: var(--stuic-collapsible-toggle-padding-y)
28
+ var(--stuic-collapsible-toggle-padding-x);
29
+ transition: opacity var(--stuic-collapsible-transition);
30
+ }
31
+
32
+ .stuic-collapsible-toggle:hover {
33
+ opacity: var(--stuic-collapsible-toggle-opacity-hover);
34
+ }
35
+
36
+ .stuic-collapsible-toggle:focus-visible {
37
+ outline: var(--stuic-collapsible-ring-width) solid var(--stuic-collapsible-ring-color);
38
+ outline-offset: 2px;
39
+ }
40
+ }
@@ -62,7 +62,7 @@
62
62
  </script>
63
63
 
64
64
  <script lang="ts">
65
- import "./index.css";
65
+ import Button from "../Button/Button.svelte";
66
66
 
67
67
  const clog = createClog("CommandMenu");
68
68
 
@@ -223,7 +223,7 @@
223
223
  <ModalDialog
224
224
  bind:this={modalDialog}
225
225
  classDialog="items-start"
226
- class="w-full max-w-3xl bg-transparent pointer-events-none"
226
+ class="w-full max-w-3xl bg-transparent! shadow-none! pointer-events-none"
227
227
  {noScrollLock}
228
228
  >
229
229
  <div class="pt-0 md:pt-[20vh] w-full">
@@ -251,8 +251,8 @@
251
251
  placeholder={searchPlaceholder ?? t("search_placeholder")}
252
252
  classInputBoxWrap={twMerge(
253
253
  // always look like focused
254
- `border-primary border-input-accent dark:border-input-accent-dark`,
255
- `ring-input-accent/20 dark:ring-input-accent-dark/20 ring-4`
254
+ `border-primary border-(--stuic-input-accent)`,
255
+ `ring-(--stuic-input-accent)/20 ring-4`
256
256
  )}
257
257
  onkeydown={(e) => {
258
258
  if (e.key === "Enter") {
@@ -262,25 +262,26 @@
262
262
  }}
263
263
  >
264
264
  {#snippet inputBefore()}
265
- <div class="flex flex-col items-center justify-center pl-3 opacity-75">
265
+ <div
266
+ class="flex flex-col items-center justify-center pl-3 stuic-command-menu-muted"
267
+ >
266
268
  {@html iconSearch({ size: 19, strokeWidth: 3 })}
267
269
  </div>
268
270
  {/snippet}
269
271
  {#snippet inputAfter()}
270
- <div class="flex pl-2 items-center justify-center opacity-50">
272
+ <div
273
+ class="flex pl-2 items-center justify-center stuic-command-menu-placeholder"
274
+ >
271
275
  {#if isFetching}
272
276
  <Spinner class="w-4" />
273
277
  {/if}
274
278
  </div>
275
279
  <div class="flex items-center justify-center">
276
- <button
280
+ <Button
281
+ x
282
+ variant="ghost"
283
+ roundedFull
277
284
  type="button"
278
- class={twMerge(
279
- "rounded m-1 opacity-75",
280
- "hover:opacity-100 hover:bg-neutral-200 dark:hover:bg-neutral-800",
281
- "focus-visible:opacity-100 focus-visible:outline-0",
282
- "focus-visible:bg-neutral-200 dark:focus-visible:bg-neutral-800"
283
- )}
284
285
  onclick={(e) => {
285
286
  e.preventDefault();
286
287
  if (!`${q || ""}`.trim()) {
@@ -289,9 +290,7 @@
289
290
  q = "";
290
291
  input?.focus();
291
292
  }}
292
- >
293
- <X class="m-2 size-6" />
294
- </button>
293
+ />
295
294
  </div>
296
295
  {/snippet}
297
296
  {#snippet inputBelow()}
@@ -304,10 +303,9 @@
304
303
  <div
305
304
  class={twMerge(
306
305
  "stuic-command-menu-options",
307
- "options block space-y-1 p-1",
306
+ "block space-y-1 p-1",
308
307
  "overflow-y-auto overflow-x-hidden mb-1",
309
- "border-t border-black/20",
310
- "max-h-60"
308
+ "border-t"
311
309
  )}
312
310
  bind:this={optionsBox}
313
311
  tabindex="-1"
@@ -320,10 +318,7 @@
320
318
  <div class="p-1">
321
319
  {#if _optgroup}
322
320
  <div
323
- class={[
324
- "mb-1 p-1 text-xs font-semibold uppercase tracking-wide",
325
- "text-neutral-500 dark:text-neutral-400",
326
- ]}
321
+ class="stuic-command-menu-group-header mb-1 p-1 font-semibold uppercase tracking-wide"
327
322
  >
328
323
  {_optgroup}
329
324
  </div>
@@ -369,9 +364,6 @@
369
364
  </ModalDialog>
370
365
 
371
366
  <style>
372
- .options {
373
- scrollbar-width: thin;
374
- }
375
367
  .sr-only {
376
368
  position: absolute;
377
369
  width: 1px;
@@ -17,7 +17,6 @@ export interface Props {
17
17
  classOptionActive?: string;
18
18
  showAllOnEmptyQ?: boolean;
19
19
  }
20
- import "./index.css";
21
20
  declare const CommandMenu: import("svelte").Component<Props, {
22
21
  close: () => void;
23
22
  open: (openerOrEvent?: null | HTMLElement | MouseEvent) => void;