puresv
v0.1.0
Published
Pure Svelte 5 UI components styled with modern CSS. No runtime dependencies, no utility-class framework.
Maintainers
Readme
PureSV
Reusable Svelte 5 UI components styled with modern CSS.
No runtime dependencies. No Tailwind, no utility classes, no CSS-in-JS — just Svelte components, scoped styles, and CSS custom properties.
Install
pnpm add puresvPeer dependency: svelte@^5.
Usage
<script>
import { Button, Card, Badge } from "puresv";
</script>
<Card>
{#snippet header()}
Monthly report
{/snippet}
<Badge tone="success">paid</Badge>
{#snippet footer()}
Updated 2 minutes ago
{/snippet}
</Card>
<Button variant="outline" size="lg" onclick={() => save()}>Save</Button>Importing anything from puresv also loads the design tokens, so components
look right with no extra setup — each component imports theme.css itself, so
only the CSS you actually use gets bundled. If you only want the tokens:
import "puresv/theme.css";Theming
Every visual value is a CSS custom property prefixed --puresv-. Override any
of them in your own stylesheet — component styles live inside
@layer puresv, so plain (unlayered) CSS always wins:
:root {
--puresv-accent: oklch(0.62 0.19 275);
--puresv-radius-md: 0.25rem;
}Light/dark follows the OS by default. Force a palette with data-theme on
<html>:
<script>
let theme = $state("system");
$effect(() => {
const root = document.documentElement;
if (theme === "system") root.removeAttribute("data-theme");
else root.dataset.theme = theme;
});
</script>Both branches are plain custom-property overrides — no light-dark(), so the
toggle keeps working even when a build step (Lightning CSS, older targets)
downlevels modern color functions.
PureSV styles its own components only. Give your page the matching canvas:
body {
background-color: var(--puresv-canvas);
color: var(--puresv-text);
}Components
Button
| Prop | Type | Default |
| --------- | ----------------------------------------------------- | --------- |
| variant | "solid" \| "soft" \| "outline" \| "ghost" \| "link" | "solid" |
| size | "sm" \| "md" \| "lg" | "md" |
| href | string | — |
- solid — the primary action. Near-black ink fill, quiet hover, and a
scale(0.97)press. No lift, no colored glow. - soft — tinted accent background, for a secondary action that still needs to read as "accent".
- outline — bordered and transparent, for an action next to a solid one.
- ghost — no border, for toolbars and dense rows.
- link — text only. Sheds the height and padding from
sizebut keeps its font size.
Pass href to render an <a> with the same styling, for navigation that must
stay a real link. Also accepts every native <button> attribute (type,
disabled, aria-*, onclick, …) — they are forwarded with {...rest}.
Card
Optional header and footer snippets around a default body slot. Accepts
native <div> attributes.
Badge
| Prop | Type | Default |
| ------ | ------------------------------------------------------------- | ----------- |
| tone | "neutral" \| "accent" \| "success" \| "warning" \| "danger" | "neutral" |
| dot | boolean | false |
Toast
Render one <Toaster /> near the root of your layout and call toast() from
anywhere:
<Button onclick={() => toast({ title: "Saved", tone: "success" })}>Save</Button>
<Toaster />Options: title, description, tone (info | success | warning | danger)
and duration (ms, 0 keeps it on screen). Hovering a toast pauses its
countdown; dismiss(id) closes it early.
The full set
33 components, grouped the same way as the docs sidebar:
| Group | Components | | ---------- | ---------------------------------------------------------------------------------- | | Actions | Button | | Forms | Input, Textarea, Select, Checkbox, Radio, Rating, Switch, Slider, SegmentedControl | | Feedback | Alert, Progress, Spinner, Skeleton, Toast, EmptyState | | Display | Avatar, AvatarGroup, Badge, Card, Separator, Table, DescriptionList, Stat, Kbd | | Navigation | Breadcrumb, Tabs, Accordion, Pagination | | Overlays | Modal, Popover, Dropdown, Tooltip |
Every component ships with its own test and uses only design tokens.
Adding a component
- Create
src/lib/components/MyThing.svelte. - Wrap its
<style>rules in@layer puresv { … }so consumers can override them. - Style with
var(--puresv-*)tokens only — no hardcoded colors or spacing. - Re-export it from
src/lib/index.ts. - Add
MyThing.test.tsnext to it and runvp test. - Add a docs page under
src/routes/components/<name>, a link insrc/routes/_shared/nav.ts, and a card on the homepage.
Development
vp dev # showcase app with every component
vp test # component tests (Vitest + happy-dom)
vp check # format, lint, type check
pnpm run build # showcase build + library build into dist/Everything in src/lib ships. Everything in src/routes is the showcase app
and is not published.
Publishing
pnpm run build runs svelte-package (source of truth is src/lib) and
publint. Update version, then npm publish.
License
MIT
