diff --git a/README.md b/README.md
index 7ec7ddb..bed5a0b 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,8 @@
+
+
@@ -85,6 +87,35 @@ import { SlotText } from "slot-text/vue";
```
+## 🔷 Solid
+
+```tsx
+import "slot-text/style.css";
+import { createSignal } from "solid-js";
+import { slotText } from "slot-text/solid";
+
+const [label, setLabel] = createSignal("Copy");
+
+
+```
+
+## 🧡 Svelte
+
+```svelte
+
+
+
+```
+
## ⚙️ Options
| Option | Default | Description |
@@ -132,6 +163,7 @@ essentially any font you'd use for those.
## 📝 Notes
- Browser-only DOM utility, zero runtime dependencies.
-- React and Vue are optional peer dependencies — plain JS users don't need them.
+- React, Vue, Solid, and Svelte are optional peer dependencies — plain JS users
+ don't need them.
- Import the CSS once before using the animation.
- Low-level helpers also exported: `buildSlotText`, `animateSlotText`, `chromatic`.
diff --git a/examples/env.d.ts b/examples/env.d.ts
new file mode 100644
index 0000000..615a741
--- /dev/null
+++ b/examples/env.d.ts
@@ -0,0 +1 @@
+declare module "slot-text/style.css";
diff --git a/examples/solid.tsx b/examples/solid.tsx
new file mode 100644
index 0000000..04a6faa
--- /dev/null
+++ b/examples/solid.tsx
@@ -0,0 +1,30 @@
+import { createSignal } from "solid-js";
+import "slot-text/style.css";
+import { chromatic } from "slot-text";
+import { slotText } from "slot-text/solid";
+
+void slotText;
+
+export function CopyButton() {
+ const [copied, setCopied] = createSignal(false);
+
+ function copy() {
+ setCopied(true);
+ window.setTimeout(() => setCopied(false), 1400);
+ }
+
+ return (
+
+ );
+}
diff --git a/examples/svelte.svelte b/examples/svelte.svelte
new file mode 100644
index 0000000..9866f47
--- /dev/null
+++ b/examples/svelte.svelte
@@ -0,0 +1,27 @@
+
+
+
diff --git a/examples/tsconfig.json b/examples/tsconfig.json
new file mode 100644
index 0000000..d15d8a3
--- /dev/null
+++ b/examples/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ES2020",
+ "moduleResolution": "Bundler",
+ "lib": ["ES2020", "DOM"],
+ "strict": true,
+ "skipLibCheck": true,
+ "jsx": "preserve",
+ "jsxImportSource": "solid-js",
+ "paths": {
+ "slot-text": ["../src/index.ts"],
+ "slot-text/solid": ["../src/solid.ts"],
+ "slot-text/svelte": ["../src/svelte.ts"]
+ }
+ },
+ "include": ["env.d.ts", "solid.tsx", "*.svelte", "../src/**/*.ts"]
+}
diff --git a/package-lock.json b/package-lock.json
index a2d337b..1172095 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,18 +12,28 @@
"@types/react": "^19.2.17",
"happy-dom": "^20.10.2",
"react": "^19.2.7",
+ "solid-js": "^1.9.13",
+ "svelte": "^5.56.3",
"typescript": "^5.8.3",
"vitest": "^4.1.8",
"vue": "^3.5.35"
},
"peerDependencies": {
"react": ">=18 <20",
+ "solid-js": ">=1.8 <2",
+ "svelte": ">=4 <6",
"vue": ">=3.4 <4"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
+ "solid-js": {
+ "optional": true
+ },
+ "svelte": {
+ "optional": true
+ },
"vue": {
"optional": true
}
@@ -113,6 +123,38 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
@@ -120,6 +162,17 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
"node_modules/@napi-rs/wasm-runtime": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz",
@@ -420,6 +473,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@sveltejs/acorn-typescript": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.10.tgz",
+ "integrity": "sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^8.9.0"
+ }
+ },
"node_modules/@tybys/wasm-util": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
@@ -476,6 +539,13 @@
"csstype": "^3.2.2"
}
},
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/whatwg-mimetype": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz",
@@ -725,6 +795,29 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/acorn": {
+ "version": "8.17.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz",
+ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz",
+ "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/assertion-error": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
@@ -735,6 +828,16 @@
"node": ">=12"
}
},
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/buffer-image-size": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/buffer-image-size/-/buffer-image-size-0.6.4.tgz",
@@ -758,6 +861,16 @@
"node": ">=18"
}
},
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@@ -782,6 +895,13 @@
"node": ">=8"
}
},
+ "node_modules/devalue": {
+ "version": "5.8.1",
+ "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz",
+ "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/entities": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
@@ -802,6 +922,31 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/esm-env": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
+ "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esrap": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.11.tgz",
+ "integrity": "sha512-gPdx+I+BjYEinNMQaBXFjbaJVyoPMU4ZODg5mE+M4DqVG9VusAVHHjcBX+zqyITlI0DIARwDMMzZwAWj36dRoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.15"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/types": "^8.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/types": {
+ "optional": true
+ }
+ }
+ },
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
@@ -871,6 +1016,16 @@
"node": ">=20.0.0"
}
},
+ "node_modules/is-reference": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
+ "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.6"
+ }
+ },
"node_modules/lightningcss": {
"version": "1.32.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
@@ -1132,6 +1287,13 @@
"url": "https://opencollective.com/parcel"
}
},
+ "node_modules/locate-character": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
+ "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@@ -1275,6 +1437,29 @@
"@rolldown/binding-win32-x64-msvc": "1.0.3"
}
},
+ "node_modules/seroval": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.4.tgz",
+ "integrity": "sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/seroval-plugins": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.5.4.tgz",
+ "integrity": "sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "seroval": "^1.0"
+ }
+ },
"node_modules/siginfo": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
@@ -1282,6 +1467,18 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/solid-js": {
+ "version": "1.9.13",
+ "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.13.tgz",
+ "integrity": "sha512-6hJeJMOcEX8ktqjpDoJZEmld3ijvcvWBDtiXBm7f4332SiFN66QeAQI1REQshvyUoISsSeJ4PHDauKYbwao9JQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.1.0",
+ "seroval": "~1.5.0",
+ "seroval-plugins": "~1.5.0"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1306,6 +1503,34 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/svelte": {
+ "version": "5.56.3",
+ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.3.tgz",
+ "integrity": "sha512-w7JvrM5IFl5cmfbY0TLik9o7mjRUJmRMhOR51tBPu708Gr/MjbGs7VnJnr/B0CaXeI4vtnOh7RKxDr0cwhMdDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.4",
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@sveltejs/acorn-typescript": "^1.0.10",
+ "@types/estree": "^1.0.5",
+ "@types/trusted-types": "^2.0.7",
+ "acorn": "^8.12.1",
+ "aria-query": "5.3.1",
+ "axobject-query": "^4.1.0",
+ "clsx": "^2.1.1",
+ "devalue": "^5.8.1",
+ "esm-env": "^1.2.1",
+ "esrap": "^2.2.11",
+ "is-reference": "^3.0.3",
+ "locate-character": "^3.0.0",
+ "magic-string": "^0.30.11",
+ "zimmerframe": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/tinybench": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
@@ -1617,6 +1842,13 @@
"optional": true
}
}
+ },
+ "node_modules/zimmerframe": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz",
+ "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==",
+ "dev": true,
+ "license": "MIT"
}
}
}
diff --git a/package.json b/package.json
index d77f5f8..577b37a 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"dist",
"examples",
"style.css",
+ "style.css.d.ts",
"README.md",
"LICENSE"
],
@@ -29,11 +30,24 @@
"types": "./dist/vue.d.ts",
"import": "./dist/vue.js"
},
- "./style.css": "./style.css"
+ "./solid": {
+ "types": "./dist/solid.d.ts",
+ "import": "./dist/solid.js"
+ },
+ "./svelte": {
+ "types": "./dist/svelte.d.ts",
+ "import": "./dist/svelte.js"
+ },
+ "./style.css": {
+ "types": "./style.css.d.ts",
+ "default": "./style.css"
+ }
},
"types": "./dist/index.d.ts",
"peerDependencies": {
"react": ">=18 <20",
+ "solid-js": ">=1.8 <2",
+ "svelte": ">=4 <6",
"vue": ">=3.4 <4"
},
"peerDependenciesMeta": {
@@ -42,6 +56,12 @@
},
"vue": {
"optional": true
+ },
+ "solid-js": {
+ "optional": true
+ },
+ "svelte": {
+ "optional": true
}
},
"scripts": {
@@ -55,6 +75,8 @@
"@types/react": "^19.2.17",
"happy-dom": "^20.10.2",
"react": "^19.2.7",
+ "solid-js": "^1.9.13",
+ "svelte": "^5.56.3",
"typescript": "^5.8.3",
"vitest": "^4.1.8",
"vue": "^3.5.35"
diff --git a/src/solid.ts b/src/solid.ts
new file mode 100644
index 0000000..efe1f4c
--- /dev/null
+++ b/src/solid.ts
@@ -0,0 +1,40 @@
+import { createRenderEffect, onCleanup, type Accessor } from "solid-js";
+import { slotText as createSlotText, type SlotOptions } from "./index.js";
+
+export interface SlotTextParams {
+ text: string;
+ options?: SlotOptions;
+}
+
+export function slotText(element: HTMLElement, accessor: Accessor) {
+ const initial = accessor();
+ const controller = createSlotText(element, initial.text);
+ let previousText = initial.text;
+ let isFirstRun = true;
+
+ createRenderEffect(() => {
+ const next = accessor();
+
+ if (isFirstRun) {
+ isFirstRun = false;
+ return;
+ }
+
+ if (next.text !== previousText) {
+ previousText = next.text;
+ controller.set(next.text, next.options);
+ }
+ });
+
+ onCleanup(() => {
+ controller.destroy();
+ });
+}
+
+declare module "solid-js" {
+ namespace JSX {
+ interface Directives {
+ slotText: SlotTextParams;
+ }
+ }
+}
diff --git a/src/svelte.ts b/src/svelte.ts
new file mode 100644
index 0000000..8b6d977
--- /dev/null
+++ b/src/svelte.ts
@@ -0,0 +1,22 @@
+import { slotText as createSlotText, type SlotOptions } from "./index.js";
+
+export interface SlotTextParams {
+ text: string;
+ options?: SlotOptions;
+}
+
+export function slotText(element: HTMLElement, params: SlotTextParams) {
+ const controller = createSlotText(element, params.text);
+ let previousText = params.text;
+
+ return {
+ update(params: SlotTextParams) {
+ if (params.text === previousText) return;
+ previousText = params.text;
+ controller.set(params.text, params.options);
+ },
+ destroy() {
+ controller.destroy();
+ },
+ };
+}
diff --git a/style.css.d.ts b/style.css.d.ts
new file mode 100644
index 0000000..9cb05fc
--- /dev/null
+++ b/style.css.d.ts
@@ -0,0 +1,2 @@
+declare const stylesheet: string;
+export default stylesheet;
diff --git a/tests/adapters.test.ts b/tests/adapters.test.ts
new file mode 100644
index 0000000..0813ba4
--- /dev/null
+++ b/tests/adapters.test.ts
@@ -0,0 +1,103 @@
+import { createRoot, createSignal } from "solid-js";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { type SlotOptions } from "../src/index.js";
+import { slotText as solidSlotText } from "../src/solid.js";
+import { slotText as svelteSlotText } from "../src/svelte.js";
+
+let el: HTMLElement;
+
+beforeEach(() => {
+ vi.useFakeTimers();
+ el = document.createElement("span");
+ document.body.appendChild(el);
+});
+
+afterEach(() => {
+ vi.useRealTimers();
+ el.remove();
+});
+
+const readText = () =>
+ Array.from(el.querySelectorAll(".char-slot"))
+ .map((s) => s.dataset.char ?? "")
+ .join("");
+
+const hasTintedFace = () =>
+ Array.from(el.querySelectorAll(".char-face")).some(
+ (face) => face.style.color === "red",
+ );
+
+describe("Solid adapter", () => {
+ it("updates text through the directive lifecycle", () => {
+ let dispose!: () => void;
+ let setText!: (text: string) => string;
+
+ createRoot((rootDispose) => {
+ dispose = rootDispose;
+ const [text, setLabel] = createSignal("Copy");
+ setText = setLabel;
+
+ solidSlotText(el, () => ({ text: text() }));
+ expect(readText()).toBe("Copy");
+ });
+
+ setText("Copied");
+ vi.runAllTimers();
+ expect(readText()).toBe("Copied");
+
+ dispose();
+ expect(el.classList.contains("slot-text")).toBe(false);
+ expect(el.textContent).toBe("Copied");
+ });
+
+ it("does not pin initial options as defaults for later updates", () => {
+ let setText!: (text: string) => string;
+ let setOptions!: (options: SlotOptions | undefined) => void;
+
+ createRoot((dispose) => {
+ const [text, setLabel] = createSignal("Copy");
+ const [options, setNextOptions] = createSignal({
+ color: "red",
+ });
+ setText = setLabel;
+ setOptions = setNextOptions;
+
+ solidSlotText(el, () => ({ text: text(), options: options() }));
+
+ setOptions(undefined);
+ setText("Done");
+
+ expect(hasTintedFace()).toBe(false);
+
+ dispose();
+ });
+ });
+});
+
+describe("Svelte adapter", () => {
+ it("updates text through the action lifecycle", () => {
+ const action = svelteSlotText(el, { text: "Copy" });
+ expect(readText()).toBe("Copy");
+
+ action.update({ text: "Copied" });
+ vi.runAllTimers();
+ expect(readText()).toBe("Copied");
+
+ action.destroy();
+ expect(el.classList.contains("slot-text")).toBe(false);
+ expect(el.textContent).toBe("Copied");
+ });
+
+ it("does not pin initial options as defaults for later updates", () => {
+ const action = svelteSlotText(el, {
+ text: "Copy",
+ options: { color: "red" },
+ });
+
+ action.update({ text: "Done" });
+
+ expect(hasTintedFace()).toBe(false);
+
+ action.destroy();
+ });
+});
diff --git a/vitest.config.ts b/vitest.config.ts
index d22e699..9974afc 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -1,6 +1,9 @@
import { defineConfig } from "vitest/config";
export default defineConfig({
+ resolve: {
+ conditions: ["browser"],
+ },
test: {
environment: "happy-dom",
},