Tutorials
Svelte tutorial
Lily's Svelte library is the canonical implementation: Svelte 5 runes,
typed props, rest-prop spread, and two-way binding with bind:value.
What you'll build
A small contact form — labelled input, submit button, two-way binding, real keyboard and screen-reader behaviour — first bare, then styled, in about 10 minutes.
Before you start
You need git, Node 22 or later, and pnpm 10 or later. Any Svelte 5
project works — a fresh npx sv create app is fine.
Step 1 — Get the code
git clone https://github.com/LilyDesignSystem/lily-design-system-svelte-headless
cd lily-design-system-svelte-headless
pnpm install
pnpm test # vitest — thousands of component cases
pnpm run storybook # browse all 490 components Components live in src/lib/components/{Name}/, one
directory per component with the .svelte file, its tests,
its stories, and its docs side by side. Copy the directories you need
into your app, or depend on the repo and import directly.
Step 2 — Your first component
<script>
import Button from "$lib/components/Button/Button.svelte";
function save() { console.log("saved"); }
</script>
<Button onclick={save}>Save</Button> Renders <button class="button" type="button">. The
props are typed: type, disabled, pressed (renders aria-pressed for toggle
buttons), label (an aria-label override), and
any extra attribute spreads onto the root.
Step 3 — Compose a small form
<script>
import Form from "$lib/components/Form/Form.svelte";
import Field from "$lib/components/Field/Field.svelte";
import Label from "$lib/components/Label/Label.svelte";
import TextInput from "$lib/components/TextInput/TextInput.svelte";
import Button from "$lib/components/Button/Button.svelte";
let name = $state("");
</script>
<Form onsubmit={handleSubmit}>
<Field>
<Label for="name">Full name</Label>
<TextInput id="name" label="Full name" bind:value={name} required />
</Field>
<Button type="submit">Save</Button>
</Form> TextInput's value is a $bindable rune prop, so bind:value gives you two-way binding straight
away.
Check your work: run the app and the form looks plain — exactly as intended: styling is still ahead. Press Tab: focus moves through every control, the label is announced, and submit works. The behaviour is done; the look is yours, and it's next.
Step 4 — Style it
Everything above renders as pure structure, ready for your styles.
Style the class hooks — .button, .text-input, .label, .field — in your global stylesheet, or link a ready-made
theme in app.html:
<link rel="stylesheet" href="/themes/dark.css" /> Per-instance tweaks go through the class prop, which appends
to the base hook: <Button class="wide"> renders class="button wide".
Step 5 — See it all working
The SvelteKit example app is the same catalog fully styled, with live demos, Playwright e2e tests, and an axe-core accessibility baseline:
git clone https://github.com/LilyDesignSystem/lily-design-system-svelte-sveltekit-examples
cd lily-design-system-svelte-sveltekit-examples
pnpm install && pnpm run dev Next steps
- Theming — runtime switching with the Svelte theme-select helper.
- Preference helpers — language and text size.
- Component catalog — props, ARIA, and keyboard per component.
Questions along the way? The help page is full of answers — and if a step could be clearer, tell us and we'll gladly improve the tutorial.