# Tutorial 4: Styling & Theming **Video Length**: ~18 minutes | **Difficulty**: Beginner | **Series**: Getting Started ## Overview Learn how to customize the appearance of shadcn-ui components to match your brand. This tutorial covers CSS variables, theme variants, dark mode, and creating custom component styles. ## What You'll Learn - Understanding the CSS variable system - Customizing colors and spacing - Implementing dark mode - Creating custom themes - Using component variants - Responsive design patterns ## Prerequisites - Completed [Tutorial 3: Basic Form Patterns](03-basic-forms.md) - Basic understanding of CSS ## Video Outline **[0:00]** Introduction to theming **[1:45]** CSS variable system overview **[4:00]** Customizing colors **[7:00]** Dark mode implementation **[9:30]** Custom theme creation **[12:00]** Component variants **[14:30]** Responsive design **[16:00]** Best practices and tips **[17:00]** Summary and next steps ## Step-by-Step Guide ### Understanding the CSS Variable System shadcn-ui uses CSS custom properties (variables) for theming. This allows for: 1. **Runtime theme switching** without page reload 2. **Type-safe theming** through CSS variables 3. **Per-component theming** using scoped variables 4. **Dark mode support** through class-based switching The base variable structure: ```css :root { /* Base colors */ --background: 0 0% 100%; /* HSL values */ --foreground: 222.2 84% 4.9%; /* Component colors */ --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; --primary: 222.2 47.4% 11.2%; --primary-foreground: 210 40% 98%; /* Semantic colors */ --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96.1%; --destructive: 0 84.2% 60.2%; /* Borders and inputs */ --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 222.2 84% 4.9%; /* Layout */ --radius: 0.5rem; } ``` ### Creating a Custom Theme Create a `brand-theme.css` file: ```css @import "tailwindcss/base"; @import "tailwindcss/components"; @import "tailwindcss/utilities"; @layer base { :root { /* Custom brand colors (purple theme) */ --background: 0 0% 100%; --foreground: 270 15% 10%; --card: 0 0% 100%; --card-foreground: 270 15% 10%; --popover: 0 0% 100%; --popover-foreground: 270 15% 10%; /* Primary - Purple */ --primary: 270 91% 65%; --primary-foreground: 0 0% 100%; /* Secondary - Pink */ --secondary: 330 81% 70%; --secondary-foreground: 0 0% 100%; /* Muted tones */ --muted: 270 20% 96%; --muted-foreground: 270 10% 40%; /* Accent */ --accent: 270 20% 96%; --accent-foreground: 270 15% 10%; /* Destructive */ --destructive: 0 84% 60%; --destructive-foreground: 0 0% 100%; /* Borders */ --border: 270 20% 90%; --input: 270 20% 90%; --ring: 270 91% 65%; /* Radius */ --radius: 0.75rem; } .dark { --background: 270 20% 8%; --foreground: 0 0% 100%; --card: 270 20% 10%; --card-foreground: 0 0% 100%; --popover: 270 20% 10%; --popover-foreground: 0 0% 100%; --primary: 270 91% 65%; --primary-foreground: 270 15% 10%; --secondary: 330 81% 70%; --secondary-foreground: 270 15% 10%; --muted: 270 20% 20%; --muted-foreground: 270 10% 60%; --accent: 270 20% 20%; --accent-foreground: 0 0% 100%; --destructive: 0 62% 30%; --destructive-foreground: 0 0% 100%; --border: 270 20% 20%; --input: 270 20% 20%; --ring: 270 91% 65%; } } @layer base { * { @apply border-border; } body { @apply bg-background text-foreground; font-feature-settings: "rlig" 1, "calt" 1; } } ``` ### Implementing Dark Mode Add dark mode toggle to your app: ```rust use leptos::*; use leptos_shadcn_button::Button; #[component] pub fn ThemeToggle() -> impl IntoView { let (is_dark, set_is_dark) = create_signal(false); // Toggle dark mode class on body let toggle_theme = move |_| { set_is_dark.update(|dark| *dark = !*dark); if is_dark.get() { leptos_dom::document() .body() .class_list() .remove_1("dark") .unwrap(); } else { leptos_dom::document() .body() .class_list() .add_1("dark") .unwrap(); } }; // Check system preference on mount leptos::create_effect(move |_| { let prefers_dark = window() .match_media("(prefers-color-scheme: dark)") .unwrap() .unwrap() .matches(); if prefers_dark { set_is_dark.set(true); leptos_dom::document() .body() .class_list() .add_1("dark") .unwrap(); } }); view! { } } ``` ### Using Component Variants Many components support variant props: ```rust use leptos::*; use leptos_shadcn_button::Button; #[component] pub fn ButtonVariants() -> impl IntoView { view! {
"This card uses a gradient background with custom styling."
"Frosted glass effect with transparency"
"Card with left accent border"