Opinionated design system for Svelte with Tailwind CSS

📦 Installation

# Create Vite + Svelte project
npm create vite@latest my-app -- --template svelte-ts
cd my-app
 
# Install dependencies
npm install @skeletonlabs/skeleton @skeletonlabs/skeleton-svelte
npm install -D tailwindcss @tailwindcss/vite

⚙️ Configuration

vite.config.ts

import { defineConfig } from "vite"
import { svelte } from "@sveltejs/vite-plugin-svelte"
import tailwindcss from "@tailwindcss/vite"
 
export default defineConfig({
  plugins: [
    tailwindcss(), // ⚠️ MUST come BEFORE svelte
    svelte(),
  ],
})

src/app.css

@import "tailwindcss";
@import "@skeletonlabs/skeleton";
@import "@skeletonlabs/skeleton/themes/skeleton";

index.html

<body data-theme="skeleton"></body>

🎨 Presets (Button/Card Styling)

PatternExampleUse Case
preset-filled-{color}-{shade}preset-filled-primary-500Solid background
preset-tonal-{color}preset-tonal-secondaryMuted/subtle
preset-outlined-{color}-{shade}preset-outlined-tertiary-500Border only
preset-gradient-*Custom gradientGradient fills

Colors

primary · secondary · tertiary · success · warning · error · surface

Shades

50 · 100 · 200 · 300 · 400 · 500 · 600 · 700 · 800 · 900 · 950


🧩 Core Classes

Buttons

<button class="btn preset-filled-primary-500">Primary</button>
<button class="btn preset-tonal-secondary">Subtle</button>
<button class="btn preset-outlined-error-500">Danger</button>
<button class="btn-icon preset-filled-surface-500">🔔</button>

Cards

<div class="card preset-filled-surface-100-900 p-4">
  <h3 class="h3">Card Title</h3>
  <p>Card content</p>
</div>
 
<div class="card p-4 preset-tonal-primary">Tonal Card</div>
<div class="card p-4 preset-outlined-secondary-500">Outlined</div>

Typography

<h1 class="h1">Heading 1</h1>
<h2 class="h2">Heading 2</h2>
<h3 class="h3">Heading 3</h3>
<h4 class="h4">Heading 4</h4>
<p class="text-surface-600-300-token">Muted text</p>

🔮 Svelte 5 Runes + Skeleton

Counter Example

<script lang="ts">
  let count = $state(0);
  let doubled = $derived(count * 2);
</script>
 
<div class="card preset-filled-surface-100-900 p-6 text-center">
  <p class="text-4xl font-bold text-primary-500">{count}</p>
  <p class="text-sm text-surface-500">Doubled: {doubled}</p>
 
  <div class="flex gap-4 justify-center mt-4">
    <button class="btn preset-filled-primary-500" onclick={() => count++}>
      +1
    </button>
    <button class="btn preset-outlined-secondary-500" onclick={() => count = 0}>
      Reset
    </button>
  </div>
</div>

State in .svelte.ts modules

// src/state/counter.svelte.ts
export const counter = $state({ count: 0 })
 
export function increment() {
  counter.count++
}

🎭 Themes

Built-in Themes

@import "@skeletonlabs/skeleton/themes/skeleton";
@import "@skeletonlabs/skeleton/themes/wintry";
@import "@skeletonlabs/skeleton/themes/modern";
@import "@skeletonlabs/skeleton/themes/hamlindigo";
@import "@skeletonlabs/skeleton/themes/rocket";
@import "@skeletonlabs/skeleton/themes/sahara";
@import "@skeletonlabs/skeleton/themes/seafoam";
@import "@skeletonlabs/skeleton/themes/vintage";
@import "@skeletonlabs/skeleton/themes/crimson";
@import "@skeletonlabs/skeleton/themes/gold-nouveau";

Theme Switching

<body data-theme="wintry"></body>
<script>
  function setTheme(name: string) {
    document.body.dataset.theme = name;
  }
</script>

📐 Layout Helpers

<!-- Container -->
<div class="container mx-auto p-4">
 
<!-- Grid -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
 
<!-- Flex -->
<div class="flex items-center justify-between gap-4">
 
<!-- Space-Y -->
<div class="space-y-4">

🔗 Resources


⚡ Quick Reference

NeedSolution
Solid buttonbtn preset-filled-primary-500
Subtle buttonbtn preset-tonal-secondary
Outlined buttonbtn preset-outlined-error-500
Cardcard preset-filled-surface-100-900 p-4
Headingh1, h2, h3, h4
Muted texttext-surface-600-300-token
Primary colortext-primary-500
Icon buttonbtn-icon preset-filled-surface-500

Created: Dec 2024 | Skeleton v2.10+ | Svelte 5 | Tailwind 4