From 6a615f78d9a9f27c0c533ec7b033b867944a0ca3 Mon Sep 17 00:00:00 2001 From: Tavian Taylor Date: Thu, 19 Mar 2026 12:28:51 +0000 Subject: [PATCH 1/8] Update svelte components to accept objects and arrays --- packages/stacks-svelte/package.json | 1 + .../ActivityIndicator.stories.svelte | 17 ++++++- .../ActivityIndicator.svelte | 30 ++++------- .../components/Avatar/Avatar.stories.svelte | 21 ++++++-- .../src/components/Avatar/Avatar.svelte | 20 +++----- .../src/components/Badge/Badge.stories.svelte | 12 ++++- .../src/components/Badge/Badge.svelte | 50 +++++++++++-------- .../src/components/Bling/Bling.stories.svelte | 36 +++++++++---- .../src/components/Bling/Bling.svelte | 30 ++++------- .../components/Button/Button.stories.svelte | 15 ++++-- .../src/components/Button/Button.svelte | 40 +++++---------- .../Checkbox/Checkbox.stories.svelte | 34 +++++++++---- .../src/components/Checkbox/Checkbox.svelte | 21 +++----- .../src/components/FormGroup/FormGroup.svelte | 21 +++----- packages/stacks-svelte/src/storybook-utils.ts | 25 ++++++++++ 15 files changed, 213 insertions(+), 160 deletions(-) create mode 100644 packages/stacks-svelte/src/storybook-utils.ts diff --git a/packages/stacks-svelte/package.json b/packages/stacks-svelte/package.json index c085ba707e..9c369aae84 100644 --- a/packages/stacks-svelte/package.json +++ b/packages/stacks-svelte/package.json @@ -33,6 +33,7 @@ "svelte": "^5.0.0" }, "dependencies": { + "clsx": "^2.1.1", "@floating-ui/core": "^1.7.3", "@stackoverflow/stacks-icons": "^7.0.0-beta.18", "@stackoverflow/stacks-icons-legacy": "npm:@stackoverflow/stacks-icons@^6.9.0", diff --git a/packages/stacks-svelte/src/components/ActivityIndicator/ActivityIndicator.stories.svelte b/packages/stacks-svelte/src/components/ActivityIndicator/ActivityIndicator.stories.svelte index b64849d6dc..8b4d3d4b20 100644 --- a/packages/stacks-svelte/src/components/ActivityIndicator/ActivityIndicator.stories.svelte +++ b/packages/stacks-svelte/src/components/ActivityIndicator/ActivityIndicator.stories.svelte @@ -1,5 +1,6 @@ - + + {#snippet template({ class: classArg, ...args })} + + {/snippet} +
diff --git a/packages/stacks-svelte/src/components/ActivityIndicator/ActivityIndicator.svelte b/packages/stacks-svelte/src/components/ActivityIndicator/ActivityIndicator.svelte index e458c3e2b7..8b442107cf 100644 --- a/packages/stacks-svelte/src/components/ActivityIndicator/ActivityIndicator.svelte +++ b/packages/stacks-svelte/src/components/ActivityIndicator/ActivityIndicator.svelte @@ -1,4 +1,7 @@ @@ -29,7 +32,7 @@ /** * Additional CSS classes added to the element */ - class?: string; + class?: ClassValue; } const { @@ -40,27 +43,12 @@ class: className = "", }: Props = $props(); - const getClasses = ( - className: string, - variant: Variant, - size: Size - ): string => { + const getClasses = (className: ClassValue, variant: Variant, size: Size) => { const base = "s-activity-indicator"; - let classes = base; - - if (variant) { - classes += ` ${base}__${variant}`; - } - - if (size) { - classes += ` ${base}__${size}`; - } - - if (className) { - classes += ` ${className}`; - } - - return classes; + const classes = [variant, size] + .filter(Boolean) + .map((modifier) => `${base}__${modifier}`); + return clsx(base, className, classes); }; const classes = $derived(getClasses(className, variant, size)); diff --git a/packages/stacks-svelte/src/components/Avatar/Avatar.stories.svelte b/packages/stacks-svelte/src/components/Avatar/Avatar.stories.svelte index b77285308b..fd61bc8152 100644 --- a/packages/stacks-svelte/src/components/Avatar/Avatar.stories.svelte +++ b/packages/stacks-svelte/src/components/Avatar/Avatar.stories.svelte @@ -1,6 +1,8 @@ - + + {#snippet template({ class: classArg, ...args })} + + {/snippet} +
diff --git a/packages/stacks-svelte/src/components/Avatar/Avatar.svelte b/packages/stacks-svelte/src/components/Avatar/Avatar.svelte index f839fbbd02..8e66f5f00a 100644 --- a/packages/stacks-svelte/src/components/Avatar/Avatar.svelte +++ b/packages/stacks-svelte/src/components/Avatar/Avatar.svelte @@ -3,10 +3,11 @@ - {#snippet template({ text, ...args })} - + {#snippet template({ text, class: classArg, ...args })} + {/snippet} diff --git a/packages/stacks-svelte/src/components/Badge/Badge.svelte b/packages/stacks-svelte/src/components/Badge/Badge.svelte index 643964bfe7..5a1250f9d1 100644 --- a/packages/stacks-svelte/src/components/Badge/Badge.svelte +++ b/packages/stacks-svelte/src/components/Badge/Badge.svelte @@ -27,8 +27,11 @@ - + {#if needsBling()} import { defineMeta } from "@storybook/addon-svelte-csf"; + import { parseClassValue } from "../../storybook-utils"; import Bling, { type Type, type Size } from "./Bling.svelte"; + const BlingSizes: Size[] = ["sm", "", "lg"]; const BlingTypes: Type[] = [ "", @@ -14,21 +16,33 @@ const { Story } = defineMeta({ title: "Components/Bling", component: Bling, + argTypes: { + type: { + control: "select", + options: BlingTypes, + }, + size: { + control: "select", + options: BlingSizes, + }, + filled: { + control: "boolean", + }, + class: { + control: "text", + }, + }, + args: { + name: "Bling", + }, }); - - {#snippet template(args)} + + {#snippet template({ class: classArg, ...args })} {/snippet} diff --git a/packages/stacks-svelte/src/components/Bling/Bling.svelte b/packages/stacks-svelte/src/components/Bling/Bling.svelte index 51f956e519..928b9d33f3 100644 --- a/packages/stacks-svelte/src/components/Bling/Bling.svelte +++ b/packages/stacks-svelte/src/components/Bling/Bling.svelte @@ -4,6 +4,9 @@ - {#snippet template({ children, ...args })} - + {#snippet template({ children, class: classArg, ...args })} + {/snippet} diff --git a/packages/stacks-svelte/src/components/Button/Button.svelte b/packages/stacks-svelte/src/components/Button/Button.svelte index dd264e98ae..5d4fb226c7 100644 --- a/packages/stacks-svelte/src/components/Button/Button.svelte +++ b/packages/stacks-svelte/src/components/Button/Button.svelte @@ -1,5 +1,6 @@ - - {#snippet template(args)} + + {#snippet template({ class: classArg, ...args })}
- +
{/snippet}
diff --git a/packages/stacks-svelte/src/components/Checkbox/Checkbox.svelte b/packages/stacks-svelte/src/components/Checkbox/Checkbox.svelte index 659e7abe93..2f6b0d6e6b 100644 --- a/packages/stacks-svelte/src/components/Checkbox/Checkbox.svelte +++ b/packages/stacks-svelte/src/components/Checkbox/Checkbox.svelte @@ -4,7 +4,9 @@ @@ -51,7 +51,9 @@ {#snippet template({ children, class: classArg, ...args })} diff --git a/packages/stacks-svelte/src/components/Checkbox/Checkbox.stories.svelte b/packages/stacks-svelte/src/components/Checkbox/Checkbox.stories.svelte index dfabcae7d1..fce867cf7d 100644 --- a/packages/stacks-svelte/src/components/Checkbox/Checkbox.stories.svelte +++ b/packages/stacks-svelte/src/components/Checkbox/Checkbox.stories.svelte @@ -70,7 +70,9 @@
{/snippet} diff --git a/packages/stacks-svelte/src/storybook-utils.ts b/packages/stacks-svelte/src/storybook-utils.ts index c42387a42c..f8225c5527 100644 --- a/packages/stacks-svelte/src/storybook-utils.ts +++ b/packages/stacks-svelte/src/storybook-utils.ts @@ -4,7 +4,9 @@ import type { ClassValue } from "svelte/elements"; * Infers ClassValue from a Storybook control string: JSON array/object → parsed; otherwise string. * Use in story templates so the class control accepts plain strings or JSON (e.g. ["wmx2"] or {"wmx2":true}). */ -export function parseClassValue(input: string | undefined): ClassValue | undefined { +export function parseClassValue( + input: string | undefined +): ClassValue | undefined { const s = typeof input === "string" ? input.trim() : ""; if (!s) return undefined; if (s.startsWith("[") && s.endsWith("]")) { From c01d51696a5a8d59186d0b150a3f972d0eaddae6 Mon Sep 17 00:00:00 2001 From: Tavian Taylor Date: Fri, 20 Mar 2026 15:58:55 +0000 Subject: [PATCH 3/8] update class in svelte components to allow objects and arrays --- .../src/components/Icon/Icon.stories.svelte | 16 ++++++++- .../src/components/Icon/Icon.svelte | 9 +++-- .../src/components/Label/Label.svelte | 21 +++++------ .../src/components/Link/Link.stories.svelte | 13 +++++-- .../src/components/Link/Link.svelte | 27 ++++++-------- .../src/components/Menu/Menu.stories.svelte | 13 +++++-- .../src/components/Menu/Menu.svelte | 14 +++----- .../src/components/Modal/Modal.stories.svelte | 8 +++-- .../src/components/Modal/Modal.svelte | 14 +++----- .../components/Notice/Notice.stories.svelte | 15 ++++++-- .../src/components/Notice/Notice.svelte | 25 +++++-------- .../components/Popover/PopoverContent.svelte | 36 +++++++++---------- .../PostSummary/PostSummary.stories.svelte | 12 +++++-- .../components/PostSummary/PostSummary.svelte | 21 +++++------ .../src/components/RadioGroup/Radio.svelte | 21 +++++------ .../RadioGroup/RadioGroup.stories.svelte | 13 +++++-- .../src/components/Tag/Tag.stories.svelte | 7 +++- .../src/components/Tag/Tag.svelte | 30 ++++++---------- .../TextArea/TextArea.stories.svelte | 10 +++++- .../src/components/TextArea/TextArea.svelte | 24 +++++-------- .../TextInput/TextInput.stories.svelte | 15 +++++++- .../src/components/TextInput/TextInput.svelte | 24 +++++-------- .../UserCard/UserCard.stories.svelte | 12 +++++-- .../src/components/UserCard/UserCard.svelte | 31 ++++++++-------- .../UserCard/UserCardAdditionalBling.svelte | 14 +++----- .../src/components/Vote/Vote.stories.svelte | 11 ++++-- .../src/components/Vote/Vote.svelte | 30 +++++++--------- 27 files changed, 259 insertions(+), 227 deletions(-) diff --git a/packages/stacks-svelte/src/components/Icon/Icon.stories.svelte b/packages/stacks-svelte/src/components/Icon/Icon.stories.svelte index 73efd74e57..ec2a21d0f9 100644 --- a/packages/stacks-svelte/src/components/Icon/Icon.stories.svelte +++ b/packages/stacks-svelte/src/components/Icon/Icon.stories.svelte @@ -1,5 +1,6 @@ - + + {#snippet template({ class: classArg, ...args })} + + {/snippet} + + import clsx from "clsx"; + import type { ClassValue } from "svelte/elements"; + interface Props { /** * The source of the SVG @@ -18,7 +21,7 @@ /** * Additional CSS classes added to the SVG element */ - class?: string; + class?: ClassValue; } const { @@ -32,7 +35,7 @@ src: string, title: string, native: boolean, - className: string + className: ClassValue ) => { let svg = src; @@ -49,7 +52,7 @@ // prepend custom classes to the classes the SVG already had if (className) { - svg = svg.replace(/class="/, 'class="' + className + " "); + svg = svg.replace(/class="/, 'class="' + clsx(className) + " "); } return svg; diff --git a/packages/stacks-svelte/src/components/Label/Label.svelte b/packages/stacks-svelte/src/components/Label/Label.svelte index ad8fe750bd..6ec08b08f6 100644 --- a/packages/stacks-svelte/src/components/Label/Label.svelte +++ b/packages/stacks-svelte/src/components/Label/Label.svelte @@ -4,7 +4,9 @@ - {#snippet template({ children, ...args })} - {children ?? "Click me"} + {#snippet template({ children, class: classArg, ...args })} + + {children ?? "Click me"} + {/snippet} diff --git a/packages/stacks-svelte/src/components/Link/Link.svelte b/packages/stacks-svelte/src/components/Link/Link.svelte index fa5ea8ae2e..9761603501 100644 --- a/packages/stacks-svelte/src/components/Link/Link.svelte +++ b/packages/stacks-svelte/src/components/Link/Link.svelte @@ -3,8 +3,10 @@ @@ -39,10 +45,13 @@ {/snippet} - {#snippet template()} + {#snippet template({ class: classArg, children: _storyChildren, ...args })} - + {@render basicChildren()} diff --git a/packages/stacks-svelte/src/components/Menu/Menu.svelte b/packages/stacks-svelte/src/components/Menu/Menu.svelte index c94db7399f..dd36633ac1 100644 --- a/packages/stacks-svelte/src/components/Menu/Menu.svelte +++ b/packages/stacks-svelte/src/components/Menu/Menu.svelte @@ -3,13 +3,15 @@ @@ -23,7 +27,7 @@ - {#snippet template(args)} + {#snippet template({ class: classArg, ...args })}