DEV Community

Cover image for Tailwind CSS + Svelte: Utility-First Styling at Scale
Ali Aslam
Ali Aslam

Posted on

Tailwind CSS + Svelte: Utility-First Styling at Scale

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>
Enter fullscreen mode Exit fullscreen mode

After (Tailwind)

<!-- Button.svelte -->
<button class="bg-blue-500 text-white px-4 py-2 rounded-lg">
  Click Me
</button>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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: []
};
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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 />
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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" />
Enter fullscreen mode Exit fullscreen mode

✅ 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: {} }
};
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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 to body

👉 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>
Enter fullscreen mode Exit fullscreen mode

Usage in a page:

<script>
  import ActiveButton from '$lib/ActiveButton.svelte';
</script>

<ActiveButton />
Enter fullscreen mode Exit fullscreen mode

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:

  1. 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>
Enter fullscreen mode Exit fullscreen mode

✅ Fix: Extract into a component (e.g. <Header />) or use Tailwind’s @apply in a global stylesheet to bundle common combos.


  1. 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: []
   }
Enter fullscreen mode Exit fullscreen mode

  1. 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>
Enter fullscreen mode Exit fullscreen mode

✅ 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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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)