svelte-weave Svelte Themes

Svelte Weave

A minimal, highly customizable Svelte component library — zero CSS framework required, zero configuration needed to look good.

svelte-weave

A minimal, highly customizable Svelte component library — zero CSS framework required, zero configuration needed to look good.


Why svelte-weave?

Most Svelte UI libraries make you choose between two bad deals:

Library The problem
Tailwind-first (ShadCN, Skeleton) Pollutes your markup with utility classes, requires Tailwind as a hard runtime dependency, and breaks if you're not on a Tailwind project
Framework-bundled (Flowbite, daisyUI) Ships megabytes of CSS you don't control, is painful to theme beyond their provided presets, and forces you into their visual language

svelte-weave takes a different approach:

  • Zero framework required. Drop a single CSS import and you're done. No tailwind.config.js changes, no CSS preprocessor, no purge setup. It works in plain Svelte, SvelteKit, or any build tool.
  • One variable rethemes everything. Override --sw-primary and the entire primary color scale (50–950 shades, hover, active, foreground) updates automatically via color-mix() — no Sass, no build step.
  • Dark mode that actually works. Three-layer system: OS preference respected with zero JS, manual override with a class, FOUC prevention built in. No flash, no jank, works on every edge runtime.
  • Edge-runtime safe. No window or document access without guards. Deploys to Vercel, Netlify, and Cloudflare Pages out of the box.
  • Svelte 4 classic syntax. Full compatibility with Svelte 4 apps and Svelte 5 apps (via the built-in legacy compat layer). No runes required.
  • Optional Tailwind plugin. If you are on Tailwind, a single plugin import maps your entire Tailwind theme to --sw-* vars so components match your design system automatically.

Installation

npm install svelte-weave

Peer dependency: Svelte 4 or later.


Quick Start

1. Import the stylesheet

// In your layout, root +layout.svelte, or global CSS entry point:
import 'svelte-weave/styles';

Or link it in src/app.html:

<link rel="stylesheet" href="/node_modules/svelte-weave/dist/styles/index.css" />

2. Use components

<script>
  import { Button, Input, Card, Alert } from 'svelte-weave';
</script>

<Card padded>
  <Input label="Email" placeholder="[email protected]" />
  <Button variant="primary" fullWidth>Sign in</Button>
</Card>

<Alert variant="success" title="All good!">Your changes were saved.</Alert>

3. (Optional) Add the FOUC-prevention script for dark mode

Paste this into src/app.html immediately before %sveltekit.head%:

<script>(function(){try{var t=localStorage.getItem('sw-theme');if(t==='dark'){document.documentElement.classList.add('dark');}else if(t==='light'){document.documentElement.classList.add('light');}else if(window.matchMedia('(prefers-color-scheme: dark)').matches){document.documentElement.classList.add('dark');}}catch(e){}})();</script>

This prevents a flash of the wrong theme on first load.


Component Library

Foundations

Component Description
Button Full-featured button with variants, sizes, loading state, icon slots, click effects, and optional link rendering via href
Badge Inline label pill for status, counts, and tags
Avatar User avatar from image, initials, or fallback
Spinner Accessible loading indicator
Progress Progress bar with optional label, striped/animated variants
Divider Horizontal or vertical rule with optional centered label
Heading Semantic h1–h6 with independent visual sizing
Text Versatile text block: body, lead, caption, overline, inline code
Link Accessible anchor with underline control and external link support

Form & Inputs

Component Description
Input Text input with label, helper text, error state, icon slots
Textarea Multi-line input with optional auto-resize
Checkbox Single checkbox with indeterminate support
RadioGroup + Radio Accessible radio group; horizontal or vertical layout
Switch Toggle switch with label
Select Native-style select with option objects, placeholder, and error state

Feedback & Status

Component Description
Alert Inline alert banner: info / success / warning / danger variants, optional title, dismissible
toast + ToastContainer Programmatic toast notifications — call toast.success() from anywhere; render <ToastContainer /> once in your layout
Tooltip Hover tooltip with configurable placement and delay
Component Description
Tabs + Tab + TabPanel Tabbed content area — underline or pills variant, full-width option
Breadcrumbs Breadcrumb trail from an items array, custom separator support
Pagination Page selector with sibling windows and optional edge buttons
Menu + MenuItem Dropdown menu with 4 placement options and a danger item variant

Data Display

Component Description
Card Surface container with optional padding, border, shadow, hover effect, and link rendering
Table Data table with column definitions, optional sorting, striped/hoverable/compact/sticky header variants
Accordion + AccordionItem Collapsible Q&A / content sections; supports multi-open mode

Overlays & Layout

Component Description
Modal Accessible dialog with title, description, closable overlay, and 5 sizes
Drawer Side panel from any edge (left / right / top / bottom)
Popover Floating content attached to a trigger, 6 placement options

Theme System

Export Description
ThemeToggle Drop-in button to toggle dark / light / system mode
theme Svelte store — set(), toggle(), setDark(), setLight(), setSystem()
resolvedTheme Derived store that resolves 'system' to the actual 'light' or 'dark' value
themeScript Minified FOUC-prevention inline script string (embed in app.html)

Theming

All component styles use --sw-* CSS custom properties. Override them in your own CSS to retheme the entire library.

One-line color retheme

:root {
  --sw-primary: #7c3aed;   /* violet — the whole 50–950 scale updates automatically */
  --sw-radius:  0.25rem;   /* sharper corners everywhere */
}

Full variable reference

Category Variables
Primary --sw-primary, --sw-primary-50--sw-primary-950, --sw-primary-hover, --sw-primary-active, --sw-primary-foreground
Danger --sw-danger + same scale pattern
Success --sw-success + same scale pattern
Warning --sw-warning + same scale pattern
Secondary --sw-secondary, --sw-secondary-hover, --sw-secondary-foreground
Neutral --sw-text, --sw-text-muted, --sw-bg, --sw-border, --sw-ring
Shape --sw-radius, --sw-radius-sm, --sw-radius-lg, --sw-radius-full
Spacing --sw-spacing-xs--sw-spacing-xl
Typography --sw-font-size-sm/md/lg, --sw-font-weight-normal/medium/semibold
Motion --sw-transition (150ms), --sw-transition-slow (300ms)
Shadows --sw-shadow-sm, --sw-shadow, --sw-shadow-lg

Dark Mode

svelte-weave ships a complete three-layer dark mode system:

Layer How it works JS required?
OS preference @media (prefers-color-scheme: dark) on :root:not(.light) No
Manual dark Add .dark class to <html> Yes
Manual light Add .light class to <html> (prevents OS override) Yes

Using the theme store and toggle

<script>
  import { ThemeToggle, theme, resolvedTheme } from 'svelte-weave';
</script>

<!-- Drop-in toggle button -->
<ThemeToggle />

<!-- With system option (three-way: light → dark → system) -->
<ThemeToggle showSystem />

<!-- With a text label in the button -->
<ThemeToggle showSystem>Theme</ThemeToggle>

<!-- Programmatic control -->
<button on:click={theme.setDark}>Force dark</button>
<button on:click={theme.setSystem}>Follow OS</button>

<!-- Read the current value -->
<p>Current theme: {$resolvedTheme}</p>

Store API

import { theme, resolvedTheme } from 'svelte-weave';

theme.set('dark');      // set explicitly
theme.set('light');
theme.set('system');    // follow OS; removes localStorage entry
theme.toggle();         // flip light ↔ dark
theme.setDark();
theme.setLight();
theme.setSystem();

$theme          // 'light' | 'dark' | 'system'
$resolvedTheme  // 'light' | 'dark' — never 'system'

Animation System

Scroll reveal (use:reveal)

Animates an element in when it enters the viewport using IntersectionObserver. SSR-safe and honours prefers-reduced-motion.

<script>
  import { reveal } from 'svelte-weave';
</script>

<div use:reveal>Fades in on scroll (default: fadeIn)</div>
<div use:reveal={{ preset: 'slideUp', delay: 150 }}>Slides up with 150 ms delay</div>
<div use:reveal={{ preset: 'scaleIn', once: false }}>Re-animates on every re-entry</div>

Built-in reveal presets:

Preset Effect
fadeIn (default) Opacity 0 → 1
slideUp Slides up + fade
slideDown Slides down + fade
slideLeft Slides in from right + fade
slideRight Slides in from left + fade
scaleIn Scale 0.92 → 1 + fade
blurIn Blur → sharp + fade

Options:

Option Default Description
preset 'fadeIn' Which animation to use
delay 0 Milliseconds before the animation starts
once true Whether to animate only the first time
threshold 0.15 How much of the element must be visible (0–1)

Click effects (use:clickEffect)

Short feedback animation triggered on every click. Works on any element or via the <Button clickEffect="..."> prop.

<script>
  import { clickEffect } from 'svelte-weave';
  import { Button } from 'svelte-weave';
</script>

<button use:clickEffect="ripple">Material ripple</button>
<Button clickEffect="bounce">Bounce on click</Button>

Built-in click presets:

Preset Effect
pulse Quick compress + release
bounce Compress → overshoot → settle
shake Horizontal shake
ripple Material-style ripple from cursor position

Custom presets

import { registerRevealPreset, registerClickPreset } from 'svelte-weave';

// 1. Define your @keyframes + class in your own CSS
// 2. Register the name → class mapping:
registerRevealPreset('spinIn', { className: 'my-anim-spin' });
registerClickPreset('flash',   { className: 'my-click-flash' });

// 3. Use it:
//    <div use:reveal={{ preset: 'spinIn' }}>...</div>
//    <Button clickEffect="flash">Flash</Button>

CSS animation variables

:root {
  --sw-anim-duration:    400ms;
  --sw-anim-easing:      cubic-bezier(0.4, 0, 0.2, 1);
  --sw-anim-delay:       0ms;
  --sw-anim-distance:    1.5rem;
  --sw-anim-scale-from:  0.92;
  --sw-anim-blur-amount: 8px;
}

/* Stagger a list of cards */
.card:nth-child(1) { --sw-anim-delay: 0ms;   }
.card:nth-child(2) { --sw-anim-delay: 100ms; }
.card:nth-child(3) { --sw-anim-delay: 200ms; }

Icon System

Components that accept icons (Button, Input, etc.) take an IconInput — any of three forms:

Form Example
SVG string "<svg viewBox='0 0 24 24'>...</svg>"
Registered name "mdi:home"
Svelte component import HomeIcon from './HomeIcon.svelte'
<!-- SVG string inline -->
<Button iconLeft="<svg viewBox='0 0 24 24' fill='currentColor'><path d='M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z'/></svg>">
  Home
</Button>

<!-- Registered icon name (needs a resolver, see below) -->
<Button iconLeft="mdi:home" iconRight="mdi:arrow-right">Go Home</Button>

<!-- Svelte component -->
<Button iconLeft={HomeIcon}>Home</Button>

<!-- Standalone icon -->
<WeaveIcon icon="mdi:star" size={24} />
<WeaveIcon icon={StarIcon} class="text-yellow-500" />

Registering an icon resolver

Call this once at app startup (e.g. in your root +layout.svelte):

import { registerIconResolver } from 'svelte-weave';

const myIcons: Record<string, string> = { 'my:home': '<svg>...</svg>' };
registerIconResolver((name) => myIcons[name] ?? null);

With Iconify:

import { registerIconResolver } from 'svelte-weave';
import { getIcon, renderSVG } from '@iconify/utils';
import icons from '@iconify-json/mdi/icons.json';

registerIconResolver((name) => {
  if (!name.startsWith('mdi:')) return null;
  const data = getIcon(icons, name.slice(4));
  return data ? renderSVG(data) : null;
});

Tailwind CSS Integration (Optional)

If your project uses Tailwind, a single plugin import maps your Tailwind color theme to --sw-* vars automatically.

// tailwind.config.js
import { weavePlugin } from 'svelte-weave/tailwind';

export default {
  plugins: [weavePlugin],
};

Custom color mapping

import { createWeavePlugin } from 'svelte-weave/tailwind';

export default {
  plugins: [
    createWeavePlugin({
      primaryColor: 'violet',
      dangerColor:  'rose',
      successColor: 'emerald',
      warningColor: 'orange',
    }),
  ],
};

Utilities

cn(...classes) — class merge helper

Merges class strings, filtering falsy values. Useful for consumer-side class composition.

import { cn } from 'svelte-weave';

const cls = cn('base-class', condition && 'conditional', userClass);

use:clickOutside — click-outside Svelte action

Dispatches a clickoutside event when the user clicks outside the element. Useful for closing dropdowns or menus without a full <Modal>.

<script>
  import { clickOutside } from 'svelte-weave';
  let open = true;
</script>

<div use:clickOutside on:clickoutside={() => open = false}>
  <!-- dropdown content -->
</div>

Package Exports

svelte-weave           → components, theme system, icon system, animations, utilities
svelte-weave/tailwind  → Tailwind CSS plugin
svelte-weave/styles    → CSS custom properties + base reset

Dev & Contributing

# Start the component playground
npm run dev

# TypeScript + Svelte type checking
npm run check

# Build the publishable package → ./dist/
npm run package

# Build the SvelteKit demo app
npm run build

Compatible with npm, pnpm, yarn, and bun.


License

MIT © svelte-weave contributors

Top categories

Loading Svelte Themes