# Svelte Styling & Tailwind Integration
## Overview
Svelte provides scoped CSS by default and integrates seamlessly with Tailwind CSS. This guide covers both approaches and best practices.
---
## 🎨 Scoped CSS (Default)
### Basic Scoped Styles
```svelte
```
### Global Styles with :global()
```svelte
```
### CSS Custom Properties (Variables)
```svelte
```
---
## 💨 Tailwind CSS Integration
### Setup
```bash
# Install Tailwind
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```
### tailwind.config.js
```javascript
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
darkMode: 'class', // or 'media'
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
},
fontFamily: {
sans: ['Inter var', 'system-ui', 'sans-serif'],
},
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
],
};
```
### postcss.config.js
```javascript
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
```
### app.css
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Custom base styles */
@layer base {
body {
@apply bg-white dark:bg-gray-900 text-gray-900 dark:text-white;
}
h1, h2, h3, h4, h5, h6 {
@apply font-bold tracking-tight;
}
}
/* Custom component classes */
@layer components {
.btn {
@apply inline-flex items-center justify-center px-4 py-2
font-medium rounded-lg transition-colors
focus:outline-none focus:ring-2 focus:ring-offset-2;
}
.btn-primary {
@apply bg-primary-600 text-white hover:bg-primary-700
focus:ring-primary-500;
}
.btn-secondary {
@apply bg-gray-100 text-gray-700 hover:bg-gray-200
dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700;
}
.input {
@apply block w-full rounded-lg border border-gray-300
px-3 py-2 shadow-sm
focus:border-primary-500 focus:ring-1 focus:ring-primary-500
dark:border-gray-600 dark:bg-gray-800;
}
.card {
@apply rounded-xl bg-white dark:bg-gray-800
border border-gray-200 dark:border-gray-700
shadow-sm overflow-hidden;
}
}
/* Custom utilities */
@layer utilities {
.text-balance {
text-wrap: balance;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
}
```
---
## 🧩 Component Styling Patterns
### Pattern 1: Tailwind Component
```svelte
```
### Pattern 2: Class Merging with clsx/tailwind-merge
```bash
npm install clsx tailwind-merge
```
```typescript
// src/lib/utils/cn.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
```
```svelte
{@render children()}
```
### Pattern 3: CVA (Class Variance Authority)
```bash
npm install class-variance-authority
```
```typescript
// src/lib/components/Button/variants.ts
import { cva, type VariantProps } from 'class-variance-authority';
export const buttonVariants = cva(
// Base classes
'inline-flex items-center justify-center font-medium rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed',
{
variants: {
variant: {
primary: 'bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500',
secondary: 'bg-gray-100 text-gray-700 hover:bg-gray-200',
danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',
ghost: 'text-gray-600 hover:bg-gray-100',
link: 'text-primary-600 underline-offset-4 hover:underline'
},
size: {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4 text-sm',
lg: 'h-12 px-6 text-base',
icon: 'h-10 w-10'
}
},
defaultVariants: {
variant: 'primary',
size: 'md'
}
}
);
export type ButtonVariants = VariantProps;
```
```svelte
```
---
## 🌓 Dark Mode
### With Tailwind
```svelte
```
### Prevent Flash of Wrong Theme
```html
%sveltekit.head%
%sveltekit.body%
```
---
## 📐 Responsive Design
### Tailwind Breakpoints
```svelte
{#each items as item}
{item.name}
{/each}
Responsive Heading
```
### Container Queries (Tailwind v3.4+)
```svelte
```
---
## ✨ Animations
### Tailwind Animations
```svelte
Spinning
Pulsing
Bouncing
Fading in
```
```javascript
// tailwind.config.js
export default {
theme: {
extend: {
animation: {
'fade-in': 'fadeIn 0.3s ease-in-out',
'slide-up': 'slideUp 0.3s ease-out',
'slide-down': 'slideDown 0.3s ease-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
slideDown: {
'0%': { transform: 'translateY(-10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
},
},
};
```
### Svelte Transitions
```svelte
{#if visible}
Fading content
Flying content
Sliding content
Scaling content
Smooth flying content
{/if}
{#if visible}
Different in/out
{/if}
```
### Custom Transition
```typescript
// src/lib/transitions/index.ts
import { cubicOut } from 'svelte/easing';
export function expandHeight(
node: Element,
{ duration = 300, easing = cubicOut } = {}
) {
const style = getComputedStyle(node);
const height = parseFloat(style.height);
const paddingTop = parseFloat(style.paddingTop);
const paddingBottom = parseFloat(style.paddingBottom);
return {
duration,
easing,
css: (t: number) => `
height: ${t * height}px;
padding-top: ${t * paddingTop}px;
padding-bottom: ${t * paddingBottom}px;
overflow: hidden;
`
};
}
```
---
## 📋 Best Practices
### ✅ Do:
- Use Tailwind for utility-first styling
- Create reusable component classes with `@layer components`
- Use CSS variables for design tokens
- Prefer `class:` directive for conditional classes
- Use `cn()` utility for merging classes
- Keep animations subtle and purposeful
### ❌ Don't:
- Mix inline styles with Tailwind randomly
- Override Tailwind classes with `!important`
- Create deeply nested CSS selectors
- Use `@apply` for everything (defeats the purpose)
- Forget dark mode variants
- Ignore accessibility (focus states, contrast)