@marianmeres/stuic 2.65.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 +16 -4
  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
@@ -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,57 +35,6 @@
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">
@@ -115,31 +64,28 @@
115
64
 
116
65
  const devicePointer = new DevicePointer();
117
66
 
118
- const _base = LIST_ITEM_BUTTON_STUIC_BASE_CLASSES;
119
- const _preset = LIST_ITEM_BUTTON_STUIC_PRESET_CLASSES;
67
+ // Check if size is a known preset
68
+ const isPresetSize = (s: string): s is "sm" | "md" | "lg" =>
69
+ ["sm", "md", "lg"].includes(s);
120
70
 
121
- let _touchClasses = $derived.by(() => {
122
- if (touchFriendly === true) return _preset.touchFriendly;
123
- if (touchFriendly === "auto" && devicePointer.isCoarse) return _preset.touchFriendly;
124
- return "";
71
+ // Compute whether touch-friendly should be active
72
+ let isTouchFriendly = $derived.by(() => {
73
+ if (touchFriendly === true) return true;
74
+ if (touchFriendly === "auto" && devicePointer.isCoarse) return true;
75
+ return false;
125
76
  });
126
77
 
78
+ // Build class string - styling is handled by CSS + data attributes
127
79
  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,
80
+ twMerge(
81
+ !unstyled && "stuic-list-item-button",
82
+ // Custom size classes when not using preset
83
+ !unstyled && !isPresetSize(size) && size,
84
+ // User-provided state classes
138
85
  active && classActive,
139
86
  focused && !active && classFocused,
140
- ]
141
- .filter(Boolean)
142
- .join(" ")
87
+ classProp
88
+ )
143
89
  );
144
90
  </script>
145
91
 
@@ -160,11 +106,29 @@
160
106
  {/snippet}
161
107
 
162
108
  {#if href}
163
- <a {href} bind:this={el} class={twMerge(_class, classProp)} {...rest as any}>
109
+ <a
110
+ {href}
111
+ bind:this={el}
112
+ class={_class}
113
+ data-size={!unstyled && isPresetSize(size) ? size : undefined}
114
+ data-active={!unstyled && active ? "" : undefined}
115
+ data-focused={!unstyled && focused ? "" : undefined}
116
+ data-touch-friendly={!unstyled && isTouchFriendly ? "" : undefined}
117
+ {...rest as any}
118
+ >
164
119
  {@render content()}
165
120
  </a>
166
121
  {:else}
167
- <button bind:this={el} class={twMerge(_class, classProp)} type="button" {...rest}>
122
+ <button
123
+ bind:this={el}
124
+ class={_class}
125
+ type="button"
126
+ data-size={!unstyled && isPresetSize(size) ? size : undefined}
127
+ data-active={!unstyled && active ? "" : undefined}
128
+ data-focused={!unstyled && focused ? "" : undefined}
129
+ data-touch-friendly={!unstyled && isTouchFriendly ? "" : undefined}
130
+ {...rest}
131
+ >
168
132
  {@render content()}
169
133
  </button>
170
134
  {/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,14 +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
36
  import "./index.css";
45
37
  declare const ListItemButton: import("svelte").Component<Props, {}, "el">;
46
38
  type ListItemButton = ReturnType<typeof 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
  ```
@@ -1,58 +1,177 @@
1
- /* Border radius (0.375rem = rounded-md) */
1
+ /* =============================================================================
2
+ LIST ITEM BUTTON COMPONENT TOKENS
3
+ Override globally: :root { --stuic-list-item-button-radius: 0; }
4
+ Override locally: <ListItemButton style="--stuic-list-item-button-radius: 9999px;">
5
+ ============================================================================= */
6
+
2
7
  :root {
3
- --lib-radius: var(--radius-md);
4
- }
5
-
6
- /* prettier-ignore */
7
- @theme inline {
8
-
9
- /* "lib" -> list item button */
10
-
11
- /* Private defaults (for human readability) */
12
- --_lib-bg: var(--color-neutral-200);
13
- --_lib-bg-hi: var(--color-neutral-500);
14
- --_lib-bg-dark: var(--color-neutral-600);
15
- --_lib-bg-hi-dark: var(--color-neutral-200);
16
-
17
- --_lib-text: var(--color-black);
18
- --_lib-text-hi: var(--color-white);
19
- --_lib-text-dark: var(--color-neutral-100);
20
- --_lib-text-hi-dark: var(--color-neutral-900);
21
-
22
- --_lib-border: transparent;
23
- --_lib-border-hi: transparent;
24
- --_lib-border-dark: transparent;
25
- --_lib-border-hi-dark: transparent;
26
-
27
- /* Base state */
28
- --color-lib-bg: var(--color-lib-bg, var(--_lib-bg));
29
- --color-lib-bg-dark: var(--color-lib-bg-dark, var(--_lib-bg-dark));
30
- --color-lib-text: var(--color-lib-text, var(--_lib-text));
31
- --color-lib-text-dark: var(--color-lib-text-dark, var(--_lib-text-dark));
32
- --color-lib-border: var(--color-lib-border, var(--_lib-border));
33
- --color-lib-border-dark: var(--color-lib-border-dark, var(--_lib-border-dark));
34
-
35
- /* Hover state */
36
- --color-lib-hover-bg: var(--color-lib-hover-bg, var(--_lib-bg-hi));
37
- --color-lib-hover-bg-dark: var(--color-lib-hover-bg-dark, var(--_lib-bg-hi-dark));
38
- --color-lib-hover-text: var(--color-lib-hover-text, var(--_lib-text-hi));
39
- --color-lib-hover-text-dark: var(--color-lib-hover-text-dark, var(--_lib-text-hi-dark));
40
- --color-lib-hover-border: var(--color-lib-hover-border, var(--_lib-border-hi));
41
- --color-lib-hover-border-dark: var(--color-lib-hover-border-dark, var(--_lib-border-hi-dark));
42
-
43
- /* Active/Selected state */
44
- --color-lib-active-bg: var(--color-lib-active-bg, var(--_lib-bg-hi));
45
- --color-lib-active-bg-dark: var(--color-lib-active-bg-dark, var(--_lib-bg-hi-dark));
46
- --color-lib-active-text: var(--color-lib-active-text, var(--_lib-text-hi));
47
- --color-lib-active-text-dark: var(--color-lib-active-text-dark, var(--_lib-text-hi-dark));
48
- --color-lib-active-border: var(--color-lib-active-border, var(--_lib-border-hi));
49
- --color-lib-active-border-dark: var(--color-lib-active-border-dark, var(--_lib-border-hi-dark));
50
-
51
- /* Focus-visible state (keyboard focus) */
52
- --color-lib-focus-bg: var(--color-lib-focus-bg, var(--_lib-bg-hi));
53
- --color-lib-focus-bg-dark: var(--color-lib-focus-bg-dark, var(--_lib-bg-hi-dark));
54
- --color-lib-focus-text: var(--color-lib-focus-text, var(--_lib-text-hi));
55
- --color-lib-focus-text-dark: var(--color-lib-focus-text-dark, var(--_lib-text-hi-dark));
56
- --color-lib-focus-border: var(--color-lib-focus-border, var(--_lib-border-hi));
57
- --color-lib-focus-border-dark: var(--color-lib-focus-border-dark, var(--_lib-border-hi-dark));
8
+ /* Structure tokens */
9
+ --stuic-list-item-button-radius: var(--radius-md);
10
+ --stuic-list-item-button-transition: 150ms;
11
+ --stuic-list-item-button-gap: 0.5rem;
12
+
13
+ /* Focus ring */
14
+ --stuic-list-item-button-ring-width: 3px;
15
+ --stuic-list-item-button-ring-offset: 0px;
16
+ --stuic-list-item-button-ring-color: var(--stuic-color-ring);
17
+
18
+ /* Size: sm */
19
+ --stuic-list-item-button-padding-x-sm: 0.5rem;
20
+ --stuic-list-item-button-padding-y-sm: 0.375rem;
21
+ --stuic-list-item-button-font-size-sm: var(--text-sm);
22
+ --stuic-list-item-button-min-height-sm: 2.25rem; /* 36px */
23
+
24
+ /* Size: md */
25
+ --stuic-list-item-button-padding-x-md: 0.625rem;
26
+ --stuic-list-item-button-padding-y-md: 0.5rem;
27
+ --stuic-list-item-button-font-size-md: var(--text-base);
28
+ --stuic-list-item-button-min-height-md: 2.5rem; /* 40px */
29
+
30
+ /* Size: lg */
31
+ --stuic-list-item-button-padding-x-lg: 0.75rem;
32
+ --stuic-list-item-button-padding-y-lg: 0.625rem;
33
+ --stuic-list-item-button-font-size-lg: var(--text-base);
34
+ --stuic-list-item-button-min-height-lg: 2.75rem; /* 44px - Apple HIG minimum */
35
+
36
+ /* Touch-friendly modifier */
37
+ --stuic-list-item-button-min-height-touch: 2.75rem; /* 44px */
38
+ --stuic-list-item-button-padding-y-touch: 0.625rem;
39
+
40
+ /* Color tokens: Base state */
41
+ --stuic-list-item-button-bg: var(--stuic-color-muted);
42
+ --stuic-list-item-button-text: var(--stuic-color-foreground);
43
+ --stuic-list-item-button-border: transparent;
44
+
45
+ /* Color tokens: Hover state */
46
+ --stuic-list-item-button-bg-hover: var(--stuic-color-primary);
47
+ --stuic-list-item-button-text-hover: var(--stuic-color-primary-foreground);
48
+ --stuic-list-item-button-border-hover: transparent;
49
+
50
+ /* Color tokens: Active/Selected state */
51
+ --stuic-list-item-button-bg-active: var(--stuic-color-primary);
52
+ --stuic-list-item-button-text-active: var(--stuic-color-primary-foreground);
53
+ --stuic-list-item-button-border-active: transparent;
54
+
55
+ /* Color tokens: Focus state (keyboard navigation) */
56
+ --stuic-list-item-button-bg-focus: var(--stuic-color-primary);
57
+ --stuic-list-item-button-text-focus: var(--stuic-color-primary-foreground);
58
+ --stuic-list-item-button-border-focus: transparent;
59
+ }
60
+
61
+ /* =============================================================================
62
+ BASE STYLES
63
+ ============================================================================= */
64
+
65
+ .stuic-list-item-button {
66
+ /* Layout */
67
+ display: flex;
68
+ align-items: center;
69
+ gap: var(--stuic-list-item-button-gap);
70
+ width: 100%;
71
+
72
+ /* Typography */
73
+ text-align: left;
74
+ text-decoration: none;
75
+
76
+ /* Box model */
77
+ border-width: 1px;
78
+ border-style: solid;
79
+ border-radius: var(--stuic-list-item-button-radius);
80
+
81
+ /* Colors */
82
+ background: var(--stuic-list-item-button-bg);
83
+ color: var(--stuic-list-item-button-text);
84
+ border-color: var(--stuic-list-item-button-border);
85
+
86
+ /* Interaction */
87
+ cursor: pointer;
88
+ user-select: none;
89
+ -webkit-tap-highlight-color: transparent;
90
+ touch-action: manipulation;
91
+ transition:
92
+ background var(--stuic-list-item-button-transition),
93
+ color var(--stuic-list-item-button-transition),
94
+ border-color var(--stuic-list-item-button-transition);
95
+ }
96
+
97
+ /* =============================================================================
98
+ STATE STYLES
99
+ ============================================================================= */
100
+
101
+ /* Hover state */
102
+ .stuic-list-item-button:hover:not(:disabled):not([data-active]) {
103
+ background: var(--stuic-list-item-button-bg-hover);
104
+ color: var(--stuic-list-item-button-text-hover);
105
+ border-color: var(--stuic-list-item-button-border-hover);
106
+ }
107
+
108
+ /* Focus styles */
109
+ .stuic-list-item-button:focus {
110
+ outline: none;
111
+ }
112
+
113
+ .stuic-list-item-button:focus-visible:not([data-active]) {
114
+ background: var(--stuic-list-item-button-bg-focus);
115
+ color: var(--stuic-list-item-button-text-focus);
116
+ border-color: var(--stuic-list-item-button-border-focus);
117
+ outline: var(--stuic-list-item-button-ring-width) solid
118
+ var(--stuic-list-item-button-ring-color);
119
+ outline-offset: var(--stuic-list-item-button-ring-offset);
120
+ }
121
+
122
+ /* Active/Selected state (via data attribute) */
123
+ .stuic-list-item-button[data-active] {
124
+ background: var(--stuic-list-item-button-bg-active);
125
+ color: var(--stuic-list-item-button-text-active);
126
+ border-color: var(--stuic-list-item-button-border-active);
127
+ }
128
+
129
+ /* Focused state (keyboard navigation highlight, distinct from focus-visible) */
130
+ .stuic-list-item-button[data-focused]:not([data-active]) {
131
+ background: var(--stuic-list-item-button-bg-focus);
132
+ color: var(--stuic-list-item-button-text-focus);
133
+ border-color: var(--stuic-list-item-button-border-focus);
134
+ }
135
+
136
+ /* Disabled state */
137
+ .stuic-list-item-button:disabled {
138
+ opacity: 0.5;
139
+ cursor: not-allowed;
140
+ pointer-events: none;
141
+ }
142
+
143
+ /* =============================================================================
144
+ SIZE VARIANTS
145
+ ============================================================================= */
146
+
147
+ .stuic-list-item-button[data-size="sm"] {
148
+ padding: var(--stuic-list-item-button-padding-y-sm)
149
+ var(--stuic-list-item-button-padding-x-sm);
150
+ font-size: var(--stuic-list-item-button-font-size-sm);
151
+ min-height: var(--stuic-list-item-button-min-height-sm);
152
+ }
153
+
154
+ .stuic-list-item-button[data-size="md"] {
155
+ padding: var(--stuic-list-item-button-padding-y-md)
156
+ var(--stuic-list-item-button-padding-x-md);
157
+ font-size: var(--stuic-list-item-button-font-size-md);
158
+ min-height: var(--stuic-list-item-button-min-height-md);
159
+ }
160
+
161
+ .stuic-list-item-button[data-size="lg"] {
162
+ padding: var(--stuic-list-item-button-padding-y-lg)
163
+ var(--stuic-list-item-button-padding-x-lg);
164
+ font-size: var(--stuic-list-item-button-font-size-lg);
165
+ min-height: var(--stuic-list-item-button-min-height-lg);
166
+ }
167
+
168
+ /* =============================================================================
169
+ TOUCH-FRIENDLY MODIFIER
170
+ Ensures minimum 44px touch target per Apple HIG guidelines
171
+ ============================================================================= */
172
+
173
+ .stuic-list-item-button[data-touch-friendly] {
174
+ min-height: var(--stuic-list-item-button-min-height-touch);
175
+ padding-top: var(--stuic-list-item-button-padding-y-touch);
176
+ padding-bottom: var(--stuic-list-item-button-padding-y-touch);
58
177
  }
@@ -1 +1 @@
1
- export { default as ListItemButton, type Props as ListItemButtonProps, type ListItemButtonPresetClasses, LIST_ITEM_BUTTON_STUIC_BASE_CLASSES, LIST_ITEM_BUTTON_STUIC_PRESET_CLASSES, LIST_ITEM_BUTTON_ACTIVE_CLASSES, LIST_ITEM_BUTTON_FOCUSED_CLASSES, } from "./ListItemButton.svelte";
1
+ export { default as ListItemButton, type Props as ListItemButtonProps, } from "./ListItemButton.svelte";
@@ -1 +1 @@
1
- export { default as ListItemButton, LIST_ITEM_BUTTON_STUIC_BASE_CLASSES, LIST_ITEM_BUTTON_STUIC_PRESET_CLASSES, LIST_ITEM_BUTTON_ACTIVE_CLASSES, LIST_ITEM_BUTTON_FOCUSED_CLASSES, } from "./ListItemButton.svelte";
1
+ export { default as ListItemButton, } from "./ListItemButton.svelte";
@@ -27,6 +27,7 @@
27
27
  <script lang="ts">
28
28
  import ModalDialog from "../ModalDialog/ModalDialog.svelte";
29
29
  import { twMerge } from "../../utils/tw-merge.js";
30
+ import "./index.css";
30
31
 
31
32
  let {
32
33
  visible = $bindable(false),
@@ -103,9 +104,8 @@
103
104
  >
104
105
  <div
105
106
  class={twMerge(
106
- "bg-white dark:bg-neutral-800",
107
+ "stuic-modal",
107
108
  "flex flex-col overflow-hidden",
108
- "rounded-none md:rounded-md",
109
109
  "w-full flex-1",
110
110
  classProp
111
111
  )}
@@ -126,9 +126,3 @@
126
126
  </div>
127
127
  </div>
128
128
  </ModalDialog>
129
-
130
- <style>
131
- .main {
132
- scrollbar-width: thin;
133
- }
134
- </style>
@@ -20,6 +20,7 @@ export interface Props {
20
20
  /** Disable body scroll lock when modal is open */
21
21
  noScrollLock?: boolean;
22
22
  }
23
+ import "./index.css";
23
24
  declare const Modal: import("svelte").Component<Props, {
24
25
  close: () => void;
25
26
  open: (openerOrEvent?: null | HTMLElement | MouseEvent) => void;