# 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

Title

Description

``` --- ## ✨ 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)