Why Svelte Changes the Component Game
Svelte, introduced by Rich Harris in 2016, takes a fundamentally different path from mainstream frameworks like Angular, React, and Vue. Instead of shipping a runtime that interprets component code in the browser, Svelte compiles components down to highly efficient imperative JavaScript at build time. This compiler-driven approach results in smaller bundle sizes and faster runtime performance — a positioning Rich laid out in detail in his talk "Rethinking Reactivity."
For building full applications, Svelte pairs with SvelteKit, its official meta-framework built on Vite. SvelteKit handles routing, server-side rendering, HTTP requests, and other application-level concerns, and is approaching its 1.0 release. However, when the team at Brain & Bones looked to migrate internal projects from Angular to SvelteKit, the UI component landscape felt sparse.
Svelte has wrappers for general-purpose libraries, such as Smelte for Material Design and Svelma for Bulma. What wasn't available was a toolkit that deeply integrated with Svelte's reactive model while also offering first-class support for Tailwind CSS.
The Tailwind Approach to Styling
Tailwind, first released in 2019, approaches styling from a utility-first angle. Rather than authoring CSS in external stylesheets and maintaining naming conventions, you apply pre-built shorthand classes directly in your markup. The result is a customizable design system that travels with your HTML.
The distinction between classic CSS and utility classes becomes clear with concrete examples:
Standard Utility Patterns
In ordinary CSS you might create flex containers with custom spacing, manage colors via CSS variables, and hand-build grid layouts with media queries. Tailwind replaces these patterns with concise class attributes:
<div class="p-4">Foobar</div>
<div class="p-8">Foobar</div>
No additional stylesheet rules needed — Tailwind generates the CSS for you.
Working with Color
Where CSS often requires defining a full color palette and applying it via named variables, Tailwind ships a default palette scaled from 50 to 900, with shade 500 typically serving as the base color. Utility classes follow the pattern text-red-500, bg-blue-100, and so on, for instant access to the theme.
Grid layouts are likewise condensed. Standard CSS and markup for a simple responsive layout would involve multiple rules and breakpoints:
.grid-three-column {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
grid-column-gap: 16px;
grid-row-gap: 16px;
}
<div class="grid-three-column">
<div>Cell</div>
<div>Cell</div>
<div>Cell</div>
</div>
Tailwind expresses the same intent in a mark of markup:
<div class="grid grid-cols-3 gap-4">
<div>Cell</div>
<div>Cell</div>
<div>Cell</div>
</div>
These classes use REM units, so gap-4 equates to 4 REM (16px). For workflow advantages, this approach really does change your pace:
- You can iterate live on layout, switching a grid from three to four columns without touching a stylesheet.
- A consistent color scale is available out of the box.
- Building up complex and responsive layouts happens inline, not in disconnected CSS files.
Filling the Gap: Svelte + Tailwind, Integrated
Heads up — this is where a genuinely valuable project for the Svelte community comes in. This Skeleton is not a dead component for a page skeleton screen, but a UI library built specifically to pair Svelte and Tailwind. It's open-source, is developed by the team led by Brain & Bones, and was launched after that same team had difficulty migrating its Angular projects over to SvelteKit.
The idea was formed around what the ecosystem lacked: a set of Svelte components that are deeply reactive, tailor-made for the new framework's way of thinking, but that still work with Tailwind's utility model to handle styling decisions.
While several UI options exist that wrap Material Design or Bulma, the ability to combine Svelte's native component model and the type of fine-grained visual systems that come from utility-based styling was uncharted territory. Skeleton went open-source under the assumption that the rest of Svelte's community would face this same integration gap. The practical bits, like installing it into a SvelteKit project with Tailwind, are handled across a foundation that was built from the start to treat both tools as equals.
At the time of this writing, SvelteKit is still early in its lifecycle, approaching 1.0 but not yet stable. Yet the gap is already becoming clear for anyone who wants to ship modern web apps using the Svelte compiler rather than a heavy runtime. Skeleton aims to be a reference for how that future looks, giving current and aspiring Svelte developers a way to bring both great aesthetics and reactive components into the same app.
Scaffolding a New Skeleton Project
Start by creating a fresh SvelteKit application. Run the following commands in your terminal, substituting a name you prefer for my-skeleton-app. When prompted, choose TypeScript and a barebones (skeleton) project template:
npm create svelte@latest my-skeleton-app
cd my-skeleton-app
npm install
npm run dev -- --open
The commands create the SvelteKit project, move your terminal into the project directory, install all required dependencies, and start a local dev server. The -- --open flag automatically opens the following address in your browser:
http://localhost:5173/
In your terminal, press Ctrl + C to stop the server. You’ll restart it shortly.
Next, install Tailwind using Svelte-add, which simplifies the process. Running the following commands installs the latest Tailwind version, creates /src/app.css for global styles, and generates the necessary tailwind.config.cjs. After that, install the Skeleton package via NPM:
npx svelte-add@latest tailwindcss
npm install
npm i @brainandbones/skeleton --save-dev
Configuring Tailwind and the Theme
Open tailwind.config.cjs in the project root and add the following to ensure Skeleton works correctly with Tailwind:
module.exports = {
content: [
// ...
'./node_modules/@brainandbones/skeleton/**/*.{html,js,svelte,ts}'
],
plugins: [
require('@brainandbones/skeleton/tailwind.cjs')
]
}
The content section tells the compiler about Tailwind classes inside Skeleton components, while the plugins section uses a Skeleton file to prepare for the theme you’ll implement next.
Your theme controls the visual appearance of all components and adapts for dark mode, while also exposing Tailwind utility classes for your custom color palette. To start, use Skeleton’s default theme by copying the CSS into a new file at /src/theme.css:
:root {
/* --- Skeleton Theme --- */
/* primary (emerald) */
--color-primary-50: 236 253 245;
--color-primary-100: 209 250 229;
--color-primary-200: 167 243 208;
--color-primary-300: 110 231 183;
--color-primary-400: 52 211 153;
--color-primary-500: 16 185 129;
--color-primary-600: 5 150 105;
--color-primary-700: 4 120 87;
--color-primary-800: 6 95 70;
--color-primary-900: 6 78 59;
/* accent (indigo) */
--color-accent-50: 238 242 255;
--color-accent-100: 224 231 255;
--color-accent-200: 199 210 254;
--color-accent-300: 165 180 252;
--color-accent-400: 129 140 248;
--color-accent-500: 99 102 241;
--color-accent-600: 79 70 229;
--color-accent-700: 67 56 202;
--color-accent-800: 55 48 163;
--color-accent-900: 49 46 129;
/* warning (rose) */
--color-warning-50: 255 241 242;
--color-warning-100: 255 228 230;
--color-warning-200: 254 205 211;
--color-warning-300: 253 164 175;
--color-warning-400: 251 113 133;
--color-warning-500: 244 63 94;
--color-warning-600: 225 29 72;
--color-warning-700: 190 18 60;
--color-warning-800: 159 18 57;
--color-warning-900: 136 19 55;
/* surface (gray) */
--color-surface-50: 249 250 251;
--color-surface-100: 243 244 246;
--color-surface-200: 229 231 235;
--color-surface-300: 209 213 219;
--color-surface-400: 156 163 175;
--color-surface-500: 107 114 128;
--color-surface-600: 75 85 99;
--color-surface-700: 55 65 81;
--color-surface-800: 31 41 55;
--color-surface-900: 17 24 39;
}
Note: Colors are converted from Hex to RGB to properly support Tailwind’s background opacity.
Configure SvelteKit to use this theme by opening your root layout file at /src/routes/__layout.svelte. Declare the theme just before the global stylesheet app.css.
import '../theme.css'; // <--
import '../app.css';
To improve appearance, add basic <body> styles that respect system light or dark mode settings. Add the following to /src/app.css:
body { @apply bg-surface-100 dark:bg-surface-900 text-black dark:text-white p-4; }
Adding Your First Component
Now implement a Skeleton component. Open /src/routes/index.svelte add the following code, replacing the file’s entire contents:
<script lang="ts">
import { Button } from '@brainandbones/skeleton';
</script>
<Button variant="filled-primary">Skeleton</Button>
Restart the local server with npm run dev and navigate to http://localhost:5173/. A Skeleton Button component should appear on the page.
Component Customization
Like any Svelte component, Skeleton components accept custom props. For instance, the Button component’s variant prop lets you select from canned options tailored to your theme. Change the value to filled-accent and the button shifts from your theme’s primary color (emerald) to the accent color (indigo).
Many prop values mirror Tailwind class names — they are passed verbatim to the component template. This means you can apply any Tailwind theme color, standard color, or even one-off values using Tailwind’s arbitrary value syntax:
<!-- Using our theme color -->
<Button background="bg-accent-500">Accent</Button>
<!-- Using Tailwind colors -->
<Button background="bg-orange-500">Orange</Button>
<!-- Using Tailwind's arbitrary value syntax -->
<Button background="bg-[#BADA55]">Arbitrary</Button>
You’re not restricted to preset props, either. Provide any valid CSS classes via the standard class attribute:
<Button variant="filled-primary" class="py-10 px-20">Big!</Button>
Dialog Utility and Reactive Stores
One advantage of framework-specific libraries is deep integration with Svelte’s core features. Consider Skeleton’s dialog system, which leans on Svelte’s reactive capabilities.
Add a Dialog component in the global scope by opening /src/routes/__layout.svelte and placing the following above the <slot /> element:
<script lang="ts">
// ...
import { Dialog } from '@brainandbones/skeleton';
</script>
<!-- Add the Dialog component here -->
<Dialog />
<slot />
Note: The Dialog component stays hidden on the page by default.
Update the home page to trigger a dialog by replacing all of /src/routes/index.svelte with:
<script lang="ts">
import { Button, dialogStore } from '@brainandbones/skeleton';
import type { DialogAlert } from '@brainandbones/skeleton/Notifications/Stores';
function triggerDialog(): void {
const d: DialogAlert = {
title: ‘Welcome to Skeleton.’,
body: ‘This is a standard alert dialog.’,
};
dialogStore.trigger(d);
}
</script>
<Button variant="filled-primary" on:click={() => { triggerDialog() }}>Trigger Dialog</Button>
Clicking the button in your browser reveals the dialog message on screen.
Skeleton achieves this with writable stores, which manage global state reactively. When you click the button, the store receives a dialog instance and acts as a queue. Because stores are reactive, the Dialog component listens for updates and shows the top-most dialog when one exists. When dismissed, it displays the next dialog in the queue, or returns to its hidden state if empty.
The following mock helps visualize the dialog store’s data structure:
dialogStore = [
// dialog #1, <-- top items the queue, shown on screen
// dialog #2, <-- the next dialog in line
// dialog #3, <-- bottom of the queue, the last added
];
That tight coupling is the strength of framework-specific tooling — structure, styling, and behavior all bundled together.
Availability and Resources
Skeleton is currently in early access beta. Visit the official documentation for detailed guides on the full suite of components and utilities. For code contributions, bug reports, or walkthrough requests, check Skeleton’s GitHub repo. Contributors also gather in the Discord community to show off projects or discuss development. Skeleton was founded by Brain & Bones.




