Svelte’s scoped CSS is fantastic for encapsulated components. But when your app grows, writing custom CSS for every button, card, and margin tweak can slow you down.
That’s where Tailwind CSS shines. It’s a utility-first framework that gives you ready-to-use classes for spacing, typography, colors, and dark mode — all inline in your markup. Combine it with Svelte’s scoped CSS, and you get the best of both worlds: rapid prototyping + maintainable components.
In this final part, you’ll learn how to:
Set up Tailwind in SvelteKit.
Style components with Tailwind utilities.
Implement dark mode with one class toggle.
Mix Tailwind utilities with scoped CSS.
Avoid pitfalls like “class soup.”
We’ll end with a styled mini-project that combines everything.
Step 12: Tailwind + Svelte — A Match Made in Heaven 💘
By now, you’ve seen how powerful Svelte’s built-in styling is. Scoped CSS, :global
, and class directives cover a lot of ground.
But sometimes you don’t want to write a custom CSS rule for every little thing:
- Spacing (
margin
,padding
) - Colors and backgrounds
- Rounded corners, shadows, hover effects
That’s where Tailwind CSS shines. It’s a utility-first CSS framework that gives you ready-made classes like:
-
p-4
→ padding -
bg-blue-500
→ background color -
rounded-lg
→ rounded corners
Instead of switching back and forth between HTML and CSS files, you style inline with classes right in your Svelte markup.
Before (manual CSS)
<!-- Button.svelte -->
<button class="btn">Click Me</button>
<style>
.btn {
background: royalblue;
color: white;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
cursor: pointer;
}
</style>
After (Tailwind)
<!-- Button.svelte -->
<button class="bg-blue-500 text-white px-4 py-2 rounded-lg">
Click Me
</button>
No <style>
block needed — Tailwind handles it all with utilities.
👉 The point isn’t to abandon Svelte’s styling — it’s that Tailwind makes common patterns faster and more consistent, while Svelte’s scoped CSS is still great for one-off component styles.
Step 13: Setting Up Tailwind in SvelteKit
Tailwind integrates seamlessly with SvelteKit. Let’s walk through it step by step.
1. Install Tailwind and friends
In your project root, run:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init tailwind.config.cjs -p
That generates two files:
-
tailwind.config.cjs
→ Tailwind config -
postcss.config.cjs
→ PostCSS config
2. Configure Tailwind
Open tailwind.config.cjs
and make sure it scans your .svelte
files:
// tailwind.config.cjs
module.exports = {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {}
},
plugins: []
};
This tells Tailwind to purge unused CSS from all your Svelte components.
3. Add Tailwind to your global CSS
Create (or edit) src/app.css
and add the Tailwind layers:
@tailwind base;
@tailwind components;
@tailwind utilities;
4. Import CSS into your root layout
In SvelteKit, the root layout is src/routes/+layout.svelte
. Import app.css
there:
<script>
import "../app.css";
</script>
<slot />
This ensures Tailwind styles are available everywhere in your app.
✅ That’s it! Now you can start using Tailwind classes directly in any .svelte
file.
Example:
<button class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg">
Click Me
</button>
Step 14: Using Tailwind in Components
Now that Tailwind is set up, let’s use it inside a Svelte component.
You don’t need a <style>
block here — just apply Tailwind classes directly in the class
attribute.
Button.svelte
<script>
export let label = "Click Me";
</script>
<button class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg">
{label}
</button>
Then import it into a page (e.g. src/routes/+page.svelte
):
<script>
import Button from '$lib/Button.svelte';
</script>
<h1>Welcome</h1>
<Button label="Get Started" />
✅ This instantly gives you a nicely styled button with:
- Blue background (
bg-blue-500
) - Hover state (
hover:bg-blue-600
) - White text, padding, and rounded corners
No extra CSS needed.
⚡ This workflow is fantastic for prototyping or for teams that want consistent, production-ready styles without writing lots of custom CSS.
Step 15: Dark Mode with Tailwind
Remember theming from Step 10? Tailwind makes dark mode ridiculously easy.
First, enable it in tailwind.config.cjs
:
module.exports = {
darkMode: 'class', // or 'media'
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: { extend: {} }
};
Now you can use the dark:
prefix to define alternate styles.
DarkModeDemo.svelte
<script>
let dark = false;
function toggleTheme() {
dark = !dark;
document.body.classList.toggle("dark", dark);
}
</script>
<button
class="px-4 py-2 mb-4 bg-gray-200 dark:bg-gray-700 rounded-lg"
on:click={toggleTheme}
>
Toggle Dark Mode
</button>
<div class="p-4 bg-white text-black dark:bg-black dark:text-white rounded-lg">
Hello Dark Mode 👋
</div>
Usage: import DarkModeDemo
into a page (+page.svelte
) and try toggling.
✅ With just the dark:
prefix:
- Background and text colors flip instantly
- Works across the whole app if you apply
dark
tobody
👉 Tailwind basically turns dark mode into a one-liner, compared to manually managing CSS variables.
Step 16: Mixing Tailwind with Scoped CSS
Tailwind is amazing for layout, spacing, and colors — but sometimes you need that extra polish (like smooth transitions, keyframes, or fine-grained overrides). That’s where Svelte’s scoped CSS still shines.
The best practice is:
👉 Use Tailwind for 90% of styles.
👉 Add scoped CSS when utilities can’t express what you need.
Example: Combining Both
ActiveButton.svelte
<script>
export let active = false;
function toggle() {
active = !active;
}
</script>
<button
class="px-4 py-2 rounded-lg text-white"
class:bg-blue-500={active}
class:bg-gray-400={!active}
on:click={toggle}
>
{active ? "Active" : "Inactive"}
</button>
<style>
/* Scoped CSS adds a smooth background transition */
button {
transition: background 0.3s ease;
}
</style>
Usage in a page:
<script>
import ActiveButton from '$lib/ActiveButton.svelte';
</script>
<ActiveButton />
Now when you click the button:
- Tailwind sets up the base look (
px-4
,rounded-lg
, colors). - Svelte’s scoped CSS makes the color fade smoothly instead of snapping.
This demonstrates the sweet spot: Tailwind gives you productivity, scoped CSS gives you control.
Step 17: Tailwind Gotchas ⚠️
Tailwind and Svelte work beautifully together, but here are a few pitfalls to watch for:
- Class soup (too many utilities in one element) Tailwind encourages stacking small classes, but it can get unreadable:
<div class="flex items-center justify-between px-4 py-2 bg-blue-500 rounded-lg shadow-md hover:bg-blue-600 text-white">
Header
</div>
✅ Fix: Extract into a component (e.g. <Header />
) or use Tailwind’s @apply
in a global stylesheet to bundle common combos.
-
Purging unused classes
Tailwind removes unused CSS at build time. If your
tailwind.config.cjs
doesn’t include*.svelte
, your styles may vanish in production.
Example config:
module.exports = {
content: ['./src/**/*.{html,js,svelte,ts}'], // ✅ includes .svelte
theme: { extend: {} },
plugins: []
}
- Scoped CSS vs Tailwind utilities Tailwind classes are global, while Svelte styles are scoped. Luckily, they don’t usually clash — but remember:
<div class="bg-blue-500">Hello</div>
<style>
div { background: red; } /* scoped → applies only here */
</style>
✅ Solution: Use Tailwind for general styles, scoped CSS only for fine-tuning this component.
👉 Rule of thumb: Use Tailwind for speed, scoped CSS for precision.
Step 18: Mini-Project — Colorful Card Grid 🃏
Let’s put everything together — scoped styles, Tailwind utilities, dark mode, and dynamic classes — to make a mini card grid.
1. Create a Card.svelte
component
src/lib/Card.svelte
<script>
export let title;
export let content;
export let color = "blue"; // accepts "blue" | "green" | "purple"
// Map colors to real Tailwind classes (important!)
const colorClasses = {
blue: "bg-blue-100 border-blue-500 text-blue-800",
green: "bg-green-100 border-green-500 text-green-800",
purple: "bg-purple-100 border-purple-500 text-purple-800"
};
</script>
<div
class={`p-6 rounded-xl shadow-lg transition border-l-4 ${colorClasses[color]}`}
>
<h2 class="text-xl font-bold mb-2">{title}</h2>
<p>{content}</p>
</div>
2. Use it in the main page
src/routes/+page.svelte
<script>
import Card from "$lib/Card.svelte";
</script>
<div class="min-h-screen bg-gradient-to-r from-pink-200 via-purple-200 to-indigo-200 p-10">
<h1 class="text-3xl font-bold mb-6 text-center text-indigo-700">
Colorful Cards Demo
</h1>
<div class="grid gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
<Card
title="Learn Svelte"
content="Build fast, reactive UIs."
color="blue"
/>
<Card
title="Style with Tailwind"
content="Utility-first classes keep CSS tidy."
color="green"
/>
<Card
title="Compose Components"
content="Cards, buttons, modals — all reusable."
color="purple"
/>
</div>
</div>
3. What this shows
✅ Tailwind utilities → spacing, shadows, gradients, responsive grid.
✅ Dynamic classes → different colors per card.
✅ Scoped CSS (optional) → if you want to add small tweaks.
✅ Dark mode ready → you can extend Card.svelte
with dark:
variants.
Result: A beautiful, colorful grid of cards instead of a plain page.
👉 This is a complete, working mini-project. Run your SvelteKit dev server, and you’ll see a gradient background + three colorful cards. 🎨
Wrapping Up
In this article, you learned how to style Svelte apps like a pro:
- Theming with CSS variables and Tailwind dark mode.
- Combining Tailwind with Svelte for maximum productivity.
You’ve now seen how Tailwind and Svelte complement each other — scoped CSS for fine control, utilities for rapid styling, and dark mode baked in. With these tools, you can build apps that are not only reactive but also beautifully styled and consistent at scale.
👉 That wraps up our styling series. Next, we’ll move on to state & data flow in Svelte: stores, context API, and advanced reactivity patterns.
👉 Next up: State & Data Flow — Stores, Context API, and Advanced Reactivity.
That’s where we’ll go beyond props and events into app-wide state management.
Top comments (0)