@marianmeres/stuic 3.4.2 → 3.4.4

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.
package/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # @marianmeres/stuic
2
2
 
3
- **S**velte **T**ailwind **UI** **C**omponents
3
+ [![NPM](https://img.shields.io/npm/v/@marianmeres/stuic)](https://www.npmjs.com/package/@marianmeres/stuic)
4
+ [![License](https://img.shields.io/npm/l/@marianmeres/stuic)](LICENSE)
4
5
 
5
- An opinionated Svelte 5 component library built with Tailwind CSS v4. Featuring a centralized design token system for consistent theming across all components.
6
+ **S**velte **T**ailwind **UI** **C**omponents — an opinionated Svelte 5 component library built with Tailwind CSS v4. Featuring a centralized design token system for consistent theming across all components.
6
7
 
7
8
  ## Installation
8
9
 
@@ -10,30 +11,28 @@ An opinionated Svelte 5 component library built with Tailwind CSS v4. Featuring
10
11
  npm install @marianmeres/stuic
11
12
  ```
12
13
 
13
- ## Quick Start
14
+ ## Usage
14
15
 
15
16
  ```svelte
16
17
  <script>
17
18
  import { Button, Modal } from "@marianmeres/stuic";
18
19
 
19
- let modal;
20
+ let open = $state(false);
20
21
  </script>
21
22
 
22
- <Button onclick={() => modal.open()}>Open Modal</Button>
23
+ <Button onclick={() => open = true}>Open Modal</Button>
23
24
 
24
- <Modal bind:this={modal}>
25
+ <Modal bind:open>
25
26
  <p>Hello from Modal!</p>
26
27
  </Modal>
27
28
  ```
28
29
 
29
30
  ## Theming System
30
31
 
31
- STUIC uses a 3-layer CSS variable token system that enables both global theming and per-component customization.
32
-
33
- ### Architecture
32
+ STUIC uses a 3-layer CSS variable token system:
34
33
 
35
34
  ```
36
- Layer 1: Global Semantic Tokens (--stuic-accent, --stuic-surface, etc.)
35
+ Layer 1: Theme Tokens (--stuic-color-*)
37
36
  ↓ (used as fallback defaults)
38
37
  Layer 2: Component Tokens (--stuic-button-bg, --stuic-input-accent, etc.)
39
38
  ↓ (Tailwind utility class references)
@@ -42,20 +41,16 @@ Layer 3: Instance Overrides (inline styles, class props)
42
41
 
43
42
  ### Global Theming
44
43
 
45
- Override global tokens in your app's CSS to change the entire library's appearance:
44
+ Override theme tokens in your app's CSS:
46
45
 
47
46
  ```css
48
- /* app.css */
49
47
  :root {
50
- /* Change the accent color globally */
51
- --stuic-accent: #6366f1; /* Indigo brand color */
52
- --stuic-accent-hover: #4f46e5;
53
- --stuic-accent-active: #4338ca;
48
+ --stuic-color-primary: #6366f1;
49
+ --stuic-color-primary-hover: #4f46e5;
54
50
  }
55
51
 
56
- .dark {
57
- --stuic-accent: #818cf8;
58
- --stuic-accent-hover: #a5b4fc;
52
+ :root.dark {
53
+ --stuic-color-primary: #818cf8;
59
54
  }
60
55
  ```
61
56
 
@@ -65,232 +60,90 @@ Override specific component tokens:
65
60
 
66
61
  ```css
67
62
  :root {
68
- /* Only change switches to green */
69
- --stuic-switch-accent: #10b981;
70
-
71
- /* Custom button colors */
72
- --stuic-button-bg: #f3f4f6;
73
- --stuic-button-bg-hover: #e5e7eb;
63
+ --stuic-button-radius: 9999px; /* Pill buttons */
64
+ --stuic-switch-accent: #10b981; /* Green switches */
74
65
  }
75
66
  ```
76
67
 
77
68
  ### Instance Overrides
78
69
 
79
- Use class props or inline styles for one-off customizations:
70
+ Use `class` props or inline styles:
80
71
 
81
72
  ```svelte
82
73
  <Button class="bg-purple-500 hover:bg-purple-600 text-white">
83
74
  Custom Button
84
75
  </Button>
85
76
 
86
- <div style="--stuic-list-item-button-bg-hover: var(--color-blue-500);">
87
- <ListItemButton>Blue hover</ListItemButton>
88
- </div>
89
- ```
90
-
91
- ## Global Design Tokens
92
-
93
- All tokens are defined in `src/lib/theme.css`. See the full reference below.
94
-
95
- ### Accent Colors
96
-
97
- | Token | Light Mode | Dark Mode | Description |
98
- |-------|------------|-----------|-------------|
99
- | `--stuic-accent` | `sky-600` | `sky-400` | Primary accent for interactive elements |
100
- | `--stuic-accent-hover` | `sky-700` | `sky-300` | Accent hover state |
101
- | `--stuic-accent-active` | `sky-800` | `sky-200` | Accent active/pressed state |
102
- | `--stuic-accent-destructive` | `red-600` | `red-400` | Destructive/error accent |
103
- | `--stuic-accent-destructive-hover` | `red-700` | `red-300` | Destructive hover state |
104
-
105
- ### Surface Colors
106
-
107
- | Token | Light Mode | Dark Mode | Description |
108
- |-------|------------|-----------|-------------|
109
- | `--stuic-surface` | `white` | `neutral-900` | Base page background |
110
- | `--stuic-surface-elevated` | `white` | `neutral-800` | Cards, modals, popovers |
111
- | `--stuic-surface-sunken` | `neutral-100` | `neutral-700` | Input backgrounds, wells |
112
- | `--stuic-surface-overlay` | `neutral-800` | `neutral-950` | Backdrops, tooltips |
113
- | `--stuic-surface-interactive` | `neutral-200` | `neutral-600` | Buttons, list items |
114
- | `--stuic-surface-interactive-hover` | `neutral-500` | `neutral-200` | Interactive hover |
115
- | `--stuic-surface-interactive-active` | `neutral-600` | `neutral-100` | Interactive active |
116
-
117
- ### Text Colors
118
-
119
- | Token | Light Mode | Dark Mode | Description |
120
- |-------|------------|-----------|-------------|
121
- | `--stuic-text` | `black` | `neutral-100` | Primary text |
122
- | `--stuic-text-muted` | `neutral-600` | `neutral-400` | Secondary/muted text |
123
- | `--stuic-text-inverse` | `white` | `neutral-900` | Text on dark backgrounds |
124
- | `--stuic-text-placeholder` | `neutral-400` | `neutral-500` | Placeholder text |
125
- | `--stuic-text-on-accent` | `white` | `neutral-950` | Text on accent backgrounds |
126
- | `--stuic-text-destructive` | `red-600` | `red-400` | Error/destructive text |
127
-
128
- ### Border Colors
129
-
130
- | Token | Light Mode | Dark Mode | Description |
131
- |-------|------------|-----------|-------------|
132
- | `--stuic-border` | `neutral-300` | `neutral-600` | Default border |
133
- | `--stuic-border-strong` | `neutral-400` | `neutral-500` | Emphasized border |
134
- | `--stuic-border-subtle` | `neutral-200` | `neutral-700` | Subtle/light border |
135
- | `--stuic-border-focus` | `sky-500` | `sky-400` | Focus ring border |
136
- | `--stuic-border-error` | `red-500` | `red-400` | Error state border |
137
-
138
- ### Other Tokens
139
-
140
- | Token | Default | Description |
141
- |-------|---------|-------------|
142
- | `--stuic-ring` | `sky-500` / `sky-400` | Focus ring color |
143
- | `--stuic-ring-offset` | `2px` | Focus ring offset |
144
- | `--stuic-ring-width` | `2px` | Focus ring width |
145
- | `--stuic-radius-sm` | `--radius-sm` | Small border radius |
146
- | `--stuic-radius` | `--radius-md` | Default border radius |
147
- | `--stuic-radius-lg` | `--radius-lg` | Large border radius |
148
- | `--stuic-radius-full` | `9999px` | Fully rounded |
149
- | `--stuic-transition-fast` | `100ms` | Fast transitions |
150
- | `--stuic-transition-normal` | `150ms` | Normal transitions |
151
- | `--stuic-transition-slow` | `300ms` | Slow transitions |
152
-
153
- ## Component Tokens
154
-
155
- Each component defines its own tokens that reference global tokens as defaults:
156
-
157
- | Component | Token Prefix | Key Tokens |
158
- |-----------|--------------|------------|
159
- | Button | `--stuic-button-*` | `bg`, `text`, `border`, `border-focus` |
160
- | Switch | `--stuic-switch-*` | `accent` |
161
- | Input | `--stuic-input-*` | `accent`, `accent-error` |
162
- | Progress | `--stuic-progress-*` | `bg`, `accent` |
163
- | ListItemButton | `--stuic-list-item-button-*` | `bg`, `text`, `border`, `bg-hover`, `text-hover`, etc. |
164
- | ButtonGroupRadio | `--stuic-button-group-*` | `bg`, `text`, `border`, `accent`, `bg-active`, `text-active` |
165
- | TabbedMenu | `--stuic-tabbed-menu-*` | `tab-bg`, `tab-text`, `tab-bg-active`, `tab-text-active`, `border` |
166
- | DismissibleMessage | `--stuic-dismissible-message-*` | `bg`, `text`, `border` |
167
- | Notifications | `--stuic-notification-*` | `bg`, `text`, `border` |
168
- | Tooltip | `--stuic-tooltip-*` | `bg`, `text` |
169
- | Popover | `--stuic-popover-*` | `bg`, `text`, `border` |
170
- | Skeleton | `--stuic-skeleton-*` | `bg`, `bg-highlight`, `duration` |
171
-
172
- ## CSS Variable Naming Convention
173
-
174
- **STRICT REQUIREMENT**: All CSS variables follow this pattern:
175
-
176
- ```
177
- --stuic-{component}-{element?}-{property}-{state?}
77
+ <!-- Or use unstyled mode for full control -->
78
+ <Button unstyled class="my-custom-button">
79
+ Fully Custom
80
+ </Button>
178
81
  ```
179
82
 
180
- - **Full component names** (no abbreviations): `list-item-button` not `lib`
181
- - **State at end**: `--stuic-button-bg-hover` not `--stuic-button-hover-bg`
182
- - **No `-dark` suffix**: Dark mode defined in `.dark {}` selector
183
- - **Properties**: `bg`, `text`, `border`, `ring`, `shadow`, `accent`
184
- - **States**: `hover`, `active`, `focus`, `disabled`, `error`
83
+ ### Dark Mode
185
84
 
186
- ### Examples
85
+ Add `class="dark"` to the `<html>` element. All tokens switch automatically — no `dark:` Tailwind prefix needed.
187
86
 
188
- ```css
189
- /* Correct */
190
- --stuic-button-bg
191
- --stuic-button-bg-hover
192
- --stuic-list-item-button-text-active
193
- --stuic-input-accent-error
194
-
195
- /* Incorrect */
196
- --stuic-btn-bg /* abbreviated component name */
197
- --stuic-button-hover-bg /* state not at end */
198
- --stuic-button-bg-dark /* -dark suffix */
199
- --color-lib-hover-bg /* old naming convention */
200
- ```
201
-
202
- ## Customization Approaches
203
-
204
- ### 1. CSS Variables (Recommended)
87
+ ### Themes
205
88
 
206
- Set variables in your CSS for theming:
89
+ 26 pre-built themes available. Default: `stone`.
207
90
 
208
91
  ```css
209
- :root {
210
- --stuic-accent: #6366f1;
211
- }
212
- ```
213
-
214
- ### 2. Class Props
215
-
216
- Pass Tailwind classes directly to components:
217
-
218
- ```svelte
219
- <Button class="bg-linear-to-r from-purple-500 to-pink-500">
220
- Gradient Button
221
- </Button>
222
- ```
223
-
224
- ### 3. Unstyled Mode
225
-
226
- Use `unstyled` prop to remove all default styling:
227
-
228
- ```svelte
229
- <Button unstyled class="my-custom-button-class">
230
- Fully Custom
231
- </Button>
92
+ /* Use a different theme */
93
+ @import "@marianmeres/stuic/dist/themes/css/blue-orange.css";
232
94
  ```
233
95
 
234
96
  ## Components
235
97
 
236
- See `src/lib/README.md` for the full component list and API documentation.
237
-
238
98
  ### Layout & Overlays
239
- - AppShell, Backdrop, Modal, ModalDialog, Drawer
99
+ AppShell, Backdrop, Modal, ModalDialog, Drawer, Collapsible, SlidingPanels, Nav
240
100
 
241
101
  ### Forms & Inputs
242
- - FieldInput, FieldTextarea, FieldSelect, FieldCheckbox, FieldRadios, FieldFile, FieldAssets, FieldOptions, FieldKeyValues, FieldSwitch, Fieldset
102
+ FieldInput, FieldTextarea, FieldSelect, FieldCheckbox, FieldRadios, FieldFile, FieldAssets, FieldOptions, FieldKeyValues, FieldSwitch, FieldInputLocalized, FieldLikeButton, Fieldset
243
103
 
244
104
  ### Buttons & Controls
245
- - Button, ButtonGroupRadio, Switch, ListItemButton, X
105
+ Button, ButtonGroupRadio, Switch, TwCheck, ListItemButton, X
246
106
 
247
107
  ### Feedback & Notifications
248
- - Notifications, AlertConfirmPrompt, DismissibleMessage, Progress, Spinner, Skeleton
108
+ Notifications, AlertConfirmPrompt, DismissibleMessage, Progress, Spinner, Skeleton
249
109
 
250
110
  ### Navigation & Menus
251
- - CommandMenu, DropdownMenu, TabbedMenu, TypeaheadInput, KbdShortcut
111
+ CommandMenu, DropdownMenu, TabbedMenu, TypeaheadInput, KbdShortcut
252
112
 
253
- ### Utilities
254
- - ColorScheme, Thc, SlidingPanels, HoverExpandableWidth, AnimatedElipsis
113
+ ### Display & Utility
114
+ Avatar, Carousel, AnimatedElipsis, ThemePreview, ColorScheme, Thc, HoverExpandableWidth, AssetsPreview
255
115
 
256
116
  ## Actions
257
117
 
258
118
  ```svelte
259
- <input use:autogrow use:validate={{ required: true }} />
260
- <button use:tooltip aria-label="Tooltip text">Hover me</button>
261
- <div use:popover={{ content: 'Popover content' }}>Anchor</div>
119
+ <textarea use:autogrow />
120
+ <input use:validate={() => ({ customValidator: (v) => !v && "Required" })} />
121
+ <input use:trim />
122
+ <button use:tooltip aria-label="Save">Save</button>
123
+ <div use:focusTrap>...</div>
124
+ <div use:fileDropzone={() => ({ onDrop: handleFiles })}>Drop here</div>
262
125
  ```
263
126
 
264
- - `autogrow` - Auto-expand textarea height
265
- - `validate` - Form validation with custom validators
266
- - `focusTrap` - Trap focus within element
267
- - `tooltip` - Tooltip from aria-label
268
- - `popover` - Anchored popover
269
- - `fileDropzone` - Drag-and-drop file upload
270
- - `highlightDragover` - Visual feedback for drag operations
127
+ `autogrow` · `validate` · `focusTrap` · `autoscroll` · `fileDropzone` · `highlightDragover` · `resizableWidth` · `trim` · `typeahead` · `onSubmitValidityCheck` · `popover` · `tooltip`
271
128
 
272
129
  ## TypeScript
273
130
 
274
131
  All components export their Props types:
275
132
 
276
133
  ```ts
277
- import type { ButtonProps, ModalProps, ListItemButtonProps } from "@marianmeres/stuic";
134
+ import type { ButtonProps, ModalProps, FieldInputProps } from "@marianmeres/stuic";
278
135
  ```
279
136
 
137
+ ## API
138
+
139
+ See [API.md](API.md) for complete API documentation including all component props, actions, utilities, icons, and design token reference.
140
+
280
141
  ## Requirements
281
142
 
282
143
  - Svelte 5 (runes mode)
283
144
  - Tailwind CSS v4
284
145
  - Modern browser with CSS custom properties support
285
146
 
286
- ## Breaking Changes in v2
287
-
288
- - All CSS variables renamed from `--color-*` to `--stuic-*` prefix
289
- - ListItemButton variables renamed from `--color-lib-*` to `--stuic-list-item-button-*`
290
- - State naming changed from `--*-hover-bg` to `--*-bg-hover` (state at end)
291
- - Removed `-dark` suffix from variables (use `.dark {}` selector instead)
292
- - Legacy variable names preserved as aliases for backwards compatibility
293
-
294
147
  ## License
295
148
 
296
- MIT
149
+ [MIT](LICENSE)
@@ -1,5 +1,4 @@
1
1
  import type { THC } from "../../components/Thc/Thc.svelte";
2
- import "./index.css";
3
2
  /**
4
3
  * Open a popover by its registered ID.
5
4
  *
@@ -1,10 +1,10 @@
1
1
  import { mount, unmount } from "svelte";
2
2
  import { twMerge } from "../../utils/tw-merge.js";
3
3
  import { addAnchorName, removeAnchorName } from "../../utils/anchor-name.js";
4
+ import { iconX } from "../../icons/index.js";
4
5
  import { BodyScroll } from "../../utils/body-scroll-locker.js";
5
6
  import PopoverContent from "./PopoverContent.svelte";
6
7
  //
7
- import "./index.css";
8
8
  // Registry of open popover hide functions for closeOthers feature
9
9
  const openPopovers = new Set();
10
10
  // Reactive state tracking which popovers are open by ID
@@ -415,20 +415,9 @@ export function popover(anchorEl, fn) {
415
415
  const closeBtn = document.createElement("button");
416
416
  closeBtn.setAttribute("type", "button");
417
417
  closeBtn.setAttribute("aria-label", "Close");
418
- closeBtn.innerHTML = `<svg fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" style="width:1.25rem;height:1.25rem"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" /></svg>`;
419
- closeBtn.style.cssText = `
420
- position: absolute;
421
- top: 4px;
422
- right: 4px;
423
- background: black;
424
- color: white;
425
- border: none;
426
- border-radius: 0.25rem;
427
- cursor: pointer;
428
- opacity: 0.6;
429
- padding: 0.33rem;
430
- line-height: 1;
431
- `;
418
+ closeBtn.classList.add("stuic-close-button");
419
+ closeBtn.style.cssText = `position: absolute; top: 4px; right: 4px;`;
420
+ closeBtn.innerHTML = iconX();
432
421
  closeBtn.addEventListener("click", hide);
433
422
  popoverEl.appendChild(closeBtn);
434
423
  }
@@ -1,4 +1,3 @@
1
- import "./index.css";
2
1
  /**
3
2
  * Checks if the browser supports CSS Anchor Positioning for tooltips.
4
3
  *
@@ -1,7 +1,5 @@
1
1
  import { twMerge } from "../../utils/tw-merge.js";
2
2
  import { addAnchorName, removeAnchorName } from "../../utils/anchor-name.js";
3
- //
4
- import "./index.css";
5
3
  const TIMEOUT = 200;
6
4
  const TRANSITION = 200;
7
5
  /**
@@ -60,10 +60,12 @@
60
60
  el = $bindable(),
61
61
  }: Props = $props();
62
62
 
63
+ const DEFAULT_ICON_SIZE = 24;
64
+
63
65
  // Icon sizes for preset sizes (visual sizes are handled by CSS)
64
66
  const ICON_SIZES: Record<string, number> = {
65
67
  sm: 16,
66
- md: 20,
68
+ md: DEFAULT_ICON_SIZE,
67
69
  lg: 28,
68
70
  xl: 32,
69
71
  "2xl": 36,
@@ -159,7 +161,7 @@
159
161
  return parseInt(match[1]) * 2;
160
162
  }
161
163
 
162
- return 20; // Default fallback
164
+ return DEFAULT_ICON_SIZE; // Default fallback
163
165
  });
164
166
 
165
167
  let colors = $derived(
@@ -24,7 +24,7 @@
24
24
  --stuic-avatar-font-size-sm: var(--text-xs);
25
25
 
26
26
  /* Size: md */
27
- --stuic-avatar-size-md: calc(var(--spacing) * 10);
27
+ --stuic-avatar-size-md: calc(var(--spacing) * 11);
28
28
  --stuic-avatar-font-size-md: var(--text-base);
29
29
 
30
30
  /* Size: lg */
@@ -37,7 +37,15 @@
37
37
  el?: HTMLElement;
38
38
  /** Optional tooltip configuration or direct content */
39
39
  tooltip?: string | TooltipConfig;
40
- /** Is this button a "X" button (this is a pragmatic convenience) */
40
+ /**
41
+ * Is this button a "X" button (this is a pragmatic convenience).
42
+ *
43
+ * Note: this does NOT use the shared `.stuic-close-button` styling (see X/index.css)
44
+ * because `<Button x>` is a general-purpose "button with an X icon" (tags, toolbars,
45
+ * dialogs...), styled by the Button system (intent/variant/size). The `.stuic-close-button`
46
+ * class is specifically for floating overlay dismiss controls (popover/dropdown fallback).
47
+ * For that look, use: `<Button x unstyled class="stuic-close-button" />`
48
+ */
41
49
  x?: boolean | XProps;
42
50
  /** Optional out-of-the-box spinner support */
43
51
  spinner?: boolean | THC;
@@ -92,9 +100,13 @@
92
100
  return _tooltip ? _tooltip : () => ({ enabled: false });
93
101
  });
94
102
 
103
+ const DEFAULT_X_CLS = "size-7 -m-2";
104
+
95
105
  let _xProps: undefined | XProps = $derived.by(() => {
96
106
  if (x) {
97
- return typeof x === "boolean" ? {} : x;
107
+ const props = typeof x === "boolean" ? { class: "" } : { ...x };
108
+ props.class = twMerge(DEFAULT_X_CLS, props.class);
109
+ return props;
98
110
  }
99
111
  });
100
112
 
@@ -36,7 +36,15 @@ export interface Props extends Omit<HTMLButtonAttributes, "children"> {
36
36
  el?: HTMLElement;
37
37
  /** Optional tooltip configuration or direct content */
38
38
  tooltip?: string | TooltipConfig;
39
- /** Is this button a "X" button (this is a pragmatic convenience) */
39
+ /**
40
+ * Is this button a "X" button (this is a pragmatic convenience).
41
+ *
42
+ * Note: this does NOT use the shared `.stuic-close-button` styling (see X/index.css)
43
+ * because `<Button x>` is a general-purpose "button with an X icon" (tags, toolbars,
44
+ * dialogs...), styled by the Button system (intent/variant/size). The `.stuic-close-button`
45
+ * class is specifically for floating overlay dismiss controls (popover/dropdown fallback).
46
+ * For that look, use: `<Button x unstyled class="stuic-close-button" />`
47
+ */
40
48
  x?: boolean | XProps;
41
49
  /** Optional out-of-the-box spinner support */
42
50
  spinner?: boolean | THC;
@@ -533,7 +533,12 @@
533
533
  }
534
534
  });
535
535
 
536
- // Runtime viewport overflow detection
536
+ // Runtime viewport overflow detection.
537
+ // Known issue: Safari (as of 18.x) does not honor CSS position-try-fallbacks
538
+ // for anchor-positioned elements, so this JS check + centered modal fallback
539
+ // is the actual mechanism that handles overflow on Safari/iOS.
540
+ // On Chrome/Android, CSS position-try-fallbacks (defined in index.css) handles
541
+ // overflow natively and this check rarely fires.
537
542
  $effect(() => {
538
543
  if (!isOpen || !dropdownEl || forceFallback || runtimeFallback) return;
539
544
  if (!isAnchorPositioningSupported()) return;
@@ -842,25 +847,13 @@
842
847
  <button
843
848
  type="button"
844
849
  aria-label="Close"
845
- class="stuic-dropdown-menu-close absolute right-0 top-0 pointer-events-auto"
850
+ class="stuic-close-button absolute right-0 top-0 pointer-events-auto"
846
851
  onclick={() => {
847
852
  isOpen = false;
848
853
  triggerEl?.focus();
849
854
  }}
850
855
  >
851
- <svg
852
- fill="none"
853
- viewBox="0 0 24 24"
854
- stroke-width="2.5"
855
- stroke="currentColor"
856
- class="w-5 h-5"
857
- >
858
- <path
859
- stroke-linecap="round"
860
- stroke-linejoin="round"
861
- d="M6 18 18 6M6 6l12 12"
862
- />
863
- </svg>
856
+ {@html iconX()}
864
857
  </button>
865
858
  </div>
866
859
  {/if}
@@ -56,12 +56,6 @@
56
56
  --stuic-dropdown-menu-backdrop-bg: rgb(0 0 0 / 0.25);
57
57
  --stuic-dropdown-menu-backdrop-z-index: 40;
58
58
 
59
- /* Close button (fallback mode) */
60
- --stuic-dropdown-menu-close-bg: var(--stuic-color-foreground);
61
- --stuic-dropdown-menu-close-text: var(--stuic-color-background);
62
- --stuic-dropdown-menu-close-opacity: 0.6;
63
- --stuic-dropdown-menu-close-opacity-hover: 1;
64
-
65
59
  /* Expandable section indent */
66
60
  --stuic-dropdown-menu-expandable-indent: calc(var(--spacing) * 4);
67
61
 
@@ -196,25 +190,6 @@
196
190
  transition-property: opacity;
197
191
  }
198
192
 
199
- /* =============================================================================
200
- CLOSE BUTTON (Fallback Mode)
201
- ============================================================================= */
202
-
203
- .stuic-dropdown-menu-close {
204
- background: var(--stuic-dropdown-menu-close-bg);
205
- color: var(--stuic-dropdown-menu-close-text);
206
- opacity: var(--stuic-dropdown-menu-close-opacity);
207
- border-radius: var(--stuic-dropdown-menu-radius);
208
- padding: calc(var(--spacing) * 2);
209
- cursor: pointer;
210
- line-height: 1;
211
- transition: opacity var(--stuic-dropdown-menu-transition);
212
- }
213
-
214
- .stuic-dropdown-menu-close:hover {
215
- opacity: var(--stuic-dropdown-menu-close-opacity-hover);
216
- }
217
-
218
193
  /* =============================================================================
219
194
  EXPANDABLE SECTION
220
195
  ============================================================================= */
@@ -163,13 +163,15 @@
163
163
  {...rest as any}
164
164
  {tabindex}
165
165
  >
166
- {#if typeof rendered === "function"}
167
- {@render rendered(value)}
168
- {:else if rendered}
169
- {@html rendered}
170
- {:else}
171
- &nbsp;
172
- {/if}
166
+ <span class="block truncate">
167
+ {#if typeof rendered === "function"}
168
+ {@render rendered(value)}
169
+ {:else if rendered}
170
+ {@html rendered}
171
+ {:else}
172
+ &nbsp;
173
+ {/if}
174
+ </span>
173
175
  </Button>
174
176
 
175
177
  <input
@@ -94,7 +94,7 @@
94
94
  cardinality_of: "of max",
95
95
  cardinality_selected: "selected",
96
96
  submit: "Submit",
97
- select_all: "Select results",
97
+ select_all: "Select all",
98
98
  clear_all: "Clear selected",
99
99
  clear: "Clear",
100
100
  search_placeholder: "Type to search...",
@@ -561,7 +561,7 @@
561
561
  bind:this={modalDialog}
562
562
  preEscapeClose={escape}
563
563
  classDialog="items-start"
564
- class={twMerge("w-full max-w-2xl bg-transparent! shadow-none! pointer-events-none")}
564
+ class={twMerge("w-full max-w-2xl bg-transparent shadow-none pointer-events-none")}
565
565
  ariaLabelledby={id}
566
566
  {noScrollLock}
567
567
  >
@@ -569,7 +569,7 @@
569
569
  <div class="pointer-events-auto">
570
570
  <InputWrap
571
571
  size={renderSize}
572
- class={twMerge("m-2 mb-12 shadow-xl", classModalField)}
572
+ class={twMerge("m-1 sm:m-2 mb-12 shadow-xl", classModalField)}
573
573
  classInputBoxWrap={twMerge(
574
574
  // always look like focused
575
575
  `border border-(--stuic-input-accent)`,
@@ -88,7 +88,6 @@
88
88
  });
89
89
 
90
90
  let hasLabel = $derived(isTHCNotEmpty(label) || typeof label === "function");
91
-
92
91
  </script>
93
92
 
94
93
  {#snippet snippetOrThc({ id, value }: { id: string; value?: SnippetWithId | THC })}
@@ -140,7 +139,7 @@
140
139
 
141
140
  <div
142
141
  class={twMerge(
143
- "input-box",
142
+ "input-box min-w-0",
144
143
  hasLabel && labelLeft && labelLeftWidth === "normal" && "flex-3",
145
144
  hasLabel && labelLeft && labelLeftWidth === "wide" && "flex-2",
146
145
  classInputBox
@@ -164,24 +163,14 @@
164
163
  {#if validation && !validation?.valid}
165
164
  <div
166
165
  transition:slide={{ duration: 150 }}
167
- class={twMerge(
168
- "validation-box",
169
- "my-1 text-sm px-2",
170
- classValidationBox
171
- )}
166
+ class={twMerge("validation-box", "my-1 text-sm px-2", classValidationBox)}
172
167
  >
173
168
  {validation.message}
174
169
  </div>
175
170
  {/if}
176
171
 
177
172
  {#if description}
178
- <div
179
- class={twMerge(
180
- "desc-box",
181
- "mx-2 mt-1 text-sm opacity-50",
182
- classDescBox
183
- )}
184
- >
173
+ <div class={twMerge("desc-box", "mx-2 mt-1 text-sm opacity-50", classDescBox)}>
185
174
  {#if descriptionCollapsible}
186
175
  <Collapsible
187
176
  expanded={descriptionDefaultExpanded}
@@ -62,7 +62,7 @@
62
62
  setOpener(
63
63
  openerOrEvent instanceof MouseEvent
64
64
  ? (openerOrEvent.currentTarget as HTMLElement)
65
- : openerOrEvent ?? (document.activeElement as HTMLElement)
65
+ : (openerOrEvent ?? (document.activeElement as HTMLElement))
66
66
  );
67
67
  // dialog must be rendered in the DOM before it can be opened...
68
68
  waitForNextRepaint().then(() => {
@@ -134,7 +134,10 @@
134
134
  aria-describedby={ariaDescribedby}
135
135
  class={twMerge(
136
136
  "stuic-modal-dialog",
137
- "fixed inset-4 m-auto size-auto",
137
+ "fixed m-auto size-auto",
138
+ // Note that the default browser styling is (so we are not touching the edge):
139
+ // max-width/height: calc((100% - 6px) - 2em);
140
+ "p-0 inset-0",
138
141
  "flex justify-center items-center",
139
142
  "focus:outline-none focus-visible:outline-none",
140
143
  "bg-transparent",
@@ -0,0 +1,45 @@
1
+ /* =============================================================================
2
+ CLOSE BUTTON - Shared "floating X dismiss" for fallback/modal contexts
3
+ Override globally: :root { --stuic-close-button-radius: 0.25rem; }
4
+ Override locally: .my-component .stuic-close-button { --stuic-close-button-opacity: 0.8; }
5
+ ============================================================================= */
6
+
7
+ :root {
8
+ --stuic-close-button-bg: var(--stuic-color-foreground);
9
+ --stuic-close-button-text: var(--stuic-color-background);
10
+ --stuic-close-button-opacity: 0.6;
11
+ --stuic-close-button-opacity-hover: 1;
12
+ --stuic-close-button-radius: 9999px;
13
+ --stuic-close-button-padding: calc(var(--spacing) * 2);
14
+ --stuic-close-button-min-size: 44px;
15
+ --stuic-close-button-transition: 150ms;
16
+ --stuic-close-button-icon-size: 1.75rem;
17
+ }
18
+
19
+ @layer components {
20
+ .stuic-close-button {
21
+ display: inline-flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ background: var(--stuic-close-button-bg);
25
+ color: var(--stuic-close-button-text);
26
+ opacity: var(--stuic-close-button-opacity);
27
+ border: none;
28
+ border-radius: var(--stuic-close-button-radius);
29
+ padding: var(--stuic-close-button-padding);
30
+ cursor: pointer;
31
+ line-height: 1;
32
+ min-height: var(--stuic-close-button-min-size);
33
+ min-width: var(--stuic-close-button-min-size);
34
+ transition: opacity var(--stuic-close-button-transition);
35
+ }
36
+
37
+ .stuic-close-button:hover {
38
+ opacity: var(--stuic-close-button-opacity-hover);
39
+ }
40
+
41
+ .stuic-close-button svg {
42
+ width: var(--stuic-close-button-icon-size);
43
+ height: var(--stuic-close-button-icon-size);
44
+ }
45
+ }
package/dist/index.css CHANGED
@@ -48,6 +48,11 @@ In practice:
48
48
  @import "./components/TabbedMenu/index.css";
49
49
  @import "./components/ThemePreview/index.css";
50
50
  @import "./components/TwCheck/index.css";
51
+ @import "./components/X/index.css";
52
+
53
+ /* Action CSS */
54
+ @import "./actions/popover/index.css";
55
+ @import "./actions/tooltip/index.css";
51
56
 
52
57
  /* Base styles for STUIC components */
53
58
  @layer base {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "3.4.2",
3
+ "version": "3.4.4",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",