shadcnui-customization-theming
Use this skill when customizing shadcn/ui themes, implementing dark mode, modifying component styles, or changing color schemes. Covers CSS variables, design tokens, theme generation, and component.json configuration. Assumes shadcn is already set up with components installed.
Installation
Suivez les instructions du dépôt pour votre agent. Consultez les fichiers et dépendances avant l’installation.
Voir la source ↗GuidesInstructions
shadcn/ui Customization & Theming
This skill covers customizing shadcn/ui components and implementing themes.
Prerequisites: Your project must have:
- Completed
shadcn initsetup - At least one component installed
-
components.jsonandsrc/lib/utils.tscreated - Tailwind CSS configured with CSS variables enabled
CSS Variables & Design Tokens
shadcn uses CSS variables to define the design system. After shadcn init, your global CSS file contains:
@import "tailwindcss";
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--muted: 221.2 63.6% 97.8%;
--muted-foreground: 215.4 16.3% 46.9%;
--accent: 221.2 83.2% 53.3%;
--accent-foreground: 210 40% 98%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--ring: 221.2 83.2% 53.3%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.3% 65.1%;
--accent: 221.2 83.2% 53.3%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--ring: 221.2 83.2% 53.3%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
Understanding CSS Variables
Each variable uses HSL format (Hue, Saturation, Lightness):
--accent: 221.2 83.2% 53.3%;
↑ ↑ ↑
hue sat light
- Hue: 0-360 (color)
- Saturation: 0-100% (intensity)
- Lightness: 0-100% (brightness)
Color Tokens
| Token | Purpose |
|---|---|
--background |
Default background color |
--foreground |
Default text color |
--card |
Card component background |
--card-foreground |
Card text color |
--muted |
Muted/disabled state background |
--muted-foreground |
Muted text color |
--accent |
Primary accent/highlight color |
--accent-foreground |
Text on accent backgrounds |
--destructive |
Destructive action color (red) |
--destructive-foreground |
Text on destructive backgrounds |
--border |
Border colors |
--input |
Input field background |
--ring |
Focus ring color |
--radius |
Default border radius |
Customizing Colors
To change a color, update the CSS variable value:
Change Primary Accent Color
:root {
--accent: 270 100% 50%; /* Changed from blue to purple */
}
.dark {
--accent: 270 100% 60%; /* Purple for dark mode */
}
Change All Backgrounds to Slightly Darker
:root {
--background: 0 0% 98%; /* Was 100%, slightly darker */
--card: 0 0% 98%;
--input: 214.3 31.8% 88%; /* Slightly darker input */
}
Monochrome Theme
:root {
--background: 0 0% 100%;
--foreground: 0 0% 0%;
--card: 0 0% 98%;
--card-foreground: 0 0% 0%;
--accent: 0 0% 20%; /* Dark gray accent */
--border: 0 0% 90%;
}
Dark Mode Implementation
After shadcn init, dark mode is configured with CSS variables. To toggle dark mode:
Using the dark class
Add the dark class to the root element to switch themes:
// src/App.tsx or root component
export function App() {
const [isDark, setIsDark] = useState(false)
return (
<div className={isDark ? 'dark' : ''}>
{/* Your app content */}
<button onClick={() => setIsDark(!isDark)}>
{isDark ? 'Light' : 'Dark'} Mode
</button>
</div>
)
}
Using next-themes (Recommended)
For persistent theme preference:
npm install next-themes
Setup in app entry:
// src/main.tsx or app/layout.tsx
import { ThemeProvider } from 'next-themes'
export default function App() {
return (
<ThemeProvider attribute="class" defaultTheme="system">
{/* Your app */}
</ThemeProvider>
)
}
Create theme toggle:
'use client'
import { useTheme } from 'next-themes'
export function ThemeToggle() {
const { theme, setTheme } = useTheme()
return (
<button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
{theme === 'dark' ? '☀️ Light' : '🌙 Dark'}
</button>
)
}
Customizing Component Styles
Since shadcn components are in your source code, modify them directly. No need for wrappers or overrides.
Example: Customize Button
Open src/components/ui/button.tsx:
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground hover:bg-primary/90",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
// Add custom variant
premium: "bg-gradient-to-r from-purple-600 to-blue-600 text-white hover:from-purple-700 hover:to-blue-700",
},
// Add custom size
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
xl: "h-14 rounded-lg px-6 text-lg",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
Now use the custom variant:
<Button variant="premium">Premium Button</Button>
<Button size="xl">Large Button</Button>
Example: Customize Card
Open src/components/ui/card.tsx and add styling:
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
// Add custom shadow or border
"rounded-lg border border-border/50 bg-card p-6 shadow-lg hover:shadow-xl transition-shadow",
className
)}
{...props}
/>
))
Custom Color Palette
Use the official theme generator: https://ui.shadcn.com/themes
- Visit the theme customizer
- Adjust colors visually
- Copy the generated CSS variables
- Paste into your global CSS
Adding Custom Components
Add your own components alongside shadcn components:
// src/components/ui/custom-button.tsx
import { Button } from './button'
export function CustomButton(props: React.ButtonHTMLAttributes<HTMLButtonElement>) {
return <Button {...props} className={`custom-class ${props.className}`} />
}
Responsive Design
shadcn uses Tailwind responsive classes. Customize spacing and sizing:
/* In your global CSS, customize default responsive values */
@layer base {
:root {
--radius: 0.5rem;
}
}
In components:
<div className="p-4 md:p-6 lg:p-8">
Responsive padding
</div>
Design System Best Practices
Consistent Spacing
Use Tailwind spacing scale:
<div className="space-y-4"> {/* 1rem gap between children */}
<Card>Item 1</Card>
<Card>Item 2</Card>
</div>
Consistent Typography
Tailwind predefined sizes:
<h1 className="text-4xl font-bold">Heading 1</h1>
<h2 className="text-2xl font-semibold">Heading 2</h2>
<p className="text-base text-muted-foreground">Body text</p>
Consistent Colors
Use design tokens, not hardcoded colors:
// ✅ Good - uses design tokens
<div className="bg-background text-foreground border border-border">Content</div>
// ❌ Avoid - hardcoded colors
<div className="bg-white text-black border border-gray-200">Content</div>
Configuration Reference
components.json
Customize how components are installed:
{
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"@/components": "./src/components",
"@/lib/utils": "./src/lib/utils"
}
}
Key settings:
-
style- Visual design system (new-york, default) -
baseColor- Primary color palette (neutral, slate, gray, zinc, stone) -
cssVariables- Use CSS variables for theming (true recommended)
Common Customizations
Rounded Corners
:root {
--radius: 0.25rem; /* Sharper corners */
}
Or:
:root {
--radius: 1rem; /* More rounded */
}
Border Width
Add to button customization:
const buttonVariants = cva(
"border-2", // Thicker borders
// ... rest of styles
)
Font Family
In Tailwind config or global CSS:
@layer base {
body {
@apply font-sans; /* Uses Tailwind's sans font */
}
}
Troubleshooting
Styles not applying
Check:
- CSS variables exist in your global CSS file
- Global CSS is imported in your app entry
- Tailwind CSS is configured
- Component uses
classNameprop
Dark mode not toggling
Check:
- Provider is wrapping your app (if using next-themes)
-
darkclass is applied to root element - CSS variables for
.darktheme are defined
Custom colors not working
Check:
- Variables use correct HSL format:
hue saturation% lightness% - Variables are updated in both
:rootand.darkselectors - Components use
var(--your-variable)or Tailwind classes
Next Steps
Once theming is configured, you can:
- Build pages using styled components
- Implement advanced patterns (forms, tables, etc.)
- Create reusable component wrappers
For advanced patterns and complex features, see the patterns skill.
Exemples
Consultez les exemples du dépôt source.
Voir la source ↗Historique source
- Date d’indexation
- 2026-01-20
- Mise à jour de l’index
- 2026-01-26
- Branch
- main
Les dates concernent ce répertoire. Consultez le dépôt pour l’historique du code.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!