@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
@@ -44,14 +44,11 @@
44
44
  let { class: classProp, metas, keys, forcedOs }: Props = $props();
45
45
 
46
46
  function wrap(s: string, mac: boolean) {
47
- const cls = ["pr-0.5 font-mono tracking-tight"];
48
47
  const sym = mac ? macSymbol[s as KnownMeta] : otherSymbol[s as KnownMeta];
49
48
  if (sym) {
50
- s = sym;
51
- cls.push("font-sans pr-0");
49
+ return `<span class="stuic-kbd-symbol">${sym}</span>`;
52
50
  }
53
- const _class = twMerge(...cls);
54
- return `<span class="${_class}">${ucfirst(s)}</span>`;
51
+ return `<span class="stuic-kbd-key">${ucfirst(s)}</span>`;
55
52
  }
56
53
 
57
54
  async function get_meta_key() {
@@ -73,13 +70,8 @@
73
70
  }
74
71
  </script>
75
72
 
76
- <kbd
77
- class={twMerge(
78
- "text-sm leading-0 space-x-0.5 rounded px-1 py-0 outline outline-neutral-400",
79
- classProp
80
- )}
81
- >
73
+ <kbd class={twMerge("stuic-kbd", classProp)}>
82
74
  {#await get_meta_key() then metaKey}
83
- <span class="font-sans">{@html metaKey}</span><span>{keys}</span>
75
+ {@html metaKey}<span class="stuic-kbd-key">{keys}</span>
84
76
  {/await}
85
77
  </kbd>
@@ -79,3 +79,37 @@ Display keyboard shortcuts with platform-aware modifier key symbols. Automatical
79
79
  class="bg-gray-100 px-2 py-1"
80
80
  />
81
81
  ```
82
+
83
+ ## CSS Variables
84
+
85
+ Override to customize appearance:
86
+
87
+ | Variable | Default | Description |
88
+ |----------|---------|-------------|
89
+ | `--stuic-kbd-font-family` | `var(--font-mono)` | Font family |
90
+ | `--stuic-kbd-font-size` | `var(--text-sm)` | Font size |
91
+ | `--stuic-kbd-padding-x` | `calc(var(--spacing) * 1)` | Horizontal padding |
92
+ | `--stuic-kbd-padding-y` | `0` | Vertical padding |
93
+ | `--stuic-kbd-gap` | `calc(var(--spacing) * 0.5)` | Gap between elements |
94
+ | `--stuic-kbd-radius` | `var(--radius-sm)` | Border radius |
95
+ | `--stuic-kbd-border-width` | `1px` | Border width |
96
+ | `--stuic-kbd-border-color` | `var(--stuic-color-border)` | Border color (themeable) |
97
+
98
+ ### Global Override
99
+
100
+ ```css
101
+ :root {
102
+ --stuic-kbd-border-color: var(--color-blue-500);
103
+ --stuic-kbd-radius: var(--radius-lg);
104
+ }
105
+ ```
106
+
107
+ ### Local Override
108
+
109
+ ```svelte
110
+ <KbdShortcut
111
+ metas={['cmd']}
112
+ keys="K"
113
+ style="--stuic-kbd-border-color: blue; --stuic-kbd-radius: 0;"
114
+ />
115
+ ```
@@ -0,0 +1,55 @@
1
+ /* ============================================================================
2
+ KBD SHORTCUT COMPONENT TOKENS
3
+ Override globally: :root { --stuic-kbd-border-color: blue; }
4
+ Override locally: <KbdShortcut style="--stuic-kbd-radius: 0;">
5
+ ============================================================================ */
6
+
7
+ :root {
8
+ /* Typography */
9
+ --stuic-kbd-font-family: var(--font-sans);
10
+ --stuic-kbd-font-size: var(--text-sm);
11
+
12
+ /* Spacing */
13
+ --stuic-kbd-padding-x: calc(var(--spacing) * 1);
14
+ --stuic-kbd-padding-y: calc(var(--spacing) * 1);
15
+ --stuic-kbd-gap: calc(var(--spacing) * 0.5);
16
+
17
+ /* Border */
18
+ --stuic-kbd-radius: var(--radius-sm);
19
+ --stuic-kbd-border-width: 1px;
20
+ --stuic-kbd-border-color: var(--stuic-color-border);
21
+ }
22
+
23
+ @layer components {
24
+ /* ============================================================================
25
+ BASE STYLES
26
+ ============================================================================ */
27
+
28
+ .stuic-kbd {
29
+ /* Layout */
30
+ display: inline-flex;
31
+ align-items: center;
32
+ gap: var(--stuic-kbd-gap);
33
+
34
+ /* Typography */
35
+ font-family: var(--stuic-kbd-font-family);
36
+ font-size: var(--stuic-kbd-font-size);
37
+ line-height: 1;
38
+
39
+ /* Box model */
40
+ padding: var(--stuic-kbd-padding-y) var(--stuic-kbd-padding-x);
41
+ border-radius: var(--stuic-kbd-radius);
42
+ border: var(--stuic-kbd-border-width) solid var(--stuic-kbd-border-color);
43
+ }
44
+
45
+ /* Symbol styling (⌘, ⇧, ⌥, ⌃, ⊞) */
46
+ .stuic-kbd-symbol {
47
+ font-family: var(--font-sans);
48
+ }
49
+
50
+ /* Text key styling (Ctrl, Alt, etc.) */
51
+ .stuic-kbd-key {
52
+ font-family: var(--font-mono);
53
+ letter-spacing: var(--tracking-tight);
54
+ }
55
+ }
@@ -10,7 +10,7 @@
10
10
  active?: boolean;
11
11
  /** Whether this item is currently focused via keyboard navigation */
12
12
  focused?: boolean;
13
- /** Size preset affecting padding and min-height (sm, md, lg) */
13
+ /** Size preset affecting padding and min-height (sm, md, lg) or custom Tailwind classes */
14
14
  size?: "sm" | "md" | "lg" | string;
15
15
  /** Skip all default styling, use only custom classes */
16
16
  unstyled?: boolean;
@@ -35,64 +35,12 @@
35
35
  /** Bindable element reference */
36
36
  el?: HTMLButtonElement | HTMLAnchorElement;
37
37
  }
38
-
39
- export interface ListItemButtonPresetClasses {
40
- size: Record<string, string>;
41
- touchFriendly: string;
42
- }
43
-
44
- export const LIST_ITEM_BUTTON_STUIC_BASE_CLASSES = `
45
- w-full
46
- flex items-center gap-2
47
- text-left
48
- rounded-[var(--lib-radius)]
49
- cursor-pointer
50
- touch-action-manipulation
51
-
52
- bg-lib-bg dark:bg-lib-bg-dark
53
- text-lib-text dark:text-lib-text-dark
54
-
55
- border border-lib-border dark:border-lib-border-dark
56
-
57
- hover:bg-lib-hover-bg dark:hover:bg-lib-hover-bg-dark
58
- hover:text-lib-hover-text dark:hover:text-lib-hover-text-dark
59
- hover:border-lib-hover-border dark:hover:border-lib-hover-border-dark
60
-
61
- focus:outline-none
62
- focus-visible:bg-lib-focus-bg dark:focus-visible:bg-lib-focus-bg-dark
63
- focus-visible:text-lib-focus-text dark:focus-visible:text-lib-focus-text-dark
64
- focus-visible:border-lib-focus-border dark:focus-visible:border-lib-focus-border-dark
65
-
66
- disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none
67
- `;
68
-
69
- export const LIST_ITEM_BUTTON_STUIC_PRESET_CLASSES: ListItemButtonPresetClasses = {
70
- size: {
71
- sm: `px-2 py-1.5 text-sm min-h-[36px]`,
72
- md: `px-2.5 py-2 text-base min-h-[40px]`,
73
- lg: `px-3 py-2.5 text-base min-h-[44px]`,
74
- },
75
- touchFriendly: `min-h-[44px] py-2.5`,
76
- };
77
-
78
- export const LIST_ITEM_BUTTON_ACTIVE_CLASSES = `
79
- bg-lib-active-bg dark:bg-lib-active-bg-dark
80
- text-lib-active-text dark:text-lib-active-text-dark
81
- border-lib-active-border dark:border-lib-active-border-dark
82
- `;
83
-
84
- export const LIST_ITEM_BUTTON_FOCUSED_CLASSES = `
85
- bg-lib-focus-bg dark:bg-lib-focus-bg-dark
86
- text-lib-focus-text dark:text-lib-focus-text-dark
87
- border-lib-focus-border dark:border-lib-focus-border-dark
88
- `;
89
38
  </script>
90
39
 
91
40
  <script lang="ts">
92
41
  import { twMerge } from "../../utils/tw-merge.js";
93
42
  import { DevicePointer } from "../../utils/device-pointer.svelte.js";
94
43
  import Thc from "../Thc/Thc.svelte";
95
- import "./index.css";
96
44
 
97
45
  let {
98
46
  children,
@@ -115,31 +63,28 @@
115
63
 
116
64
  const devicePointer = new DevicePointer();
117
65
 
118
- const _base = LIST_ITEM_BUTTON_STUIC_BASE_CLASSES;
119
- const _preset = LIST_ITEM_BUTTON_STUIC_PRESET_CLASSES;
66
+ // Check if size is a known preset
67
+ const isPresetSize = (s: string): s is "sm" | "md" | "lg" =>
68
+ ["sm", "md", "lg"].includes(s);
120
69
 
121
- let _touchClasses = $derived.by(() => {
122
- if (touchFriendly === true) return _preset.touchFriendly;
123
- if (touchFriendly === "auto" && devicePointer.isCoarse) return _preset.touchFriendly;
124
- return "";
70
+ // Compute whether touch-friendly should be active
71
+ let isTouchFriendly = $derived.by(() => {
72
+ if (touchFriendly === true) return true;
73
+ if (touchFriendly === "auto" && devicePointer.isCoarse) return true;
74
+ return false;
125
75
  });
126
76
 
77
+ // Build class string - styling is handled by CSS + data attributes
127
78
  let _class = $derived(
128
- [
129
- "stuic-list-item-button",
130
- size,
131
- active && "active",
132
- focused && "focused",
133
- !unstyled && _base,
134
- !unstyled && size && _preset.size[size],
135
- !unstyled && _touchClasses,
136
- !unstyled && active && LIST_ITEM_BUTTON_ACTIVE_CLASSES,
137
- !unstyled && focused && !active && LIST_ITEM_BUTTON_FOCUSED_CLASSES,
79
+ twMerge(
80
+ !unstyled && "stuic-list-item-button",
81
+ // Custom size classes when not using preset
82
+ !unstyled && !isPresetSize(size) && size,
83
+ // User-provided state classes
138
84
  active && classActive,
139
85
  focused && !active && classFocused,
140
- ]
141
- .filter(Boolean)
142
- .join(" ")
86
+ classProp
87
+ )
143
88
  );
144
89
  </script>
145
90
 
@@ -160,11 +105,29 @@
160
105
  {/snippet}
161
106
 
162
107
  {#if href}
163
- <a {href} bind:this={el} class={twMerge(_class, classProp)} {...rest as any}>
108
+ <a
109
+ {href}
110
+ bind:this={el}
111
+ class={_class}
112
+ data-size={!unstyled && isPresetSize(size) ? size : undefined}
113
+ data-active={!unstyled && active ? "" : undefined}
114
+ data-focused={!unstyled && focused ? "" : undefined}
115
+ data-touch-friendly={!unstyled && isTouchFriendly ? "" : undefined}
116
+ {...rest as any}
117
+ >
164
118
  {@render content()}
165
119
  </a>
166
120
  {:else}
167
- <button bind:this={el} class={twMerge(_class, classProp)} type="button" {...rest}>
121
+ <button
122
+ bind:this={el}
123
+ class={_class}
124
+ type="button"
125
+ data-size={!unstyled && isPresetSize(size) ? size : undefined}
126
+ data-active={!unstyled && active ? "" : undefined}
127
+ data-focused={!unstyled && focused ? "" : undefined}
128
+ data-touch-friendly={!unstyled && isTouchFriendly ? "" : undefined}
129
+ {...rest}
130
+ >
168
131
  {@render content()}
169
132
  </button>
170
133
  {/if}
@@ -8,7 +8,7 @@ export interface Props extends Omit<HTMLButtonAttributes, "children" | "class">
8
8
  active?: boolean;
9
9
  /** Whether this item is currently focused via keyboard navigation */
10
10
  focused?: boolean;
11
- /** Size preset affecting padding and min-height (sm, md, lg) */
11
+ /** Size preset affecting padding and min-height (sm, md, lg) or custom Tailwind classes */
12
12
  size?: "sm" | "md" | "lg" | string;
13
13
  /** Skip all default styling, use only custom classes */
14
14
  unstyled?: boolean;
@@ -33,15 +33,6 @@ export interface Props extends Omit<HTMLButtonAttributes, "children" | "class">
33
33
  /** Bindable element reference */
34
34
  el?: HTMLButtonElement | HTMLAnchorElement;
35
35
  }
36
- export interface ListItemButtonPresetClasses {
37
- size: Record<string, string>;
38
- touchFriendly: string;
39
- }
40
- export declare const LIST_ITEM_BUTTON_STUIC_BASE_CLASSES = "\n\t\tw-full\n\t\tflex items-center gap-2\n\t\ttext-left\n\t\trounded-[var(--lib-radius)]\n\t\tcursor-pointer\n\t\ttouch-action-manipulation\n\n\t\tbg-lib-bg dark:bg-lib-bg-dark\n\t\ttext-lib-text dark:text-lib-text-dark\n\n\t\tborder border-lib-border dark:border-lib-border-dark\n\n\t\thover:bg-lib-hover-bg dark:hover:bg-lib-hover-bg-dark\n\t\thover:text-lib-hover-text dark:hover:text-lib-hover-text-dark\n\t\thover:border-lib-hover-border dark:hover:border-lib-hover-border-dark\n\n\t\tfocus:outline-none\n\t\tfocus-visible:bg-lib-focus-bg dark:focus-visible:bg-lib-focus-bg-dark\n\t\tfocus-visible:text-lib-focus-text dark:focus-visible:text-lib-focus-text-dark\n\t\tfocus-visible:border-lib-focus-border dark:focus-visible:border-lib-focus-border-dark\n\n\t\tdisabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none\n\t";
41
- export declare const LIST_ITEM_BUTTON_STUIC_PRESET_CLASSES: ListItemButtonPresetClasses;
42
- export declare const LIST_ITEM_BUTTON_ACTIVE_CLASSES = "\n\t\tbg-lib-active-bg dark:bg-lib-active-bg-dark\n\t\ttext-lib-active-text dark:text-lib-active-text-dark\n\t\tborder-lib-active-border dark:border-lib-active-border-dark\n\t";
43
- export declare const LIST_ITEM_BUTTON_FOCUSED_CLASSES = "\n\t\tbg-lib-focus-bg dark:bg-lib-focus-bg-dark\n\t\ttext-lib-focus-text dark:text-lib-focus-text-dark\n\t\tborder-lib-focus-border dark:border-lib-focus-border-dark\n\t";
44
- import "./index.css";
45
36
  declare const ListItemButton: import("svelte").Component<Props, {}, "el">;
46
37
  type ListItemButton = ReturnType<typeof ListItemButton>;
47
38
  export default ListItemButton;
@@ -19,7 +19,7 @@ A versatile button component for list-like contexts such as dropdown menus, comm
19
19
  | `children` | `Snippet` | - | Content displayed in the button |
20
20
  | `active` | `boolean` | `false` | Whether this item is currently active/selected |
21
21
  | `focused` | `boolean` | `false` | Whether this item is focused via keyboard navigation |
22
- | `size` | `"sm" \| "md" \| "lg"` | `"md"` | Size preset affecting padding and min-height |
22
+ | `size` | `"sm" \| "md" \| "lg" \| string` | `"md"` | Size preset or custom Tailwind classes |
23
23
  | `unstyled` | `boolean` | `false` | Skip all default styling, use only custom classes |
24
24
  | `touchFriendly` | `boolean \| "auto"` | `false` | Enable touch-friendly sizing. `"auto"` detects coarse pointer. |
25
25
  | `contentBefore` | `THC` | - | Icon/content displayed before the main content |
@@ -100,81 +100,136 @@ A versatile button component for list-like contexts such as dropdown menus, comm
100
100
 
101
101
  ## CSS Variables
102
102
 
103
- All colors support customization via CSS variables. Define them on a parent element to override defaults.
103
+ All styling can be customized via CSS variables. Define them on a parent element or in `:root` to override defaults.
104
104
 
105
- ### Border Radius
105
+ ### Structure Tokens
106
106
 
107
107
  | Variable | Default | Description |
108
108
  |----------|---------|-------------|
109
- | `--lib-radius` | `var(--radius-md)` | Border radius |
109
+ | `--stuic-list-item-button-radius` | `var(--radius-md)` | Border radius |
110
+ | `--stuic-list-item-button-transition` | `150ms` | Transition duration |
111
+ | `--stuic-list-item-button-gap` | `0.5rem` | Gap between icon and content |
110
112
 
111
- ### Base State
113
+ ### Focus Ring Tokens
112
114
 
113
115
  | Variable | Default | Description |
114
116
  |----------|---------|-------------|
115
- | `--color-lib-bg` | `neutral-200` | Background color |
116
- | `--color-lib-bg-dark` | `neutral-600` | Background color (dark mode) |
117
- | `--color-lib-text` | `black` | Text color |
118
- | `--color-lib-text-dark` | `neutral-100` | Text color (dark mode) |
119
- | `--color-lib-border` | `transparent` | Border color |
120
- | `--color-lib-border-dark` | `transparent` | Border color (dark mode) |
117
+ | `--stuic-list-item-button-ring-width` | `3px` | Focus ring width |
118
+ | `--stuic-list-item-button-ring-offset` | `0px` | Focus ring offset |
119
+ | `--stuic-list-item-button-ring-color` | `var(--stuic-color-ring)` | Focus ring color |
121
120
 
122
- ### Hover State
121
+ ### Size Tokens: Small (`size="sm"`)
123
122
 
124
123
  | Variable | Default | Description |
125
124
  |----------|---------|-------------|
126
- | `--color-lib-hover-bg` | `neutral-500` | Hover background |
127
- | `--color-lib-hover-bg-dark` | `neutral-200` | Hover background (dark mode) |
128
- | `--color-lib-hover-text` | `white` | Hover text color |
129
- | `--color-lib-hover-text-dark` | `neutral-900` | Hover text color (dark mode) |
130
- | `--color-lib-hover-border` | `transparent` | Hover border color |
131
- | `--color-lib-hover-border-dark` | `transparent` | Hover border color (dark mode) |
125
+ | `--stuic-list-item-button-padding-x-sm` | `0.5rem` | Horizontal padding |
126
+ | `--stuic-list-item-button-padding-y-sm` | `0.375rem` | Vertical padding |
127
+ | `--stuic-list-item-button-font-size-sm` | `var(--text-sm)` | Font size |
128
+ | `--stuic-list-item-button-min-height-sm` | `2.25rem` | Minimum height (36px) |
132
129
 
133
- ### Active State
130
+ ### Size Tokens: Medium (`size="md"`)
134
131
 
135
132
  | Variable | Default | Description |
136
133
  |----------|---------|-------------|
137
- | `--color-lib-active-bg` | `neutral-500` | Active background |
138
- | `--color-lib-active-bg-dark` | `neutral-200` | Active background (dark mode) |
139
- | `--color-lib-active-text` | `white` | Active text color |
140
- | `--color-lib-active-text-dark` | `neutral-900` | Active text color (dark mode) |
141
- | `--color-lib-active-border` | `transparent` | Active border color |
142
- | `--color-lib-active-border-dark` | `transparent` | Active border color (dark mode) |
134
+ | `--stuic-list-item-button-padding-x-md` | `0.625rem` | Horizontal padding |
135
+ | `--stuic-list-item-button-padding-y-md` | `0.5rem` | Vertical padding |
136
+ | `--stuic-list-item-button-font-size-md` | `var(--text-base)` | Font size |
137
+ | `--stuic-list-item-button-min-height-md` | `2.5rem` | Minimum height (40px) |
143
138
 
144
- ### Focus State
139
+ ### Size Tokens: Large (`size="lg"`)
145
140
 
146
141
  | Variable | Default | Description |
147
142
  |----------|---------|-------------|
148
- | `--color-lib-focus-bg` | `neutral-500` | Focus background |
149
- | `--color-lib-focus-bg-dark` | `neutral-200` | Focus background (dark mode) |
150
- | `--color-lib-focus-text` | `white` | Focus text color |
151
- | `--color-lib-focus-text-dark` | `neutral-900` | Focus text color (dark mode) |
152
- | `--color-lib-focus-border` | `transparent` | Focus border color |
153
- | `--color-lib-focus-border-dark` | `transparent` | Focus border color (dark mode) |
143
+ | `--stuic-list-item-button-padding-x-lg` | `0.75rem` | Horizontal padding |
144
+ | `--stuic-list-item-button-padding-y-lg` | `0.625rem` | Vertical padding |
145
+ | `--stuic-list-item-button-font-size-lg` | `var(--text-base)` | Font size |
146
+ | `--stuic-list-item-button-min-height-lg` | `2.75rem` | Minimum height (44px) |
147
+
148
+ ### Touch-Friendly Tokens
149
+
150
+ | Variable | Default | Description |
151
+ |----------|---------|-------------|
152
+ | `--stuic-list-item-button-min-height-touch` | `2.75rem` | Touch-friendly min height (44px) |
153
+ | `--stuic-list-item-button-padding-y-touch` | `0.625rem` | Touch-friendly vertical padding |
154
+
155
+ ### Color Tokens: Base State
156
+
157
+ | Variable | Default | Description |
158
+ |----------|---------|-------------|
159
+ | `--stuic-list-item-button-bg` | `var(--stuic-color-muted)` | Background color |
160
+ | `--stuic-list-item-button-text` | `var(--stuic-color-foreground)` | Text color |
161
+ | `--stuic-list-item-button-border` | `transparent` | Border color |
162
+
163
+ ### Color Tokens: Hover State
164
+
165
+ | Variable | Default | Description |
166
+ |----------|---------|-------------|
167
+ | `--stuic-list-item-button-bg-hover` | `var(--stuic-color-primary)` | Hover background |
168
+ | `--stuic-list-item-button-text-hover` | `var(--stuic-color-primary-foreground)` | Hover text color |
169
+ | `--stuic-list-item-button-border-hover` | `transparent` | Hover border color |
170
+
171
+ ### Color Tokens: Active State (selected)
172
+
173
+ | Variable | Default | Description |
174
+ |----------|---------|-------------|
175
+ | `--stuic-list-item-button-bg-active` | `var(--stuic-color-primary)` | Active background |
176
+ | `--stuic-list-item-button-text-active` | `var(--stuic-color-primary-foreground)` | Active text color |
177
+ | `--stuic-list-item-button-border-active` | `transparent` | Active border color |
178
+
179
+ ### Color Tokens: Focus State (keyboard navigation)
180
+
181
+ | Variable | Default | Description |
182
+ |----------|---------|-------------|
183
+ | `--stuic-list-item-button-bg-focus` | `var(--stuic-color-primary)` | Focus background |
184
+ | `--stuic-list-item-button-text-focus` | `var(--stuic-color-primary-foreground)` | Focus text color |
185
+ | `--stuic-list-item-button-border-focus` | `transparent` | Focus border color |
154
186
 
155
187
  ### Custom Theme Example
156
188
 
157
189
  ```svelte
158
190
  <div style="
159
- --color-lib-hover-bg: var(--color-blue-500);
160
- --color-lib-hover-bg-dark: var(--color-blue-600);
161
- --color-lib-active-bg: var(--color-blue-600);
162
- --color-lib-active-bg-dark: var(--color-blue-500);
191
+ --stuic-list-item-button-bg-hover: var(--color-blue-500);
192
+ --stuic-list-item-button-bg-active: var(--color-blue-600);
163
193
  ">
164
194
  <ListItemButton>Blue theme</ListItemButton>
165
195
  <ListItemButton active>Active blue</ListItemButton>
166
196
  </div>
167
197
  ```
168
198
 
169
- ## Exported Constants
199
+ ### Global Theme Override
200
+
201
+ ```css
202
+ /* In your app.css */
203
+ :root {
204
+ --stuic-list-item-button-radius: 0;
205
+ --stuic-list-item-button-bg-hover: var(--color-indigo-500);
206
+ --stuic-list-item-button-text-hover: white;
207
+ }
208
+ ```
209
+
210
+ ## Data Attributes
211
+
212
+ The component uses data attributes for styling states. These are automatically applied based on props:
213
+
214
+ | Attribute | Applied When | CSS Selector |
215
+ |-----------|--------------|--------------|
216
+ | `data-size="sm\|md\|lg"` | When `size` is a preset value | `.stuic-list-item-button[data-size="md"]` |
217
+ | `data-active` | When `active={true}` | `.stuic-list-item-button[data-active]` |
218
+ | `data-focused` | When `focused={true}` | `.stuic-list-item-button[data-focused]` |
219
+ | `data-touch-friendly` | When `touchFriendly={true}` or auto-detected | `.stuic-list-item-button[data-touch-friendly]` |
220
+
221
+ ### Custom Styling via Data Attributes
170
222
 
171
- The component exports several class constants for advanced customization:
223
+ ```css
224
+ /* Example: Custom active state styling */
225
+ .stuic-list-item-button[data-active] {
226
+ background: var(--color-green-500);
227
+ color: white;
228
+ }
172
229
 
173
- ```typescript
174
- import {
175
- LIST_ITEM_BUTTON_STUIC_BASE_CLASSES,
176
- LIST_ITEM_BUTTON_STUIC_PRESET_CLASSES,
177
- LIST_ITEM_BUTTON_ACTIVE_CLASSES,
178
- LIST_ITEM_BUTTON_FOCUSED_CLASSES,
179
- } from "stuic";
230
+ /* Example: Different padding for all sizes */
231
+ .stuic-list-item-button[data-size] {
232
+ padding-left: 1rem;
233
+ padding-right: 1rem;
234
+ }
180
235
  ```