From ef4270278867f336e37fc61b25cde03caced30e1 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 25 Nov 2025 10:18:09 -0500
Subject: [PATCH 01/44] shine changes in classic
---
.../input_textarea/input_textarea.less | 30 ++++---------------
packages/stacks-docs/_data/inputs.json | 16 ++--------
2 files changed, 9 insertions(+), 37 deletions(-)
diff --git a/packages/stacks-classic/lib/components/input_textarea/input_textarea.less b/packages/stacks-classic/lib/components/input_textarea/input_textarea.less
index b023847389..b2d9aca845 100644
--- a/packages/stacks-classic/lib/components/input_textarea/input_textarea.less
+++ b/packages/stacks-classic/lib/components/input_textarea/input_textarea.less
@@ -8,7 +8,7 @@
--_in-fs: var(--fs-body1);
--_in-o: unset;
--_in-px: 0.7em;
- --_in-py: 0.6em;
+ --_in-py: var(--su8);
--_in-placeholder-fc: var(--black-300);
// CONTEXTUAL STYLES
@@ -56,29 +56,19 @@
// Sizes
&&__sm {
.size-styles(sm; in; @styles: fs);
+ --_in-py: calc(var(--su6) - var(--su1)); // 5px
}
- &&__md {
+ &&__lg {
@supports (-webkit-overflow-scrolling: touch) {
--_in-fs: calc(var(--su-static16) + var(--su-static1)); // 17px
--_in-py: 0.4em;
}
.size-styles(md; in; @styles: br, fs);
+ --_in-py: calc(var(--su8) + var(--su1)); // 9px
}
-
- &&__lg {
- .size-styles(lg; in; @styles: br, fs);
- --_in-px: 0.6em;
- --_in-py: 0.45em;
- }
-
- &&__xl {
- .size-styles(xl; in; @styles: br, fs);
- --_in-px: 0.5em;
- --_in-py: 0.4em;
- }
-
+
// CHILD ELEMENTS
.webkit-autofill();
@@ -112,7 +102,7 @@
font-size: var(--_in-fs);
opacity: var(--_in-o);
padding: var(--_in-py) var(--_in-px) var(--_in-py) var(--_in-pl, var(--_in-px));
-
+ line-height: var(--lh-md);
-webkit-appearance: none; // Removes shadows we don't want in mobile Safari
font-family: inherit;
margin: 0; // A guard against Core's default margins
@@ -123,10 +113,6 @@
.input-states({
padding-right: var(--su32);
});
-
- &&__md {
- --_in-py: 0.5em;
- }
}
.s-textarea {
@@ -134,10 +120,6 @@
padding-right: var(--su48);
});
- &&__md {
- --_in-py: 0.72em;
- }
-
~ .s-input-icon {
right: 1.5em;
top: 1.5em;
diff --git a/packages/stacks-docs/_data/inputs.json b/packages/stacks-docs/_data/inputs.json
index 4eaf7b1c18..b0456905de 100644
--- a/packages/stacks-docs/_data/inputs.json
+++ b/packages/stacks-docs/_data/inputs.json
@@ -71,28 +71,18 @@
"sizes": [
{
"name": "Small",
- "size": "12px",
+ "size": "13px",
"class": "s-input__sm"
},
{
"name": "Default",
- "size": "13px",
+ "size": "14px",
"class": "N/A"
},
- {
- "name": "Medium",
- "size": "17px",
- "class": "s-input__md"
- },
{
"name": "Large",
- "size": "21px",
+ "size": "18px",
"class": "s-input__lg"
- },
- {
- "name": "Extra Large",
- "size": "27px",
- "class": "s-input__xl"
}
]
},
From 29ad4f5b428f381c6cc8985faf484078462d7169 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 25 Nov 2025 10:38:29 -0500
Subject: [PATCH 02/44] svelte updates
---
.../src/components/TextInput/TextInput.stories.svelte | 2 +-
.../stacks-svelte/src/components/TextInput/TextInput.svelte | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/packages/stacks-svelte/src/components/TextInput/TextInput.stories.svelte b/packages/stacks-svelte/src/components/TextInput/TextInput.stories.svelte
index e3d0b9f183..ef6f39a782 100644
--- a/packages/stacks-svelte/src/components/TextInput/TextInput.stories.svelte
+++ b/packages/stacks-svelte/src/components/TextInput/TextInput.stories.svelte
@@ -4,7 +4,7 @@
import type { FillSide, Size, State, Type } from "./TextInput.svelte";
const TextInputFillSides: FillSide[] = ["prepend", "append"];
- const TextInputSizes: Size[] = ["", "sm", "md", "lg", "xl"];
+ const TextInputSizes: Size[] = ["", "sm", "lg"];
const TextInputTypes: Type[] = [
"credit-card",
"date",
diff --git a/packages/stacks-svelte/src/components/TextInput/TextInput.svelte b/packages/stacks-svelte/src/components/TextInput/TextInput.svelte
index 66fd467a27..2821e23dd1 100644
--- a/packages/stacks-svelte/src/components/TextInput/TextInput.svelte
+++ b/packages/stacks-svelte/src/components/TextInput/TextInput.svelte
@@ -1,6 +1,6 @@
+
+
+
+
+
+ {#each [false, true] as hideLabel (hideLabel)}
+
+
+
+ {/each}
+
+
+
+
+
+ {#each TextAreaSizes as size (size)}
+
+
+
+ {/each}
+
+
+
+
+
+ {#each TextAreaStates as state (state)}
+
+
+
+ {/each}
+
+
+
+
+
+
+
+
+
+ `disabled` and `readonly` props add a custom style to the textarea. All
+ other props are proxied to the underlying textarea element.
+
+
+
+
+
+
+
+
diff --git a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
new file mode 100644
index 0000000000..4aaa2690d2
--- /dev/null
+++ b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+ {label}
+
+
+ {#if $$slots.description}
+
+
+
+
+ {/if}
+
+
+
+
+
+ {#if state}
+
+ {#if state === "error"}
+
+ {:else if state === "success"}
+
+ {:else}
+
+ {/if}
+
+ {/if}
+
+
+
+ {#if $$slots.message}
+
+
+
+
+ {/if}
+
diff --git a/packages/stacks-svelte/src/components/TextArea/TextArea.test.ts b/packages/stacks-svelte/src/components/TextArea/TextArea.test.ts
new file mode 100644
index 0000000000..0846d58cac
--- /dev/null
+++ b/packages/stacks-svelte/src/components/TextArea/TextArea.test.ts
@@ -0,0 +1,283 @@
+import { createRawSnippet, tick } from "svelte";
+import { expect } from "@open-wc/testing";
+import { render, screen } from "@testing-library/svelte";
+import userEvent from "@testing-library/user-event";
+import sinon from "sinon";
+
+import TextArea from "./TextArea.svelte";
+
+const user = userEvent.setup();
+
+const snippet = createRawSnippet(() => ({
+ render: () => "test snippet ",
+}));
+
+describe("TextArea", () => {
+ it("should render the TextArea", () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ });
+ expect(screen.getByRole("textbox")).to.have.id("example-input");
+ expect(screen.getByText("example label")).to.exist;
+ });
+
+ // props
+ it("should render the appropriate classes to visually hide the label", () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ hideLabel: true,
+ });
+ expect(screen.getByText("example label")).to.have.class("v-visible-sr");
+ });
+
+ it("should render the appropriate size class", () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ size: "sm",
+ });
+ expect(screen.getByRole("textbox")).to.have.class("s-textarea__sm");
+ });
+
+ it("should render the appropriate state class and icon", () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ state: "error",
+ });
+ expect(screen.getByText("example label").parentElement).to.have.class(
+ "has-error"
+ );
+ expect(screen.getByRole("textbox")).to.have.attribute(
+ "aria-invalid",
+ "true"
+ );
+ expect(screen.getByRole("textbox").nextElementSibling).to.have.class(
+ "s-input-icon"
+ );
+ });
+
+ it('should render the "optional" label status', () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ optional: true,
+ });
+ expect(screen.getByText("Optional")).to.have.class("s-label--status");
+ });
+
+ it('should render the "required" label status', () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ required: true,
+ });
+
+ const requiredSymbol = screen.getByText("*");
+ expect(requiredSymbol).to.exist;
+ expect(requiredSymbol).to.have.class("s-required-symbol");
+ expect(requiredSymbol).to.have.attr("title", "Required");
+ });
+
+ it("should render the disabled attribute on the input", () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ disabled: true,
+ });
+ expect(screen.getByRole("textbox")).to.have.attribute("disabled");
+ });
+
+ it("should render the readonly attribute on the input", () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ readonly: true,
+ });
+ expect(screen.getByRole("textbox")).to.have.attribute("readonly");
+ });
+
+ it("should render the TextArea with artbirary classes", () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ class: "fc-theme-primary-400",
+ });
+
+ expect(screen.getByRole("textbox")).to.have.class(
+ "fc-theme-primary-400"
+ );
+ });
+
+ it("should adjust the classes on prop updates", async () => {
+ const { rerender } = render(TextArea, {
+ class: "fc-theme-primary-400",
+ id: "example-input",
+ label: "example label",
+ });
+ rerender({
+ class: "fc-theme-secondary-400",
+ id: "example-input",
+ label: "example label",
+ });
+ await tick();
+ expect(screen.getByRole("textbox")).not.to.have.class(
+ "fc-theme-primary-400"
+ );
+ expect(screen.getByRole("textbox")).to.have.class(
+ "fc-theme-secondary-400"
+ );
+ });
+
+ // slots
+ it("should render the description slot", () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ $$slots: {
+ description: snippet,
+ },
+ });
+
+ expect(screen.getByText("test snippet").parentElement).to.have.class(
+ "s-description"
+ );
+ });
+
+ it("should render the message slot", () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ $$slots: {
+ message: snippet,
+ },
+ });
+
+ expect(screen.getByText("test snippet").parentElement).to.have.class(
+ "s-input-message"
+ );
+ });
+
+ // events
+ it("should call the on:change callback when the user changes the value", async () => {
+ const onChangeSpy = sinon.spy();
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ onchange: onChangeSpy,
+ });
+ await user.type(screen.getByRole("textbox"), "test");
+ await user.tab();
+ expect(onChangeSpy).to.have.been.calledOnce;
+ expect(onChangeSpy).to.have.been.calledWithMatch({
+ target: { value: "test" },
+ });
+ });
+
+ it("should call the on:input callback when the user changes the value", async () => {
+ const onInputSpy = sinon.spy();
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ oninput: onInputSpy,
+ });
+ await user.type(screen.getByRole("textbox"), "a");
+ await user.tab();
+ expect(onInputSpy).to.have.been.calledOnce;
+ expect(onInputSpy).to.have.been.calledWithMatch({
+ target: { value: "a" },
+ });
+ });
+
+ it("should call the on:keydown callback when the user changes the value", async () => {
+ const onKeydownSpy = sinon.spy();
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ onkeydown: onKeydownSpy,
+ });
+ await user.type(screen.getByRole("textbox"), "123");
+ expect(onKeydownSpy).to.have.been.calledThrice;
+ expect(onKeydownSpy).to.have.been.calledWithMatch({
+ target: { value: "123" },
+ });
+ });
+
+ it("should call the on:keyup callback when the user changes the value", async () => {
+ const onKeyupSpy = sinon.spy();
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ onkeyup: onKeyupSpy,
+ });
+ await user.type(screen.getByRole("textbox"), "123");
+ expect(onKeyupSpy).to.have.been.calledThrice;
+ expect(onKeyupSpy).to.have.been.calledWithMatch({
+ target: { value: "123" },
+ });
+ });
+
+ it("should call the on:focus callback when the user changes the value", async () => {
+ const onFocusSpy = sinon.spy();
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ onfocus: onFocusSpy,
+ });
+ await user.click(screen.getByRole("textbox"));
+
+ expect(onFocusSpy).to.have.been.calledOnce;
+ });
+
+ it("should call the on:blur callback when the user changes the value", async () => {
+ const onBlurSpy = sinon.spy();
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ onblur: onBlurSpy,
+ });
+ await user.click(screen.getByRole("textbox"));
+ await user.tab();
+ expect(onBlurSpy).to.have.been.calledOnce;
+ });
+
+ it("should call the on:paste callback when the user changes the value", async () => {
+ const onPasteSpy = sinon.spy();
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ onpaste: onPasteSpy,
+ });
+ await user.click(screen.getByRole("textbox"));
+ await user.paste("test");
+ expect(onPasteSpy).to.have.been.calledOnce;
+ expect(onPasteSpy).to.have.been.calledWithMatch({
+ target: { value: "test" },
+ });
+ });
+
+ // i18n
+ it("should localize the required label status text when dedicated prop is specified", () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ required: true,
+ i18nRequiredText: "Obbligatorio",
+ });
+
+ const requiredSymbol = screen.getByText("*");
+ expect(requiredSymbol).to.have.attr("title", "Obbligatorio");
+ });
+
+ it("should localize the optional label status text when dedicated prop is specified", () => {
+ render(TextArea, {
+ id: "example-input",
+ label: "example label",
+ optional: true,
+ i18nOptionalText: "Facoltativo",
+ });
+ expect(screen.getByText("Facoltativo")).to.exist;
+ });
+});
From 509a2c98cffe07cb192ba81502f2c4052ae8a7e2 Mon Sep 17 00:00:00 2001
From: Sal
Date: Wed, 26 Nov 2025 15:43:32 -0500
Subject: [PATCH 20/44] update figma link
---
packages/stacks-docs/product/components/inputs.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/stacks-docs/product/components/inputs.html b/packages/stacks-docs/product/components/inputs.html
index d98a118d0e..28625d45f4 100644
--- a/packages/stacks-docs/product/components/inputs.html
+++ b/packages/stacks-docs/product/components/inputs.html
@@ -2,7 +2,7 @@
layout: page
title: Inputs
svelte: https://beta.svelte.stackoverflow.design/?path=/docs/components-textinput--docs
-figma: https://svelte.stackoverflow.design/figma/form-elements
+figma: https://www.figma.com/design/do4Ug0Yws8xCfRjHe9cJfZ/Project-SHINE---Product-UI?node-id=713-24991&p=f&m=dev
description: Input elements are used to gather information from users.
tags: components
---
From ececbf231b5d2646ac512bc44ef2cb0f1f4d2ccc Mon Sep 17 00:00:00 2001
From: Sal
Date: Wed, 26 Nov 2025 15:47:09 -0500
Subject: [PATCH 21/44] set figma and svelte links
---
packages/stacks-docs/product/components/inputs.html | 2 +-
packages/stacks-docs/product/components/textarea.html | 5 +++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/packages/stacks-docs/product/components/inputs.html b/packages/stacks-docs/product/components/inputs.html
index 4cc6dd8fa3..d98a118d0e 100644
--- a/packages/stacks-docs/product/components/inputs.html
+++ b/packages/stacks-docs/product/components/inputs.html
@@ -253,7 +253,7 @@
Username
- @Svg.Check.With("s-input-icon")
+ @Svg.Checkmark.With("s-input-icon")
That name is available! Why do we require a username?
diff --git a/packages/stacks-docs/product/components/textarea.html b/packages/stacks-docs/product/components/textarea.html
index 791d7d27a9..c56ed34f50 100644
--- a/packages/stacks-docs/product/components/textarea.html
+++ b/packages/stacks-docs/product/components/textarea.html
@@ -1,7 +1,8 @@
---
layout: page
title: Textarea
-figma: https://svelte.stackoverflow.design/figma/form-elements
+svelte: https://beta.svelte.stackoverflow.design/?path=/docs/components-textarea--docs
+figma: https://www.figma.com/design/do4Ug0Yws8xCfRjHe9cJfZ/Project-SHINE---Product-UI?node-id=4537-49735&m=dev
description: Multi-line inputs used by users to enter longer text portions.
tags: components
---
@@ -112,7 +113,7 @@
Description
- @Svg.Check.With("s-input-icon")
+ @Svg.Checkmark.With("s-input-icon")
Thanks for providing a description.
From d3acbc2fe344bc354e49e901b67c02ea7929c9ce Mon Sep 17 00:00:00 2001
From: Sal
Date: Wed, 26 Nov 2025 15:52:49 -0500
Subject: [PATCH 22/44] Create afraid-cheetahs-fetch.md
---
.changeset/afraid-cheetahs-fetch.md | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 .changeset/afraid-cheetahs-fetch.md
diff --git a/.changeset/afraid-cheetahs-fetch.md b/.changeset/afraid-cheetahs-fetch.md
new file mode 100644
index 0000000000..66734ec499
--- /dev/null
+++ b/.changeset/afraid-cheetahs-fetch.md
@@ -0,0 +1,10 @@
+---
+"@stackoverflow/stacks": minor
+"@stackoverflow/stacks-svelte": minor
+---
+
+feat(textarea): new SHINE styles and Svelte component
+
+BREAKING CHANGES:
+* `s-input__md` removed
+* `s-input__xl` removed
From 0bc83748e86de42e60f1790e3eec4dd403327fe1 Mon Sep 17 00:00:00 2001
From: Sal
Date: Wed, 26 Nov 2025 16:26:28 -0500
Subject: [PATCH 23/44] expose textarea component
---
packages/stacks-svelte/src/components/index.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/stacks-svelte/src/components/index.ts b/packages/stacks-svelte/src/components/index.ts
index 16863861a4..e551216bc9 100644
--- a/packages/stacks-svelte/src/components/index.ts
+++ b/packages/stacks-svelte/src/components/index.ts
@@ -28,6 +28,7 @@ export { default as SelectItem } from "./Select/SelectItem.svelte";
export { default as Skeleton } from "./Skeleton/Skeleton.svelte";
export { default as Spinner } from "./Spinner/Spinner.svelte";
export { default as Tag } from "./Tag/Tag.svelte";
+export { default as TextArea } from "./TextArea/TextArea.svelte";
export { default as TextInput } from "./TextInput/TextInput.svelte";
export { default as Toaster, showToast, hideToast } from "./Toast/Toast.svelte";
export { default as UserCard } from "./UserCard/UserCard.svelte";
From 3f51599dc881c3a2642166782a5609a90f17506e Mon Sep 17 00:00:00 2001
From: Sal
Date: Wed, 26 Nov 2025 16:28:18 -0500
Subject: [PATCH 24/44] lint
---
.../src/components/TextArea/TextArea.stories.svelte | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/packages/stacks-svelte/src/components/TextArea/TextArea.stories.svelte b/packages/stacks-svelte/src/components/TextArea/TextArea.stories.svelte
index e46effc97b..3b8ed574f6 100644
--- a/packages/stacks-svelte/src/components/TextArea/TextArea.stories.svelte
+++ b/packages/stacks-svelte/src/components/TextArea/TextArea.stories.svelte
@@ -31,7 +31,15 @@
});
-
+
From 7c9d70d2079288f9674dc8896b5e09d6a7edc510 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 09:47:41 -0500
Subject: [PATCH 25/44] PR feedback
---
.changeset/lemon-coins-chew.md | 1 +
packages/stacks-docs/_data/site-navigation.json | 3 ++-
.../src/components/TextInput/TextInput.svelte | 13 -------------
3 files changed, 3 insertions(+), 14 deletions(-)
diff --git a/.changeset/lemon-coins-chew.md b/.changeset/lemon-coins-chew.md
index 76717e5504..cf001c66e4 100644
--- a/.changeset/lemon-coins-chew.md
+++ b/.changeset/lemon-coins-chew.md
@@ -9,3 +9,4 @@ BREAKING CHANGES:
* `s-input__md` removed
* `s-input__xl` removed
* Nested input styles slightly updated
+* `optional` and `i18nOptionalText` attributes removed from TextInput Svelte component
diff --git a/packages/stacks-docs/_data/site-navigation.json b/packages/stacks-docs/_data/site-navigation.json
index a34129e250..787a002741 100644
--- a/packages/stacks-docs/_data/site-navigation.json
+++ b/packages/stacks-docs/_data/site-navigation.json
@@ -181,7 +181,8 @@
},
{
"title": "Inputs",
- "url": "/product/components/inputs/"
+ "url": "/product/components/inputs/",
+ "new": true
},
{
"title": "Labels",
diff --git a/packages/stacks-svelte/src/components/TextInput/TextInput.svelte b/packages/stacks-svelte/src/components/TextInput/TextInput.svelte
index 2821e23dd1..3e945f9224 100644
--- a/packages/stacks-svelte/src/components/TextInput/TextInput.svelte
+++ b/packages/stacks-svelte/src/components/TextInput/TextInput.svelte
@@ -65,12 +65,6 @@
*/
export let name: string | undefined = undefined;
- /**
- * Shows optional label status
- * @type {boolean}
- */
- export let optional: boolean = false;
-
/**
* Placeholder text for the input
* @type {string}
@@ -114,11 +108,6 @@
let className = "";
export { className as class };
- /**
- * Localized translation for the optional label status text
- */
- export let i18nOptionalText: string | undefined = undefined;
-
/**
* Localized translation for the required label status text
*/
@@ -154,8 +143,6 @@
{size}
{required}
{i18nRequiredText}
- {optional}
- {i18nOptionalText}
>
{label}
From 529c43178f747ea533eec00dca36eefe4dd87985 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 09:56:41 -0500
Subject: [PATCH 26/44] fix tests
---
.../components/TextInput/TextInput.test.ts | 19 -------------------
1 file changed, 19 deletions(-)
diff --git a/packages/stacks-svelte/src/components/TextInput/TextInput.test.ts b/packages/stacks-svelte/src/components/TextInput/TextInput.test.ts
index 727c48a568..2959e1db05 100644
--- a/packages/stacks-svelte/src/components/TextInput/TextInput.test.ts
+++ b/packages/stacks-svelte/src/components/TextInput/TextInput.test.ts
@@ -89,15 +89,6 @@ describe("TextInput", () => {
);
});
- it('should render the "optional" label status', () => {
- render(TextInput, {
- id: "example-input",
- label: "example label",
- optional: true,
- });
- expect(screen.getByText("Optional")).to.have.class("s-label--status");
- });
-
it('should render the "required" label status', () => {
render(TextInput, {
id: "example-input",
@@ -339,14 +330,4 @@ describe("TextInput", () => {
const requiredSymbol = screen.getByText("*");
expect(requiredSymbol).to.have.attr("title", "Obbligatorio");
});
-
- it("should localize the optional label status text when dedicated prop is specified", () => {
- render(TextInput, {
- id: "example-input",
- label: "example label",
- optional: true,
- i18nOptionalText: "Facoltativo",
- });
- expect(screen.getByText("Facoltativo")).to.exist;
- });
});
From fd3dda7b636d6911dbf7555e456396e5a00f8d5e Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 10:03:10 -0500
Subject: [PATCH 27/44] fix min-height
---
.../lib/components/input_textarea/input_textarea.less | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/packages/stacks-classic/lib/components/input_textarea/input_textarea.less b/packages/stacks-classic/lib/components/input_textarea/input_textarea.less
index bc0a404eae..ca7e77b65c 100644
--- a/packages/stacks-classic/lib/components/input_textarea/input_textarea.less
+++ b/packages/stacks-classic/lib/components/input_textarea/input_textarea.less
@@ -13,7 +13,7 @@
--_in-px: calc(var(--su12) - var(--su1)); // 11px
--_in-py: var(--su8);
--_in-placeholder-fc: var(--black-300);
- --_in-min-h: unset;
+ --_te-hmn: unset;
}
.s-input,
@@ -89,7 +89,7 @@
font-size: var(--_in-fs);
line-height: var(--_in-lh);
padding: var(--_in-py) var(--_in-px) var(--_in-py) var(--_in-pl, var(--_in-px));
-
+ min-height: var(--_te-hmn);
align-items: center;
border-radius: var(--br-md);
display: flex;
@@ -128,14 +128,14 @@
});
--_in-py: var(--su8);
- --_in-min-h: calc(var(--su32) + var(--su4)); // 36px
+ --_te-hmn: calc(var(--su32) + var(--su4)); // 36px
&&__sm {
- --_in-min-h: calc(var(--su32) - var(--su4)); // 28px
+ --_te-hmn: calc(var(--su32) - var(--su4)); // 28px
}
&&__lg {
- --_in-min-h: calc(var(--su48) - var(--su4)); // 44px
+ --_te-hmn: calc(var(--su48) - var(--su4)); // 44px
}
~ .s-input-icon {
From 2e1d46d5b0a268e71d89d381d62a33d549dff898 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 10:03:27 -0500
Subject: [PATCH 28/44] add *new* label in navigation
---
packages/stacks-docs/_data/site-navigation.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/stacks-docs/_data/site-navigation.json b/packages/stacks-docs/_data/site-navigation.json
index 787a002741..9025e7277c 100644
--- a/packages/stacks-docs/_data/site-navigation.json
+++ b/packages/stacks-docs/_data/site-navigation.json
@@ -195,7 +195,8 @@
},
{
"title": "Textarea",
- "url": "/product/components/textarea/"
+ "url": "/product/components/textarea/",
+ "new": true
}
]
},
From c00d05601390a30c55654c1b584bdf92f7ecb930 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 10:24:41 -0500
Subject: [PATCH 29/44] add accessibility section
---
packages/stacks-docs/product/components/textarea.html | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/packages/stacks-docs/product/components/textarea.html b/packages/stacks-docs/product/components/textarea.html
index c56ed34f50..99c67c97e2 100644
--- a/packages/stacks-docs/product/components/textarea.html
+++ b/packages/stacks-docs/product/components/textarea.html
@@ -28,6 +28,14 @@
+
+ {% header "h2", "Accessibility" %}
+
+ It is recommended to follow the same accessibility guidance as the Input component .
+ Including marking Textarea's as required via the .s-required-symbol class.
+
+
+
{% header "h2", "Validation states" %}
Validation states provides the user feedback based on their interaction (or lack of interaction) with a textarea. These styles are applied by applying the appropriate class to the wrapping parent container.
From 80b5944a41ae33fed93d6a23d43e436cda065907 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 10:38:27 -0500
Subject: [PATCH 30/44] remove `optiona`l props per pr feedback
---
.../src/components/TextArea/TextArea.svelte | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
index 4aaa2690d2..b2f99acea0 100644
--- a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
+++ b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
@@ -42,12 +42,6 @@
*/
export let name: string | undefined = undefined;
- /**
- * Shows optional label status
- * @type {boolean}
- */
- export let optional: boolean = false;
-
/**
* Placeholder text for the input
* @type {string}
@@ -85,11 +79,6 @@
let className = "";
export { className as class };
- /**
- * Localized translation for the optional label status text
- */
- export let i18nOptionalText: string | undefined = undefined;
-
/**
* Localized translation for the required label status text
*/
@@ -125,8 +114,6 @@
{size}
{required}
{i18nRequiredText}
- {optional}
- {i18nOptionalText}
>
{label}
From 27683cfddfaa6d8d169d6a19e31e16b351aaa5a7 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 10:45:32 -0500
Subject: [PATCH 31/44] remove superfluous wrapper div
---
.../src/components/TextArea/TextArea.svelte | 74 +++++++++----------
1 file changed, 36 insertions(+), 38 deletions(-)
diff --git a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
index b2f99acea0..dcf5fb3e99 100644
--- a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
+++ b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
@@ -125,44 +125,42 @@
{/if}
-
-
-
-
- {#if state}
-
- {#if state === "error"}
-
- {:else if state === "success"}
-
- {:else}
-
- {/if}
-
- {/if}
-
+
+
+
+ {#if state}
+
+ {#if state === "error"}
+
+ {:else if state === "success"}
+
+ {:else}
+
+ {/if}
+
+ {/if}
{#if $$slots.message}
From f697e471e684f0d2568186004b35782e3ff2fe86 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 10:53:39 -0500
Subject: [PATCH 32/44] make min-height textarea specific
---
.../lib/components/input_textarea/input_textarea.less | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/packages/stacks-classic/lib/components/input_textarea/input_textarea.less b/packages/stacks-classic/lib/components/input_textarea/input_textarea.less
index ca7e77b65c..1e3026aabf 100644
--- a/packages/stacks-classic/lib/components/input_textarea/input_textarea.less
+++ b/packages/stacks-classic/lib/components/input_textarea/input_textarea.less
@@ -13,7 +13,6 @@
--_in-px: calc(var(--su12) - var(--su1)); // 11px
--_in-py: var(--su8);
--_in-placeholder-fc: var(--black-300);
- --_te-hmn: unset;
}
.s-input,
@@ -89,7 +88,7 @@
font-size: var(--_in-fs);
line-height: var(--_in-lh);
padding: var(--_in-py) var(--_in-px) var(--_in-py) var(--_in-pl, var(--_in-px));
- min-height: var(--_te-hmn);
+
align-items: center;
border-radius: var(--br-md);
display: flex;
@@ -138,6 +137,8 @@
--_te-hmn: calc(var(--su48) - var(--su4)); // 44px
}
+ min-height: var(--_te-hmn);
+
~ .s-input-icon {
right: var(--_in-px);
top: var(--_in-py);
From 19001ecb1ba3f5e6c10eb56c8af02b3c6027806c Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 10:57:10 -0500
Subject: [PATCH 33/44] lint
---
.../stacks-svelte/src/components/TextArea/TextArea.svelte | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
index dcf5fb3e99..e5c317598c 100644
--- a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
+++ b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
@@ -131,8 +131,8 @@
aria-describedby={$$slots.message
? `${id}-message`
: $$slots.description
- ? `${id}-description`
- : undefined}
+ ? `${id}-description`
+ : undefined}
aria-invalid={state === "error"}
class={classes}
{disabled}
From fda2ebe1168b56064497532605016bb6cd30e764 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 10:58:00 -0500
Subject: [PATCH 34/44] fix tests
---
.../src/components/TextArea/TextArea.test.ts | 19 -------------------
1 file changed, 19 deletions(-)
diff --git a/packages/stacks-svelte/src/components/TextArea/TextArea.test.ts b/packages/stacks-svelte/src/components/TextArea/TextArea.test.ts
index 0846d58cac..ab2302ee11 100644
--- a/packages/stacks-svelte/src/components/TextArea/TextArea.test.ts
+++ b/packages/stacks-svelte/src/components/TextArea/TextArea.test.ts
@@ -59,15 +59,6 @@ describe("TextArea", () => {
);
});
- it('should render the "optional" label status', () => {
- render(TextArea, {
- id: "example-input",
- label: "example label",
- optional: true,
- });
- expect(screen.getByText("Optional")).to.have.class("s-label--status");
- });
-
it('should render the "required" label status', () => {
render(TextArea, {
id: "example-input",
@@ -270,14 +261,4 @@ describe("TextArea", () => {
const requiredSymbol = screen.getByText("*");
expect(requiredSymbol).to.have.attr("title", "Obbligatorio");
});
-
- it("should localize the optional label status text when dedicated prop is specified", () => {
- render(TextArea, {
- id: "example-input",
- label: "example label",
- optional: true,
- i18nOptionalText: "Facoltativo",
- });
- expect(screen.getByText("Facoltativo")).to.exist;
- });
});
From f08863c1e21894bdd814880b01bcfb9bbb0b1ba4 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 11:37:39 -0500
Subject: [PATCH 35/44] remove all usages of flex--item
---
.../product/components/inputs.html | 60 +++++++++----------
.../product/components/textarea.html | 46 +++++++-------
2 files changed, 53 insertions(+), 53 deletions(-)
diff --git a/packages/stacks-docs/product/components/inputs.html b/packages/stacks-docs/product/components/inputs.html
index 28625d45f4..795a7719cb 100644
--- a/packages/stacks-docs/product/components/inputs.html
+++ b/packages/stacks-docs/product/components/inputs.html
@@ -166,7 +166,7 @@
{% assign state = item.validation %}
{% for class in state %}
- {{ class.class }}
+ {{ class.class }}
{{ class.applies }}
{{ class.description }}
@@ -192,24 +192,24 @@
{% highlight html %}
{% endhighlight %}
-
@@ -223,24 +223,24 @@
{% highlight html %}
{% endhighlight %}
-
@@ -250,23 +250,23 @@
{% highlight html %}
{% endhighlight %}
-
@@ -351,22 +351,22 @@
{% highlight html %}
-
Website URL
+
Website URL
{% endhighlight %}
-
Website URL
+
Website URL
@@ -377,32 +377,32 @@
{% highlight html %}
-
Minimum Salary
+
Minimum Salary
{% endhighlight %}
-
Minimum Salary
+
Minimum Salary
diff --git a/packages/stacks-docs/product/components/textarea.html b/packages/stacks-docs/product/components/textarea.html
index 99c67c97e2..1b48635161 100644
--- a/packages/stacks-docs/product/components/textarea.html
+++ b/packages/stacks-docs/product/components/textarea.html
@@ -11,17 +11,17 @@
{% highlight html %}
-
Question body
+
Question body
-
+
{% endhighlight %}
-
Question body
+
Question body
-
+
@@ -55,7 +55,7 @@
{% assign state = item.validation %}
{% for class in state %}
- {{ class.class }}
+ {{ class.class }}
{{ class.applies }}
{{ class.description }}
@@ -70,22 +70,22 @@
{% highlight html %}
-
Description
+
Description
-
+
@Svg.Alert.With("s-input-icon")
-
Consider entering a description to help us better help you.
+
Consider entering a description to help us better help you.
{% endhighlight %}
-
Description
+
Description
-
+
{% icon "Alert", "s-input-icon" %}
-
Consider entering a description to help us better help you.
+
Consider entering a description to help us better help you.
@@ -94,22 +94,22 @@
{% highlight html %}
-
Description
+
Description
-
+
@Svg.AlertCircle.With("s-input-icon")
-
A description must be provided.
+
A description must be provided.
{% endhighlight %}
-
Description
+
Description
-
+
{% icon "AlertCircle", "s-input-icon" %}
-
A description must be provided.
+
A description must be provided.
@@ -118,22 +118,22 @@
{% highlight html %}
-
Description
+
Description
-
+
@Svg.Checkmark.With("s-input-icon")
-
Thanks for providing a description.
+
Thanks for providing a description.
{% endhighlight %}
-
Description
+
Description
-
+
{% icon "Check", "s-input-icon" %}
-
Thanks for providing a description.
+
Thanks for providing a description.
From 683e040abbc3fda5882eb0c0cd5fe6edb8c70ef7 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 13:06:40 -0500
Subject: [PATCH 36/44] upgrade textinput & textarea to Svelte 5 style
---
.../TextArea/TextArea.stories.svelte | 37 ++-
.../src/components/TextArea/TextArea.svelte | 188 +++++++-------
.../TextInput/TextInput.stories.svelte | 47 ++--
.../src/components/TextInput/TextInput.svelte | 234 ++++++++++--------
4 files changed, 274 insertions(+), 232 deletions(-)
diff --git a/packages/stacks-svelte/src/components/TextArea/TextArea.stories.svelte b/packages/stacks-svelte/src/components/TextArea/TextArea.stories.svelte
index 3b8ed574f6..bd60274376 100644
--- a/packages/stacks-svelte/src/components/TextArea/TextArea.stories.svelte
+++ b/packages/stacks-svelte/src/components/TextArea/TextArea.stories.svelte
@@ -81,7 +81,9 @@
label={`${state || "default"} input`}
{state}
>
- {state || "default"} message
+ {#snippet message()}
+ {state || "default"} message
+ {/snippet}
{/each}
@@ -90,21 +92,17 @@
-
-
-
+ >
+ {#snippet description()}
+ this input has been marked required.
+ {/snippet}
+
@@ -135,20 +133,21 @@
-
+
+ The examples below show all the snippets this component supports.
-
-
-
- This TextArea includes a message slot.
-
+
+ {#snippet message()}
+ This TextArea includes a message snippet.
+ {/snippet}
diff --git a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
index e5c317598c..c39634e094 100644
--- a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
+++ b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
@@ -12,79 +12,97 @@
IconCheckmark,
} from "@stackoverflow/stacks-icons-legacy/icons";
- /**
- * `id` attribute of the text input
- * @type {string}
- */
- export let id: string;
-
- /**
- * The label associated with the input
- * @type {string}
- */
- export let label: string;
-
- /**
- * Sets the disabled state of the input
- * @type {boolean}
- */
- export let disabled: boolean = false;
-
- /**
- * The visiblity of the label element
- * @type {boolean}
- */
- export let hideLabel: boolean = false;
-
- /**
- * Name attribute of the textarea
- * @type {string | undefined}
- */
- export let name: string | undefined = undefined;
-
- /**
- * Placeholder text for the input
- * @type {string}
- */
- export let placeholder: string = "";
-
- /**
- * Sets the readonly state of the input
- * @type {boolean}
- */
- export let readonly: boolean = false;
-
- /**
- * Make the input required and show required label status
- * @type {boolean}
- */
- export let required: boolean = false;
-
- /**
- * The size of the text input
- * @type {"" | "sm" | "lg"} Size
- */
- export let size: Size = "";
-
- /**
- * The size of the text input
- * @type {"" | "error" | "success" | "warning"} State
- */
- export let state: State = "";
-
- /**
- * Additional CSS classes added to the underlying HTML input element
- * @type {string}
- */
- let className = "";
- export { className as class };
-
- /**
- * Localized translation for the required label status text
- */
- export let i18nRequiredText: string | undefined = undefined;
-
- $: classes = getClasses(className, size);
+ interface Props {
+ /**
+ * `id` attribute of the text input
+ * @type {string}
+ */
+ id: string;
+ /**
+ * The label associated with the input
+ * @type {string}
+ */
+ label: string;
+ /**
+ * Sets the disabled state of the input
+ * @type {boolean}
+ */
+ disabled?: boolean;
+ /**
+ * The visiblity of the label element
+ * @type {boolean}
+ */
+ hideLabel?: boolean;
+ /**
+ * Name attribute of the textarea
+ * @type {string | undefined}
+ */
+ name?: string | undefined;
+ /**
+ * Placeholder text for the input
+ * @type {string}
+ */
+ placeholder?: string;
+ /**
+ * Sets the readonly state of the input
+ * @type {boolean}
+ */
+ readonly?: boolean;
+ /**
+ * Make the input required and show required label status
+ * @type {boolean}
+ */
+ required?: boolean;
+ /**
+ * The size of the text input
+ * @type {"" | "sm" | "lg"} Size
+ */
+ size?: Size;
+ /**
+ * The size of the text input
+ * @type {"" | "error" | "success" | "warning"} State
+ */
+ state?: State;
+
+ /**
+ * Additional CSS classes added to the underlying HTML input element
+ * @type {string}
+ */
+ class?: string;
+ /**
+ * Localized translation for the required label status text
+ */
+ i18nRequiredText?: string | undefined;
+
+ /**
+ * Optional description snippet rendered between the label and input.
+ */
+ description?: import('svelte').Snippet;
+
+ /**
+ * Optional message snippet rendered after the input.
+ */
+ message?: import('svelte').Snippet;
+ }
+
+ let {
+ id,
+ label,
+ disabled = false,
+ hideLabel = false,
+ name = undefined,
+ placeholder = "",
+ readonly = false,
+ required = false,
+ size = "",
+ state = "",
+ class: className = "",
+ i18nRequiredText = undefined,
+ description,
+ message,
+ ...rest
+ }: Props = $props();
+
const getClasses = (className: string, size: Size) => {
const base = "s-textarea";
@@ -100,6 +118,7 @@
return classes;
};
+ let classes = $derived(getClasses(className, size));
- {#if $$slots.description}
+ {#if description}
-
+ {@render description()}
{/if}
+ {...rest}
+>
{#if state}
@@ -163,10 +175,10 @@
{/if}
- {#if $$slots.message}
+ {#if message}
-
+ {@render message()}
{/if}
diff --git a/packages/stacks-svelte/src/components/TextInput/TextInput.stories.svelte b/packages/stacks-svelte/src/components/TextInput/TextInput.stories.svelte
index ef6f39a782..7ea9a8d6cd 100644
--- a/packages/stacks-svelte/src/components/TextInput/TextInput.stories.svelte
+++ b/packages/stacks-svelte/src/components/TextInput/TextInput.stories.svelte
@@ -66,7 +66,9 @@
label="{fillSide}ed fill"
{fillSide}
>
-
{fillSide}ed fill
+ {#snippet fill()}
+
{fillSide}ed fill
+ {/snippet}
{/each}
@@ -113,7 +115,9 @@
label={`${state || "default"} input`}
{state}
>
- {state || "default"} message
+ {#snippet message()}
+ {state || "default"} message
+ {/snippet}
{/each}
@@ -122,21 +126,17 @@
-
-
-
+ >
+ {#snippet description()}
+ this input has been marked required.
+ {/snippet}
+
@@ -182,25 +182,28 @@
-
+
+ The examples below show all the snippets this component supports.
-
-
- This TextInput includes a description slot.
-
+
+ {#snippet description()}
+ This TextInput includes a description snippet.
+ {/snippet}
-
-
- This TextInput includes a message slot.
-
+
+ {#snippet message()}
+ This TextInput includes a message snippet.
+ {/snippet}
-
- https://
+
+ {#snippet fill()}
+ https://
+ {/snippet}
diff --git a/packages/stacks-svelte/src/components/TextInput/TextInput.svelte b/packages/stacks-svelte/src/components/TextInput/TextInput.svelte
index 3e945f9224..7cb231036c 100644
--- a/packages/stacks-svelte/src/components/TextInput/TextInput.svelte
+++ b/packages/stacks-svelte/src/components/TextInput/TextInput.svelte
@@ -29,91 +29,125 @@
IconSearch,
} from "@stackoverflow/stacks-icons-legacy/icons";
- /**
- * `id` attribute of the text input
- * @type {string}
- */
- export let id: string;
-
- /**
- * The label associated with the input
- * @type {string}
- */
- export let label: string;
-
- /**
- * Sets the disabled state of the input
- * @type {boolean}
- */
- export let disabled: boolean = false;
-
- /**
- * The visiblity of the label element
- * @type {boolean}
- */
- export let hideLabel: boolean = false;
-
- /**
- * Where to place the input fill element
- * @type {"prepend" | "append"} FillSide
- */
- export let fillSide: FillSide = "prepend";
-
- /**
- * Name attribute of the input
- * @type {string | undefined}
- */
- export let name: string | undefined = undefined;
-
- /**
- * Placeholder text for the input
- * @type {string}
- */
- export let placeholder: string = "";
-
- /**
- * Sets the readonly state of the input
- * @type {boolean}
- */
- export let readonly: boolean = false;
-
- /**
- * Make the input required and show required label status
- * @type {boolean}
- */
- export let required: boolean = false;
-
- /**
- * The size of the text input
- * @type {"" | "sm" | "lg"} Size
- */
- export let size: Size = "";
-
- /**
- * The size of the text input
- * @type {"" | "error" | "success" | "warning"} State
- */
- export let state: State = "";
-
- /**
- * The type of the text input
- * @type {"credit-card" | "date" | "datetime-local" | "email" | "month" | "number" | "password" | "search" | "tel" | "text" | "time" | "url" | "week"}
- */
- export let type: Type = "text";
-
- /**
- * Additional CSS classes added to the underlying HTML input element
- * @type {string}
- */
- let className = "";
- export { className as class };
-
- /**
- * Localized translation for the required label status text
- */
- export let i18nRequiredText: string | undefined = undefined;
-
- $: classes = getClasses(className, size);
+ interface Props {
+ /**
+ * `id` attribute of the text input
+ * @type {string}
+ */
+ id: string;
+
+ /**
+ * The label associated with the input
+ * @type {string}
+ */
+ label: string;
+
+ /**
+ * Sets the disabled state of the input
+ * @type {boolean}
+ */
+ disabled?: boolean;
+
+ /**
+ * The visiblity of the label element
+ * @type {boolean}
+ */
+ hideLabel?: boolean;
+
+ /**
+ * Where to place the input fill element
+ * @type {"prepend" | "append"} FillSide
+ */
+ fillSide?: FillSide;
+
+ /**
+ * Name attribute of the input
+ * @type {string | undefined}
+ */
+ name?: string | undefined;
+ /**
+ * Placeholder text for the input
+ * @type {string}
+ */
+ placeholder?: string;
+
+ /**
+ * Sets the readonly state of the input
+ * @type {boolean}
+ */
+ readonly?: boolean;
+
+ /**
+ * Make the input required and show required label status
+ * @type {boolean}
+ */
+ required?: boolean;
+
+ /**
+ * The size of the text input
+ * @type {"" | "sm" | "lg"} Size
+ */
+ size?: Size;
+
+ /**
+ * The size of the text input
+ * @type {"" | "error" | "success" | "warning"} State
+ */
+ state?: State;
+
+ /**
+ * The type of the text input
+ * @type {"credit-card" | "date" | "datetime-local" | "email" | "month" | "number" | "password" | "search" | "tel" | "text" | "time" | "url" | "week"}
+ */
+ type?: Type;
+
+ /**
+ * Additional CSS classes added to the underlying HTML input element
+ * @type {string}
+ */
+ class?: string;
+
+ /**
+ * Localized translation for the required label status text
+ */
+ i18nRequiredText?: string | undefined;
+
+ /**
+ * Optional description snippet rendered between the label and input.
+ */
+ description?: import("svelte").Snippet;
+
+ /**
+ * Optional fill snippet rendered either before or after the input based on the value of `fillSide`.
+ */
+ fill?: import("svelte").Snippet;
+
+ /**
+ * Optional message snippet rendered after the input.
+ */
+ message?: import("svelte").Snippet;
+ }
+
+ let {
+ id,
+ label,
+ disabled = false,
+ hideLabel = false,
+ fillSide = "prepend",
+ name = undefined,
+ placeholder = "",
+ readonly = false,
+ required = false,
+ size = "",
+ state = "",
+ type = "text",
+ class: className = "",
+ i18nRequiredText = undefined,
+ description,
+ fill,
+ message,
+ ...rest
+ }: Props = $props();
const getClasses = (className: string, size: Size) => {
const base = "s-input";
@@ -129,6 +163,7 @@
return classes;
};
+ let classes = $derived(getClasses(className, size));
- {#if $$slots.description}
+ {#if description}
-
+ {@render description()}
{/if}
- {#if $$slots.fill}
+ {#if fill}
-
+ {@render fill()}
{/if}
@@ -184,31 +219,24 @@
{#if state}
@@ -225,10 +253,10 @@
- {#if $$slots.message}
+ {#if message}
-
+ {@render message()}
{/if}
From b51bb78b8d50202ce8fc12d55f90571bfbd140f1 Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 13:13:19 -0500
Subject: [PATCH 37/44] lint
---
.../components/TextArea/TextArea.stories.svelte | 9 ++++++---
.../src/components/TextArea/TextArea.svelte | 9 ++++-----
.../TextInput/TextInput.stories.svelte | 17 ++++++++++-------
3 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/packages/stacks-svelte/src/components/TextArea/TextArea.stories.svelte b/packages/stacks-svelte/src/components/TextArea/TextArea.stories.svelte
index bd60274376..2483f88fe4 100644
--- a/packages/stacks-svelte/src/components/TextArea/TextArea.stories.svelte
+++ b/packages/stacks-svelte/src/components/TextArea/TextArea.stories.svelte
@@ -82,7 +82,7 @@
{state}
>
{#snippet message()}
- {state || "default"} message
+ {state || "default"} message
{/snippet}
@@ -100,7 +100,7 @@
required
>
{#snippet description()}
-
this input has been marked required.
+
this input has been marked required.
{/snippet}
@@ -137,7 +137,10 @@
The examples below show all the snippets this component supports.
-
+
{#snippet description()}
This TextArea includes a description snippet.
{/snippet}
diff --git a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
index c39634e094..c1989d675b 100644
--- a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
+++ b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
@@ -58,7 +58,7 @@
* @type {"" | "sm" | "lg"} Size
*/
size?: Size;
- /**
+ /**
* The size of the text input
* @type {"" | "error" | "success" | "warning"} State
*/
@@ -77,12 +77,12 @@
/**
* Optional description snippet rendered between the label and input.
*/
- description?: import('svelte').Snippet;
+ description?: import("svelte").Snippet;
/**
* Optional message snippet rendered after the input.
*/
- message?: import('svelte').Snippet;
+ message?: import("svelte").Snippet;
}
let {
@@ -103,7 +103,6 @@
...rest
}: Props = $props();
-
const getClasses = (className: string, size: Size) => {
const base = "s-textarea";
let classes = base;
@@ -160,7 +159,7 @@
{readonly}
{required}
{...rest}
->
+ >
{#if state}
diff --git a/packages/stacks-svelte/src/components/TextInput/TextInput.stories.svelte b/packages/stacks-svelte/src/components/TextInput/TextInput.stories.svelte
index 7ea9a8d6cd..2c052f2d9f 100644
--- a/packages/stacks-svelte/src/components/TextInput/TextInput.stories.svelte
+++ b/packages/stacks-svelte/src/components/TextInput/TextInput.stories.svelte
@@ -67,7 +67,7 @@
{fillSide}
>
{#snippet fill()}
- {fillSide}ed fill
+ {fillSide}ed fill
{/snippet}
@@ -116,7 +116,7 @@
{state}
>
{#snippet message()}
-
{state || "default"} message
+
{state || "default"} message
{/snippet}
@@ -134,7 +134,7 @@
required
>
{#snippet description()}
-
this input has been marked required.
+
this input has been marked required.
{/snippet}
@@ -186,23 +186,26 @@
The examples below show all the snippets this component supports.
-
+
{#snippet description()}
- This TextInput includes a description snippet.
+ This TextInput includes a description snippet.
{/snippet}
{#snippet message()}
- This TextInput includes a message snippet.
+ This TextInput includes a message snippet.
{/snippet}
{#snippet fill()}
- https://
+ https://
{/snippet}
From 7d8d4d580f8631b8f1185031e8673fff395af44f Mon Sep 17 00:00:00 2001
From: Sal
Date: Tue, 2 Dec 2025 13:34:06 -0500
Subject: [PATCH 38/44] fix lint
---
packages/stacks-svelte/src/components/TextArea/TextArea.svelte | 3 ++-
.../stacks-svelte/src/components/TextInput/TextInput.svelte | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
index c1989d675b..165974fcba 100644
--- a/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
+++ b/packages/stacks-svelte/src/components/TextArea/TextArea.svelte
@@ -4,6 +4,7 @@