@marianmeres/stuic 3.73.0 → 3.75.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.
package/AGENTS.md CHANGED
@@ -23,7 +23,7 @@
23
23
 
24
24
  ```
25
25
  src/lib/
26
- ├── components/ # 55 UI components
26
+ ├── components/ # 56 UI components
27
27
  ├── actions/ # 15 Svelte actions
28
28
  ├── utils/ # 43 utility modules
29
29
  ├── themes/ # Generated theme CSS (css/) — definitions from @marianmeres/design-tokens
@@ -116,7 +116,7 @@ Global tokens that control cross-component visual properties. Defined in `src/li
116
116
 
117
117
  ### Domain Docs
118
118
 
119
- - [Components](./docs/domains/components.md) — 55 component directories, Props pattern, snippets
119
+ - [Components](./docs/domains/components.md) — 56 component directories, Props pattern, snippets
120
120
  - [Theming](./docs/domains/theming.md) — CSS tokens, dark mode, themes
121
121
  - [Actions](./docs/domains/actions.md) — 15 Svelte directives
122
122
  - [Utils](./docs/domains/utils.md) — 43 utility modules
package/API.md CHANGED
@@ -841,6 +841,36 @@ User avatar with fallback to initials or icon.
841
841
  <!-- Shows "JD" initials -->
842
842
  ```
843
843
 
844
+ #### `Pill`
845
+
846
+ Inline rounded badge/tag/chip with intent + variant + size system. Polymorphic: renders as `<span>` (default), `<a>` (when `href` set), or `<button>` (when `onclick` set).
847
+
848
+ | Prop | Type | Default | Description |
849
+ | --------------- | ------------------------------------------------------------------ | -------- | ---------------------------------------------------- |
850
+ | `intent` | `"primary" \| "accent" \| "destructive" \| "warning" \| "success"` | - | Semantic color |
851
+ | `variant` | `"solid" \| "outline" \| "ghost" \| "soft" \| "link"` | `"soft"` | Visual treatment |
852
+ | `size` | `"sm" \| "md" \| "lg"` | `"md"` | Pill size |
853
+ | `roundedFull` | `boolean` | `true` | Fully rounded (9999px). `false` → element radius |
854
+ | `block` | `boolean` | `false` | Block-level flex (full width) |
855
+ | `dot` | `boolean` | `false` | Status dot before content |
856
+ | `dismissible` | `boolean` | `false` | Built-in X dismiss button |
857
+ | `ondismiss` | `(e: MouseEvent) => void` | - | Called when X clicked (stops propagation) |
858
+ | `active` | `boolean` | `false` | Selected state (filter-chip) |
859
+ | `muted` | `boolean` | `false` | Lower opacity |
860
+ | `href`, `target`| `string` | - | Render as `<a>` |
861
+ | `onclick` | `(e: MouseEvent) => void` | - | Render as `<button>` |
862
+ | `contentBefore` | `THC` | - | Content before children |
863
+ | `contentAfter` | `THC` | - | Content after children |
864
+
865
+ ```svelte
866
+ <Pill intent="success" dot>Online</Pill>
867
+ <Pill intent="primary" dismissible ondismiss={() => removeTag()}>tag</Pill>
868
+ <Pill intent="accent" href="/profile">Profile</Pill>
869
+ <Pill intent="warning" variant="outline" active onclick={toggle}>Filter</Pill>
870
+ ```
871
+
872
+ CSS tokens: `--stuic-pill-radius`, `--stuic-pill-font-family`, `--stuic-pill-font-weight`, `--stuic-pill-gap`, `--stuic-pill-dot-size`, `--stuic-pill-ring-{width,color}`, `--stuic-pill-{padding-x,padding-y,font-size,min-height}-{sm,md,lg}`.
873
+
844
874
  #### `KbdShortcut`
845
875
 
846
876
  Keyboard shortcut display.
package/README.md CHANGED
@@ -164,7 +164,7 @@ CommandMenu, DropdownMenu, TabbedMenu, TypeaheadInput, KbdShortcut
164
164
 
165
165
  ### Display & Utility
166
166
 
167
- Avatar, Book, BookResponsive, Card, Carousel, Circle, AnimatedElipsis, H, IconSwap, ImageCycler, Separator, ThemePreview, Tree, ColorScheme, Thc, HoverExpandableWidth, AssetsPreview, AssetsPreviewInline, DataTable
167
+ Avatar, Pill, Book, BookResponsive, Card, Carousel, Circle, AnimatedElipsis, H, IconSwap, ImageCycler, Separator, ThemePreview, Tree, ColorScheme, Thc, HoverExpandableWidth, AssetsPreview, AssetsPreviewInline, DataTable
168
168
 
169
169
  ### E-commerce
170
170
 
@@ -25,6 +25,7 @@
25
25
  </script>
26
26
 
27
27
  <script lang="ts">
28
+ import { untrack } from "svelte";
28
29
  import {
29
30
  iconBookOpen,
30
31
  iconArrowRight as iconNext,
@@ -89,7 +90,7 @@
89
90
 
90
91
  // ---- Manual mode override ----
91
92
 
92
- let manualMode: "book" | "inline" | null = $state(initialMode ?? null);
93
+ let manualMode: "book" | "inline" | null = $state(untrack(() => initialMode ?? null));
93
94
 
94
95
  // ---- Inline mode ----
95
96
 
@@ -7,6 +7,12 @@
7
7
  :root {
8
8
  --stuic-dismissible-message-padding-x: calc(var(--spacing) * 4);
9
9
  --stuic-dismissible-message-padding-y: calc(var(--spacing) * 3);
10
+ --stuic-dismissible-x-padding: calc(var(--spacing) * 1);
11
+ --stuic-dismissible-x-bg-hover: rgb(0 0 0 / 0.1);
12
+ }
13
+
14
+ :root.dark {
15
+ --stuic-dismissible-x-bg-hover: rgb(255 255 255 / 0.05);
10
16
  }
11
17
 
12
18
  @layer components {
@@ -17,7 +23,10 @@
17
23
  .stuic-dismissible-message {
18
24
  display: flex;
19
25
  align-items: center;
20
- border-width: var(--stuic-dismissible-message-border-width, var(--stuic-border-width));
26
+ border-width: var(
27
+ --stuic-dismissible-message-border-width,
28
+ var(--stuic-border-width)
29
+ );
21
30
  border-style: solid;
22
31
  border-radius: var(--stuic-dismissible-message-radius, var(--stuic-radius));
23
32
  transition:
@@ -58,12 +67,14 @@
58
67
  display: flex;
59
68
  align-items: center;
60
69
  justify-content: center;
61
- padding: calc(var(--stuic-dismissible-message-padding-y) * 0.75);
70
+ padding: var(--stuic-dismissible-x-padding);
62
71
  }
63
72
 
64
73
  /* Dismiss button inherits message text color */
65
74
  .stuic-dismissible-message > .dismiss .stuic-button {
66
75
  color: var(--_text);
76
+ --_bg-hover: var(--stuic-dismissible-x-bg-hover);
77
+ --_bg-active: var(--stuic-dismissible-x-bg-hover);
67
78
  }
68
79
 
69
80
  /* =============================================================================
@@ -59,7 +59,7 @@
59
59
  </script>
60
60
 
61
61
  <script lang="ts">
62
- import { tick } from "svelte";
62
+ import { tick, untrack } from "svelte";
63
63
  import {
64
64
  validate as validateAction,
65
65
  type ValidationResult,
@@ -142,9 +142,8 @@
142
142
  });
143
143
 
144
144
  // Selected country object (initialize once from defaultCountry prop)
145
- const _initCountry = defaultCountry;
146
145
  let selectedCountry: Country | undefined = $state(
147
- _initCountry ? ISO_MAP.get(_initCountry.toUpperCase()) : undefined
146
+ untrack(() => (defaultCountry ? ISO_MAP.get(defaultCountry.toUpperCase()) : undefined))
148
147
  );
149
148
 
150
149
  // Internal local number (for controlled input)
@@ -157,7 +157,7 @@
157
157
 
158
158
  <form bind:this={el} class={_class} use:onSubmitValidityCheck {...rest}>
159
159
  <!-- General error alert -->
160
- <DismissibleMessage message={error} intent="destructive" onDismiss={false} />
160
+ <DismissibleMessage message={error} intent="destructive" />
161
161
 
162
162
  <!--
163
163
  svelte-ignore binding_property_non_reactive:
@@ -0,0 +1,205 @@
1
+ <script lang="ts" module>
2
+ import type {
3
+ HTMLAttributes,
4
+ HTMLAnchorAttributes,
5
+ HTMLButtonAttributes,
6
+ } from "svelte/elements";
7
+ import type { Snippet } from "svelte";
8
+ import type { IntentColorKey } from "../../utils/design-tokens.js";
9
+ import type { THC } from "../Thc/Thc.svelte";
10
+
11
+ export type PillVariant = "solid" | "outline" | "ghost" | "soft" | "link";
12
+ export type PillSize = "sm" | "md" | "lg";
13
+
14
+ export interface Props extends Omit<HTMLAttributes<HTMLElement>, "children"> {
15
+ /** Color intent (semantic meaning) */
16
+ intent?: IntentColorKey;
17
+ /** Visual variant (how colors are applied) */
18
+ variant?: PillVariant | string;
19
+ /** Size preset */
20
+ size?: PillSize | string;
21
+ /** Reduce emphasis (lower opacity) */
22
+ muted?: boolean;
23
+ /** Selected/active state — useful for filter-chip behavior */
24
+ active?: boolean;
25
+ /** Pill is fully rounded by default; set false to use element radius */
26
+ roundedFull?: boolean;
27
+ /** Render as block-level flex (full width). Inline-flex by default. */
28
+ block?: boolean;
29
+ /** Skip all default styling, use only custom classes */
30
+ unstyled?: boolean;
31
+ /** Additional CSS classes */
32
+ class?: string;
33
+ /** Render as anchor tag */
34
+ href?: string;
35
+ /** Link target (e.g. "_blank") — only relevant when href is set */
36
+ target?: string;
37
+ /** Render as button (when href not set) */
38
+ onclick?: (e: MouseEvent) => void;
39
+ /** Disabled (interactive variants only) */
40
+ disabled?: boolean;
41
+ /** Show built-in X dismiss control */
42
+ dismissible?: boolean;
43
+ /** Called when X is clicked. Stops propagation so parent onclick is unaffected. */
44
+ ondismiss?: (e: MouseEvent) => void;
45
+ /** Status dot rendered before content (uses current intent color) */
46
+ dot?: boolean;
47
+ /** Content rendered before children */
48
+ contentBefore?: THC;
49
+ /** Content rendered after children */
50
+ contentAfter?: THC;
51
+ /** Bindable element reference */
52
+ el?: HTMLElement;
53
+ /** Content snippet */
54
+ children?: Snippet;
55
+ }
56
+ </script>
57
+
58
+ <script lang="ts">
59
+ import { twMerge } from "../../utils/tw-merge.js";
60
+ import Thc, { isTHCNotEmpty } from "../Thc/Thc.svelte";
61
+ import { X } from "../X/index.js";
62
+
63
+ let {
64
+ class: classProp,
65
+ intent,
66
+ variant = "soft",
67
+ size = "md",
68
+ muted = false,
69
+ active = false,
70
+ roundedFull = true,
71
+ block = false,
72
+ unstyled = false,
73
+ href,
74
+ target,
75
+ onclick,
76
+ disabled,
77
+ dismissible = false,
78
+ ondismiss,
79
+ dot = false,
80
+ contentBefore,
81
+ contentAfter,
82
+ el = $bindable(),
83
+ children,
84
+ ...rest
85
+ }: Props = $props();
86
+
87
+ let _class = $derived(unstyled ? classProp : twMerge("stuic-pill", classProp));
88
+
89
+ function handleDismiss(e: MouseEvent) {
90
+ e.stopPropagation();
91
+ ondismiss?.(e);
92
+ }
93
+ </script>
94
+
95
+ {#snippet body()}
96
+ {#if dot}
97
+ <span class="stuic-pill-dot" aria-hidden="true"></span>
98
+ {/if}
99
+ {#if isTHCNotEmpty(contentBefore)}
100
+ <Thc thc={contentBefore as THC} />
101
+ {/if}
102
+ {@render children?.()}
103
+ {#if isTHCNotEmpty(contentAfter)}
104
+ <Thc thc={contentAfter as THC} />
105
+ {/if}
106
+ {/snippet}
107
+
108
+ {#snippet dismissBtn()}
109
+ <button
110
+ type="button"
111
+ class="stuic-pill-dismiss"
112
+ aria-label="Dismiss"
113
+ onclick={handleDismiss}
114
+ {disabled}
115
+ >
116
+ <X strokeWidth={2} />
117
+ </button>
118
+ {/snippet}
119
+
120
+ {#if dismissible}
121
+ <!-- Wrapper pattern: outer span carries pill styling; inner element is the
122
+ interactive area (when href/onclick); X dismiss is a sibling button. -->
123
+ <span
124
+ bind:this={el}
125
+ class={_class}
126
+ data-intent={!unstyled ? intent : undefined}
127
+ data-variant={!unstyled ? variant : undefined}
128
+ data-size={!unstyled ? size : undefined}
129
+ data-muted={!unstyled && muted ? "true" : undefined}
130
+ data-active={!unstyled && active ? "true" : undefined}
131
+ data-rounded-full={!unstyled && roundedFull ? "true" : undefined}
132
+ data-block={!unstyled && block ? "true" : undefined}
133
+ data-with-dot={!unstyled && dot ? "true" : undefined}
134
+ data-dismissible="true"
135
+ {...rest}
136
+ >
137
+ {#if href}
138
+ <a {href} {target} class="stuic-pill-main">
139
+ {@render body()}
140
+ </a>
141
+ {:else if onclick}
142
+ <button type="button" class="stuic-pill-main" {onclick} {disabled}>
143
+ {@render body()}
144
+ </button>
145
+ {:else}
146
+ {@render body()}
147
+ {/if}
148
+ {@render dismissBtn()}
149
+ </span>
150
+ {:else if href}
151
+ <a
152
+ {href}
153
+ {target}
154
+ bind:this={el}
155
+ class={_class}
156
+ data-intent={!unstyled ? intent : undefined}
157
+ data-variant={!unstyled ? variant : undefined}
158
+ data-size={!unstyled ? size : undefined}
159
+ data-muted={!unstyled && muted ? "true" : undefined}
160
+ data-active={!unstyled && active ? "true" : undefined}
161
+ data-rounded-full={!unstyled && roundedFull ? "true" : undefined}
162
+ data-block={!unstyled && block ? "true" : undefined}
163
+ data-with-dot={!unstyled && dot ? "true" : undefined}
164
+ data-interactive="true"
165
+ {...rest as HTMLAnchorAttributes}
166
+ >
167
+ {@render body()}
168
+ </a>
169
+ {:else if onclick}
170
+ <button
171
+ type="button"
172
+ bind:this={el}
173
+ class={_class}
174
+ data-intent={!unstyled ? intent : undefined}
175
+ data-variant={!unstyled ? variant : undefined}
176
+ data-size={!unstyled ? size : undefined}
177
+ data-muted={!unstyled && muted ? "true" : undefined}
178
+ data-active={!unstyled && active ? "true" : undefined}
179
+ data-rounded-full={!unstyled && roundedFull ? "true" : undefined}
180
+ data-block={!unstyled && block ? "true" : undefined}
181
+ data-with-dot={!unstyled && dot ? "true" : undefined}
182
+ data-interactive="true"
183
+ {onclick}
184
+ {disabled}
185
+ {...rest as HTMLButtonAttributes}
186
+ >
187
+ {@render body()}
188
+ </button>
189
+ {:else}
190
+ <span
191
+ bind:this={el}
192
+ class={_class}
193
+ data-intent={!unstyled ? intent : undefined}
194
+ data-variant={!unstyled ? variant : undefined}
195
+ data-size={!unstyled ? size : undefined}
196
+ data-muted={!unstyled && muted ? "true" : undefined}
197
+ data-active={!unstyled && active ? "true" : undefined}
198
+ data-rounded-full={!unstyled && roundedFull ? "true" : undefined}
199
+ data-block={!unstyled && block ? "true" : undefined}
200
+ data-with-dot={!unstyled && dot ? "true" : undefined}
201
+ {...rest}
202
+ >
203
+ {@render body()}
204
+ </span>
205
+ {/if}
@@ -0,0 +1,51 @@
1
+ import type { HTMLAttributes } from "svelte/elements";
2
+ import type { Snippet } from "svelte";
3
+ import type { IntentColorKey } from "../../utils/design-tokens.js";
4
+ import type { THC } from "../Thc/Thc.svelte";
5
+ export type PillVariant = "solid" | "outline" | "ghost" | "soft" | "link";
6
+ export type PillSize = "sm" | "md" | "lg";
7
+ export interface Props extends Omit<HTMLAttributes<HTMLElement>, "children"> {
8
+ /** Color intent (semantic meaning) */
9
+ intent?: IntentColorKey;
10
+ /** Visual variant (how colors are applied) */
11
+ variant?: PillVariant | string;
12
+ /** Size preset */
13
+ size?: PillSize | string;
14
+ /** Reduce emphasis (lower opacity) */
15
+ muted?: boolean;
16
+ /** Selected/active state — useful for filter-chip behavior */
17
+ active?: boolean;
18
+ /** Pill is fully rounded by default; set false to use element radius */
19
+ roundedFull?: boolean;
20
+ /** Render as block-level flex (full width). Inline-flex by default. */
21
+ block?: boolean;
22
+ /** Skip all default styling, use only custom classes */
23
+ unstyled?: boolean;
24
+ /** Additional CSS classes */
25
+ class?: string;
26
+ /** Render as anchor tag */
27
+ href?: string;
28
+ /** Link target (e.g. "_blank") — only relevant when href is set */
29
+ target?: string;
30
+ /** Render as button (when href not set) */
31
+ onclick?: (e: MouseEvent) => void;
32
+ /** Disabled (interactive variants only) */
33
+ disabled?: boolean;
34
+ /** Show built-in X dismiss control */
35
+ dismissible?: boolean;
36
+ /** Called when X is clicked. Stops propagation so parent onclick is unaffected. */
37
+ ondismiss?: (e: MouseEvent) => void;
38
+ /** Status dot rendered before content (uses current intent color) */
39
+ dot?: boolean;
40
+ /** Content rendered before children */
41
+ contentBefore?: THC;
42
+ /** Content rendered after children */
43
+ contentAfter?: THC;
44
+ /** Bindable element reference */
45
+ el?: HTMLElement;
46
+ /** Content snippet */
47
+ children?: Snippet;
48
+ }
49
+ declare const Pill: import("svelte").Component<Props, {}, "el">;
50
+ type Pill = ReturnType<typeof Pill>;
51
+ export default Pill;
@@ -0,0 +1,211 @@
1
+ # Pill
2
+
3
+ A small rounded inline element for tags, badges, status indicators, and filter chips. Polymorphic (renders as `<span>`, `<a>`, or `<button>` depending on props), with optional dismiss button, status dot, and content slots.
4
+
5
+ ## Props
6
+
7
+ | Prop | Type | Default | Description |
8
+ | --------------- | ------------------------------------------------------------------ | -------- | -------------------------------------------------------------------- |
9
+ | `intent` | `"primary" \| "accent" \| "destructive" \| "warning" \| "success"` | - | Semantic color intent |
10
+ | `variant` | `"solid" \| "outline" \| "ghost" \| "soft" \| "link"` | `"soft"` | Visual variant (how colors are applied) |
11
+ | `size` | `"sm" \| "md" \| "lg"` | `"md"` | Pill size |
12
+ | `muted` | `boolean` | `false` | Reduce emphasis (lower opacity) |
13
+ | `active` | `boolean` | `false` | Selected/active state (filter-chip behavior) |
14
+ | `roundedFull` | `boolean` | `true` | Fully rounded corners (9999px). Set `false` to use the element radius |
15
+ | `block` | `boolean` | `false` | Render as block-level flex (full width). `inline-flex` by default |
16
+ | `unstyled` | `boolean` | `false` | Skip all default styling |
17
+ | `href` | `string` | - | Render as `<a>` with this URL |
18
+ | `target` | `string` | - | Link target (only when `href` is set) |
19
+ | `onclick` | `(e: MouseEvent) => void` | - | Render as `<button>` with this handler (when no `href`) |
20
+ | `disabled` | `boolean` | - | Disabled state (interactive variants only) |
21
+ | `dismissible` | `boolean` | `false` | Show built-in X dismiss button |
22
+ | `ondismiss` | `(e: MouseEvent) => void` | - | Called when X is clicked. Stops propagation |
23
+ | `dot` | `boolean` | `false` | Status dot rendered before content |
24
+ | `contentBefore` | `THC` | - | Content rendered before children |
25
+ | `contentAfter` | `THC` | - | Content rendered after children |
26
+ | `el` | `HTMLElement` | - | Element reference (bindable) |
27
+ | `class` | `string` | - | Additional CSS classes |
28
+
29
+ ## Element Resolution
30
+
31
+ | Condition | Element |
32
+ | --------------------------------- | ------------------------------------------------------ |
33
+ | Default | `<span>` |
34
+ | `href` set | `<a>` |
35
+ | `onclick` set (no `href`) | `<button>` |
36
+ | `dismissible` set | `<span>` wrapper containing main element + X sibling |
37
+
38
+ ## Usage
39
+
40
+ ### Intent x Variant
41
+
42
+ ```svelte
43
+ <script lang="ts">
44
+ import { Pill } from "@marianmeres/stuic";
45
+ </script>
46
+
47
+ <Pill intent="primary">primary</Pill>
48
+ <Pill intent="success" variant="solid">success</Pill>
49
+ <Pill intent="destructive" variant="outline">destructive</Pill>
50
+ <Pill intent="warning" variant="ghost">warning</Pill>
51
+ <Pill intent="accent" variant="link">accent</Pill>
52
+ ```
53
+
54
+ ### Sizes
55
+
56
+ ```svelte
57
+ <Pill size="sm">Small</Pill>
58
+ <Pill size="md">Medium</Pill>
59
+ <Pill size="lg">Large</Pill>
60
+ ```
61
+
62
+ ### Polymorphic
63
+
64
+ ```svelte
65
+ <!-- Plain inline marker (span) -->
66
+ <Pill intent="primary">tag</Pill>
67
+
68
+ <!-- Anchor link -->
69
+ <Pill intent="accent" href="/profile">Profile</Pill>
70
+
71
+ <!-- Button -->
72
+ <Pill intent="success" onclick={() => console.log("clicked")}>
73
+ Click me
74
+ </Pill>
75
+ ```
76
+
77
+ ### Status Dot
78
+
79
+ ```svelte
80
+ <Pill intent="success" dot>Online</Pill>
81
+ <Pill intent="warning" dot>Idle</Pill>
82
+ <Pill intent="destructive" dot>Offline</Pill>
83
+ ```
84
+
85
+ ### Dismissible
86
+
87
+ ```svelte
88
+ <script lang="ts">
89
+ let tags = $state(["svelte", "tailwind", "stuic"]);
90
+ </script>
91
+
92
+ {#each tags as tag (tag)}
93
+ <Pill
94
+ intent="primary"
95
+ dismissible
96
+ ondismiss={() => (tags = tags.filter((x) => x !== tag))}
97
+ >
98
+ {tag}
99
+ </Pill>
100
+ {/each}
101
+ ```
102
+
103
+ `dismissible` works alongside `onclick` and `href` — the X button stops propagation so the parent click/navigation only fires for clicks outside the X.
104
+
105
+ ### Filter Chips (active state)
106
+
107
+ ```svelte
108
+ <script lang="ts">
109
+ let filters = $state(new Set<string>());
110
+ function toggle(f: string) {
111
+ filters.has(f) ? filters.delete(f) : filters.add(f);
112
+ filters = new Set(filters);
113
+ }
114
+ </script>
115
+
116
+ {#each ["new", "popular", "in-stock"] as opt}
117
+ <Pill
118
+ intent="primary"
119
+ variant="outline"
120
+ active={filters.has(opt)}
121
+ onclick={() => toggle(opt)}
122
+ >
123
+ {opt}
124
+ </Pill>
125
+ {/each}
126
+ ```
127
+
128
+ ### Icons via contentBefore / contentAfter
129
+
130
+ ```svelte
131
+ <Pill intent="success" contentBefore={{ html: iconCheck() }}>Verified</Pill>
132
+ <Pill intent="primary" contentAfter={{ html: iconArrowRight() }}>Next</Pill>
133
+ ```
134
+
135
+ `contentBefore` and `contentAfter` accept any `THC` value (string, `{ html }`, `{ text }`, component, snippet).
136
+
137
+ ### Block (full width)
138
+
139
+ ```svelte
140
+ <Pill intent="primary" block>Block-level pill</Pill>
141
+ ```
142
+
143
+ ### Custom Styling
144
+
145
+ ```svelte
146
+ <!-- Override radius inline -->
147
+ <Pill intent="primary" style="--stuic-pill-radius: 4px;">Squared</Pill>
148
+
149
+ <!-- Disable rounded-full default for element-radius -->
150
+ <Pill intent="primary" roundedFull={false}>Element radius</Pill>
151
+
152
+ <!-- Unstyled for full control -->
153
+ <Pill unstyled class="bg-gradient-to-r from-purple-500 to-pink-500 text-white px-3 py-1 rounded-full">
154
+ Custom
155
+ </Pill>
156
+ ```
157
+
158
+ ## CSS Variables
159
+
160
+ ### Component Tokens
161
+
162
+ | Variable | Default | Description |
163
+ | ----------------------------- | ---------------------- | --------------------- |
164
+ | `--stuic-pill-radius` | `--stuic-radius` | Border radius (overridden to `9999px` by `roundedFull`) |
165
+ | `--stuic-pill-font-family` | `--font-sans` | Font family |
166
+ | `--stuic-pill-font-weight` | `--font-weight-medium` | Font weight |
167
+ | `--stuic-pill-transition` | `--stuic-transition` | Transition duration |
168
+ | `--stuic-pill-border-width` | `--stuic-border-width` | Border width |
169
+ | `--stuic-pill-ring-width` | `2px` | Focus ring width |
170
+ | `--stuic-pill-ring-color` | `--stuic-color-ring` | Focus ring color |
171
+ | `--stuic-pill-gap` | `0.375rem` | Gap between dot/before/children/after/dismiss |
172
+ | `--stuic-pill-dot-size` | `0.5rem` | Status dot diameter |
173
+
174
+ ### Size Tokens
175
+
176
+ Each size (sm, md, lg) has corresponding tokens:
177
+
178
+ - `--stuic-pill-padding-x-{size}`
179
+ - `--stuic-pill-padding-y-{size}`
180
+ - `--stuic-pill-font-size-{size}`
181
+ - `--stuic-pill-min-height-{size}`
182
+
183
+ Dismissible pills override `padding-y` to `0` (the X button defines the height).
184
+
185
+ ### Intent Color Tokens
186
+
187
+ Pill reuses the global intent palette:
188
+
189
+ ```css
190
+ :root {
191
+ --stuic-color-primary: ...;
192
+ --stuic-color-primary-hover: ...;
193
+ --stuic-color-primary-foreground: ...;
194
+ /* + accent, destructive, warning, success */
195
+ }
196
+ ```
197
+
198
+ ## Data Attributes
199
+
200
+ The component uses data attributes for styling:
201
+
202
+ - `data-intent` - intent value
203
+ - `data-variant` - variant value
204
+ - `data-size` - size value
205
+ - `data-muted` - present when `muted`
206
+ - `data-active` - present when `active`
207
+ - `data-rounded-full` - present when `roundedFull`
208
+ - `data-block` - present when `block`
209
+ - `data-with-dot` - present when `dot`
210
+ - `data-dismissible` - present when `dismissible`
211
+ - `data-interactive` - present on `<a>` / `<button>` non-dismissible variants
@@ -0,0 +1,488 @@
1
+ /* ============================================================================
2
+ PILL COMPONENT TOKENS
3
+ Override globally: :root { --stuic-pill-radius: 4px; }
4
+ Override locally: <Pill style="--stuic-pill-radius: 4px;">
5
+ ============================================================================ */
6
+
7
+ /* prettier-ignore */
8
+ :root {
9
+ /* Component-level customization tokens */
10
+ --stuic-pill-font-family: var(--font-sans);
11
+ --stuic-pill-font-weight: var(--font-weight-medium);
12
+
13
+ /* Focus ring (interactive variants) */
14
+ --stuic-pill-ring-width: 2px;
15
+ --stuic-pill-ring-offset: 0px;
16
+ --stuic-pill-ring-color: var(--stuic-color-ring);
17
+
18
+ /* Layout */
19
+ --stuic-pill-gap: 0.375rem;
20
+
21
+ /* Status dot */
22
+ --stuic-pill-dot-size: 0.5rem;
23
+
24
+ /* Size: sm */
25
+ --stuic-pill-padding-x-sm: 0.5rem;
26
+ --stuic-pill-padding-y-sm: 0.125rem;
27
+ --stuic-pill-font-size-sm: 0.75rem;
28
+ --stuic-pill-min-height-sm: 1.25rem;
29
+
30
+ /* Size: md */
31
+ --stuic-pill-padding-x-md: 0.625rem;
32
+ --stuic-pill-padding-y-md: 0.1875rem;
33
+ --stuic-pill-font-size-md: 0.875rem;
34
+ --stuic-pill-min-height-md: 1.5rem;
35
+
36
+ /* Size: lg */
37
+ --stuic-pill-padding-x-lg: 0.875rem;
38
+ --stuic-pill-padding-y-lg: 0.3125rem;
39
+ --stuic-pill-font-size-lg: 1rem;
40
+ --stuic-pill-min-height-lg: 1.875rem;
41
+ }
42
+
43
+ @layer components {
44
+ /* ============================================================================
45
+ BASE STYLES
46
+ ============================================================================ */
47
+
48
+ .stuic-pill {
49
+ /* Layout */
50
+ display: inline-flex;
51
+ align-items: center;
52
+ justify-content: center;
53
+ gap: var(--stuic-pill-gap);
54
+ vertical-align: middle;
55
+
56
+ /* Typography */
57
+ font-family: var(--stuic-pill-font-family);
58
+ font-weight: var(--stuic-pill-font-weight);
59
+ line-height: 1;
60
+ text-align: center;
61
+ text-decoration: none;
62
+ white-space: nowrap;
63
+
64
+ /* Box model */
65
+ border-width: var(--stuic-pill-border-width, var(--stuic-border-width));
66
+ border-style: solid;
67
+ border-radius: var(--stuic-pill-radius, var(--stuic-radius));
68
+
69
+ /* Interaction */
70
+ user-select: none;
71
+ -webkit-tap-highlight-color: transparent;
72
+ touch-action: manipulation;
73
+ transition:
74
+ background var(--stuic-pill-transition, var(--stuic-transition)),
75
+ color var(--stuic-pill-transition, var(--stuic-transition)),
76
+ border-color var(--stuic-pill-transition, var(--stuic-transition)),
77
+ opacity var(--stuic-pill-transition, var(--stuic-transition));
78
+
79
+ /* Colors - use internal vars set by intent/variant */
80
+ background: var(--_bg);
81
+ color: var(--_text);
82
+ border-color: var(--_border);
83
+ }
84
+
85
+ /* Non-interactive default cursor */
86
+ span.stuic-pill {
87
+ cursor: default;
88
+ }
89
+
90
+ /* Interactive elements */
91
+ a.stuic-pill,
92
+ button.stuic-pill {
93
+ cursor: pointer;
94
+ }
95
+
96
+ /* Focus styles (interactive variants) */
97
+ a.stuic-pill:focus-visible,
98
+ button.stuic-pill:focus-visible {
99
+ outline: var(--stuic-pill-ring-width) solid
100
+ var(--_ring, var(--stuic-pill-ring-color));
101
+ outline-offset: var(--stuic-pill-ring-offset);
102
+ }
103
+
104
+ /* Hover state (interactive variants) */
105
+ a.stuic-pill:hover:not(:disabled),
106
+ button.stuic-pill:hover:not(:disabled) {
107
+ background: var(--_bg-hover);
108
+ color: var(--_text-hover);
109
+ border-color: var(--_border-hover);
110
+ }
111
+
112
+ /* Active state (interactive variants) */
113
+ a.stuic-pill:active:not(:disabled),
114
+ button.stuic-pill:active:not(:disabled) {
115
+ background: var(--_bg-active);
116
+ color: var(--_text-active);
117
+ border-color: var(--_border-active);
118
+ }
119
+
120
+ /* Disabled state */
121
+ .stuic-pill:disabled,
122
+ .stuic-pill[aria-disabled="true"] {
123
+ opacity: 0.5;
124
+ cursor: not-allowed;
125
+ }
126
+
127
+ /* ============================================================================
128
+ SIZE VARIANTS
129
+ ============================================================================ */
130
+
131
+ .stuic-pill[data-size="sm"] {
132
+ padding: var(--stuic-pill-padding-y-sm) var(--stuic-pill-padding-x-sm);
133
+ font-size: var(--stuic-pill-font-size-sm);
134
+ min-height: var(--stuic-pill-min-height-sm);
135
+ }
136
+
137
+ .stuic-pill[data-size="md"] {
138
+ padding: var(--stuic-pill-padding-y-md) var(--stuic-pill-padding-x-md);
139
+ font-size: var(--stuic-pill-font-size-md);
140
+ min-height: var(--stuic-pill-min-height-md);
141
+ }
142
+
143
+ .stuic-pill[data-size="lg"] {
144
+ padding: var(--stuic-pill-padding-y-lg) var(--stuic-pill-padding-x-lg);
145
+ font-size: var(--stuic-pill-font-size-lg);
146
+ min-height: var(--stuic-pill-min-height-lg);
147
+ }
148
+
149
+ /* Dismissible pills: drop vertical padding — the X button already provides
150
+ visual height, extra padding-y just makes them oversized. */
151
+ .stuic-pill[data-dismissible="true"][data-size="sm"],
152
+ .stuic-pill[data-dismissible="true"][data-size="md"],
153
+ .stuic-pill[data-dismissible="true"][data-size="lg"] {
154
+ padding-top: 0;
155
+ padding-bottom: 0;
156
+ }
157
+
158
+ /* ============================================================================
159
+ INTENT COLOR MAPPING
160
+ ============================================================================ */
161
+
162
+ .stuic-pill:not([data-intent]) {
163
+ --_color: var(--stuic-color-muted);
164
+ --_color-hover: var(--stuic-color-muted-hover);
165
+ --_color-active: var(--stuic-color-muted-active);
166
+ --_fg: var(--stuic-color-foreground);
167
+ --_fg-hover: var(--stuic-color-foreground-hover);
168
+ --_fg-active: var(--stuic-color-foreground-active);
169
+ }
170
+
171
+ .stuic-pill[data-intent="primary"] {
172
+ --_color: var(--stuic-color-primary);
173
+ --_color-hover: var(--stuic-color-primary-hover);
174
+ --_color-active: var(--stuic-color-primary-active);
175
+ --_fg: var(--stuic-color-primary-foreground);
176
+ --_fg-hover: var(--stuic-color-primary-foreground-hover);
177
+ --_fg-active: var(--stuic-color-primary-foreground-active);
178
+ }
179
+
180
+ .stuic-pill[data-intent="accent"] {
181
+ --_color: var(--stuic-color-accent);
182
+ --_color-hover: var(--stuic-color-accent-hover);
183
+ --_color-active: var(--stuic-color-accent-active);
184
+ --_fg: var(--stuic-color-accent-foreground);
185
+ --_fg-hover: var(--stuic-color-accent-foreground-hover);
186
+ --_fg-active: var(--stuic-color-accent-foreground-active);
187
+ }
188
+
189
+ .stuic-pill[data-intent="destructive"] {
190
+ --_color: var(--stuic-color-destructive);
191
+ --_color-hover: var(--stuic-color-destructive-hover);
192
+ --_color-active: var(--stuic-color-destructive-active);
193
+ --_fg: var(--stuic-color-destructive-foreground);
194
+ --_fg-hover: var(--stuic-color-destructive-foreground-hover);
195
+ --_fg-active: var(--stuic-color-destructive-foreground-active);
196
+ }
197
+
198
+ .stuic-pill[data-intent="warning"] {
199
+ --_color: var(--stuic-color-warning);
200
+ --_color-hover: var(--stuic-color-warning-hover);
201
+ --_color-active: var(--stuic-color-warning-active);
202
+ --_fg: var(--stuic-color-warning-foreground);
203
+ --_fg-hover: var(--stuic-color-warning-foreground-hover);
204
+ --_fg-active: var(--stuic-color-warning-foreground-active);
205
+ }
206
+
207
+ .stuic-pill[data-intent="success"] {
208
+ --_color: var(--stuic-color-success);
209
+ --_color-hover: var(--stuic-color-success-hover);
210
+ --_color-active: var(--stuic-color-success-active);
211
+ --_fg: var(--stuic-color-success-foreground);
212
+ --_fg-hover: var(--stuic-color-success-foreground-hover);
213
+ --_fg-active: var(--stuic-color-success-foreground-active);
214
+ }
215
+
216
+ /* ============================================================================
217
+ VARIANT STYLES
218
+ ============================================================================ */
219
+
220
+ /* Solid: filled background with contrasting text */
221
+ .stuic-pill[data-variant="solid"] {
222
+ --_bg: var(--_color);
223
+ --_bg-hover: var(--_color-hover);
224
+ --_bg-active: var(--_color-active);
225
+ --_text: var(--_fg);
226
+ --_text-hover: var(--_fg-hover);
227
+ --_text-active: var(--_fg-active);
228
+ --_border: var(--_color);
229
+ --_border-hover: var(--_color-hover);
230
+ --_border-active: var(--_color-active);
231
+ }
232
+
233
+ /* Outline: transparent background with colored border and text */
234
+ .stuic-pill[data-variant="outline"] {
235
+ --_bg: transparent;
236
+ --_bg-hover: color-mix(in srgb, var(--_color) 10%, var(--stuic-color-background));
237
+ --_bg-active: color-mix(in srgb, var(--_color) 20%, var(--stuic-color-background));
238
+ --_text: var(--_color);
239
+ --_text-hover: var(--_color-hover);
240
+ --_text-active: var(--_color-active);
241
+ --_border: var(--_color);
242
+ --_border-hover: var(--_color-hover);
243
+ --_border-active: var(--_color-active);
244
+ }
245
+
246
+ /* Ghost: transparent background, colored text, subtle hover state */
247
+ .stuic-pill[data-variant="ghost"] {
248
+ --_bg: transparent;
249
+ --_bg-hover: color-mix(in srgb, var(--_color) 10%, var(--stuic-color-background));
250
+ --_bg-active: color-mix(in srgb, var(--_color) 20%, var(--stuic-color-background));
251
+ --_text: var(--_color);
252
+ --_text-hover: var(--_color-hover);
253
+ --_text-active: var(--_color-active);
254
+ --_border: transparent;
255
+ --_border-hover: transparent;
256
+ --_border-active: transparent;
257
+ }
258
+
259
+ /* Soft (default): muted background tint with colored text */
260
+ .stuic-pill[data-variant="soft"],
261
+ .stuic-pill:not([data-variant]) {
262
+ --_bg: color-mix(in srgb, var(--_color) 15%, var(--stuic-color-background));
263
+ --_bg-hover: color-mix(in srgb, var(--_color) 25%, var(--stuic-color-background));
264
+ --_bg-active: color-mix(in srgb, var(--_color) 35%, var(--stuic-color-background));
265
+ --_text: var(--_color);
266
+ --_text-hover: var(--_color-hover);
267
+ --_text-active: var(--_color-active);
268
+ --_border: transparent;
269
+ --_border-hover: transparent;
270
+ --_border-active: transparent;
271
+ }
272
+
273
+ /* Link: appears as inline text link, minimal styling */
274
+ .stuic-pill[data-variant="link"] {
275
+ --_bg: transparent;
276
+ --_bg-hover: transparent;
277
+ --_bg-active: transparent;
278
+ --_text: var(--_color);
279
+ --_text-hover: var(--_color-hover);
280
+ --_text-active: var(--_color-active);
281
+ --_border: transparent;
282
+ --_border-hover: transparent;
283
+ --_border-active: transparent;
284
+ text-decoration: underline;
285
+ text-underline-offset: 2px;
286
+ min-height: auto;
287
+ padding: 0;
288
+ }
289
+
290
+ /* ============================================================================
291
+ DEFAULT INTENT OVERRIDES
292
+ The default intent uses neutral role tokens — adjust per variant for legibility.
293
+ ============================================================================ */
294
+
295
+ .stuic-pill:not([data-intent])[data-variant="solid"] {
296
+ --_text: var(--stuic-color-foreground);
297
+ --_text-hover: var(--stuic-color-foreground-hover);
298
+ --_text-active: var(--stuic-color-foreground-active);
299
+ --_border: var(--stuic-color-border-hover);
300
+ --_border-hover: var(--stuic-color-border-active);
301
+ --_border-active: var(--stuic-color-border-active);
302
+ }
303
+
304
+ .stuic-pill:not([data-intent])[data-variant="outline"] {
305
+ --_text: var(--stuic-color-foreground);
306
+ --_text-hover: var(--stuic-color-foreground-hover);
307
+ --_text-active: var(--stuic-color-foreground-active);
308
+ --_border: var(--stuic-color-border-hover);
309
+ --_border-hover: var(--stuic-color-border-hover);
310
+ --_border-active: var(--stuic-color-border-active);
311
+ --_bg-hover: var(--stuic-color-muted-hover);
312
+ --_bg-active: var(--stuic-color-muted-active);
313
+ }
314
+
315
+ .stuic-pill:not([data-intent])[data-variant="ghost"] {
316
+ --_text: var(--stuic-color-foreground);
317
+ --_text-hover: var(--stuic-color-foreground-hover);
318
+ --_text-active: var(--stuic-color-foreground-active);
319
+ --_bg-hover: var(--stuic-color-muted-hover);
320
+ --_bg-active: var(--stuic-color-muted-active);
321
+ }
322
+
323
+ .stuic-pill:not([data-intent])[data-variant="soft"],
324
+ .stuic-pill:not([data-intent]):not([data-variant]) {
325
+ --_bg: var(--stuic-color-muted);
326
+ --_bg-hover: var(--stuic-color-muted-hover);
327
+ --_bg-active: var(--stuic-color-muted-active);
328
+ --_text: var(--stuic-color-foreground);
329
+ --_text-hover: var(--stuic-color-foreground-hover);
330
+ --_text-active: var(--stuic-color-foreground-active);
331
+ }
332
+
333
+ .stuic-pill:not([data-intent])[data-variant="link"] {
334
+ --_text: var(--stuic-color-muted-foreground);
335
+ --_text-hover: var(--stuic-color-foreground-hover);
336
+ --_text-active: var(--stuic-color-foreground-active);
337
+ }
338
+
339
+ /* ============================================================================
340
+ MUTED MODIFIER
341
+ ============================================================================ */
342
+
343
+ .stuic-pill[data-muted="true"] {
344
+ opacity: 0.7;
345
+ }
346
+
347
+ a.stuic-pill[data-muted="true"]:hover:not(:disabled),
348
+ button.stuic-pill[data-muted="true"]:hover:not(:disabled) {
349
+ opacity: 0.85;
350
+ }
351
+
352
+ /* ============================================================================
353
+ ACTIVE (selected) MODIFIER
354
+ Slight emphasis — useful for filter-chip toggleable behavior.
355
+ ============================================================================ */
356
+
357
+ .stuic-pill[data-active="true"][data-variant="outline"],
358
+ .stuic-pill[data-active="true"][data-variant="ghost"],
359
+ .stuic-pill[data-active="true"][data-variant="soft"] {
360
+ --_bg: color-mix(in srgb, var(--_color) 25%, var(--stuic-color-background));
361
+ --_border: var(--_color);
362
+ }
363
+
364
+ .stuic-pill[data-active="true"][data-variant="solid"] {
365
+ --_bg: var(--_color-active);
366
+ --_border: var(--_color-active);
367
+ }
368
+
369
+ /* ============================================================================
370
+ ROUNDED FULL (default)
371
+ ============================================================================ */
372
+
373
+ .stuic-pill[data-rounded-full="true"] {
374
+ --stuic-pill-radius: 9999px;
375
+ }
376
+
377
+ /* ============================================================================
378
+ BLOCK MODIFIER
379
+ Block-level flex (full width) instead of the default inline-flex.
380
+ ============================================================================ */
381
+
382
+ .stuic-pill[data-block="true"] {
383
+ display: flex;
384
+ width: 100%;
385
+ }
386
+
387
+ /* ============================================================================
388
+ STATUS DOT
389
+ ============================================================================ */
390
+
391
+ .stuic-pill-dot {
392
+ display: inline-block;
393
+ width: var(--stuic-pill-dot-size);
394
+ height: var(--stuic-pill-dot-size);
395
+ border-radius: 9999px;
396
+ background: var(--_color);
397
+ flex-shrink: 0;
398
+ }
399
+
400
+ /* On solid variants the bg already IS the intent color — use foreground instead. */
401
+ .stuic-pill[data-variant="solid"] .stuic-pill-dot {
402
+ background: var(--_text);
403
+ }
404
+
405
+ /* ============================================================================
406
+ DISMISSIBLE WRAPPER
407
+ When dismissible, the outer span is the styled pill; inner main + dismiss
408
+ button are siblings. Strip default styling from the inner interactive area.
409
+ ============================================================================ */
410
+
411
+ .stuic-pill[data-dismissible="true"] {
412
+ gap: var(--stuic-pill-gap);
413
+ }
414
+
415
+ .stuic-pill-main {
416
+ display: inline-flex;
417
+ align-items: center;
418
+ justify-content: center;
419
+ gap: var(--stuic-pill-gap);
420
+
421
+ /* Reset native button/anchor defaults */
422
+ appearance: none;
423
+ background: transparent;
424
+ border: 0;
425
+ padding: 0;
426
+ margin: 0;
427
+ font: inherit;
428
+ color: inherit;
429
+ text-decoration: none;
430
+ cursor: pointer;
431
+ line-height: 1;
432
+ }
433
+
434
+ .stuic-pill-main:focus-visible {
435
+ outline: var(--stuic-pill-ring-width) solid
436
+ var(--_ring, var(--stuic-pill-ring-color));
437
+ outline-offset: 2px;
438
+ border-radius: 2px;
439
+ }
440
+
441
+ .stuic-pill-main:disabled {
442
+ cursor: not-allowed;
443
+ }
444
+
445
+ /* Dismiss X button */
446
+ .stuic-pill-dismiss {
447
+ display: inline-flex;
448
+ align-items: center;
449
+ justify-content: center;
450
+ appearance: none;
451
+ background: transparent;
452
+ border: 0;
453
+ padding: 0;
454
+ margin: 0;
455
+ color: inherit;
456
+ cursor: pointer;
457
+ opacity: 0.7;
458
+ transition: opacity var(--stuic-pill-transition, var(--stuic-transition));
459
+ flex-shrink: 0;
460
+ }
461
+
462
+ .stuic-pill-dismiss:hover:not(:disabled) {
463
+ opacity: 1;
464
+ }
465
+
466
+ .stuic-pill-dismiss:focus-visible {
467
+ outline: var(--stuic-pill-ring-width) solid
468
+ var(--_ring, var(--stuic-pill-ring-color));
469
+ outline-offset: 1px;
470
+ border-radius: 9999px;
471
+ }
472
+
473
+ .stuic-pill-dismiss:disabled {
474
+ opacity: 0.4;
475
+ cursor: not-allowed;
476
+ }
477
+
478
+ .stuic-pill-dismiss svg {
479
+ width: 1em;
480
+ height: 1em;
481
+ }
482
+
483
+ /* Tighten dismiss icon size per pill size */
484
+ .stuic-pill[data-size="sm"] .stuic-pill-dismiss svg {
485
+ width: 0.875em;
486
+ height: 0.875em;
487
+ }
488
+ }
@@ -0,0 +1 @@
1
+ export { default as Pill, type Props as PillProps, type PillVariant, type PillSize, } from "./Pill.svelte";
@@ -0,0 +1 @@
1
+ export { default as Pill, } from "./Pill.svelte";
@@ -195,7 +195,6 @@
195
195
  onButtonClick={(i) => {
196
196
  billingPeriod = i === 0 ? "monthly" : "annual";
197
197
  }}
198
- {unstyled}
199
198
  class={classToggleProp}
200
199
  style="
201
200
  width: auto;
@@ -229,7 +228,6 @@
229
228
  class={tierClass}
230
229
  data-highlighted={!unstyled && tier.highlighted ? "" : undefined}
231
230
  data-disabled={!unstyled && tier.disabled ? "" : undefined}
232
- aria-disabled={tier.disabled ? "true" : undefined}
233
231
  role="listitem"
234
232
  >
235
233
  {#if tier.highlighted && tier.highlightedBadge}
@@ -193,7 +193,7 @@
193
193
 
194
194
  <form bind:this={el} class={_class} use:onSubmitValidityCheck {...rest}>
195
195
  <!-- General error alert -->
196
- <DismissibleMessage message={error} intent="destructive" onDismiss={false} />
196
+ <DismissibleMessage message={error} intent="destructive" />
197
197
 
198
198
  <!-- Top-position extra fields -->
199
199
  {#each topFields as cfg (cfg.name)}
package/dist/index.css CHANGED
@@ -84,6 +84,7 @@ In practice:
84
84
  @import "./components/Nav/index.css";
85
85
  @import "./components/Notifications/index.css";
86
86
  @import "./components/OtpInput/index.css";
87
+ @import "./components/Pill/index.css";
87
88
  @import "./components/PricingTable/index.css";
88
89
  @import "./components/Progress/index.css";
89
90
  @import "./components/Separator/index.css";
package/dist/index.d.ts CHANGED
@@ -60,6 +60,7 @@ export * from "./components/ModalDialog/index.js";
60
60
  export * from "./components/Nav/index.js";
61
61
  export * from "./components/Notifications/index.js";
62
62
  export * from "./components/OtpInput/index.js";
63
+ export * from "./components/Pill/index.js";
63
64
  export * from "./components/PricingTable/index.js";
64
65
  export * from "./components/Progress/index.js";
65
66
  export * from "./components/Separator/index.js";
package/dist/index.js CHANGED
@@ -61,6 +61,7 @@ export * from "./components/ModalDialog/index.js";
61
61
  export * from "./components/Nav/index.js";
62
62
  export * from "./components/Notifications/index.js";
63
63
  export * from "./components/OtpInput/index.js";
64
+ export * from "./components/Pill/index.js";
64
65
  export * from "./components/PricingTable/index.js";
65
66
  export * from "./components/Progress/index.js";
66
67
  export * from "./components/Separator/index.js";
package/dist/mcp.js CHANGED
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck - Deno-style imports for @marianmeres/mcp-server discovery
1
2
  import { z } from "npm:zod";
2
3
  import { generateThemeCss } from "./utils/design-tokens.js";
3
4
  import { hexToOklch, hexToRgb } from "./utils/colors.js";
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- 55 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
5
+ 56 Svelte 5 component directories with consistent API patterns. All use runes-based reactivity.
6
6
 
7
7
  ## Component Categories
8
8
 
@@ -72,6 +72,7 @@
72
72
  | Component | Purpose |
73
73
  | --------------- | ------------------------------------------------------------------- |
74
74
  | Avatar | User avatars with fallback |
75
+ | Pill | Inline rounded badge/tag/chip (intent + variant + size, dismissible, dot, polymorphic span/a/button) |
75
76
  | KbdShortcut | Keyboard shortcut hints |
76
77
  | Carousel | Image/content slider with snap, keyboard nav, wheel scroll, arrows |
77
78
  | ListItemButton | List item with actions |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "3.73.0",
3
+ "version": "3.75.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",