dark-mode-implementer
Add a complete dark/light mode theming system to your application.
Install
mkdir -p .claude/skills/dark-mode-implementer && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15234" && unzip -o skill.zip -d .claude/skills/dark-mode-implementer && rm skill.zipInstalls to .claude/skills/dark-mode-implementer
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Implements complete dark/light mode theming systems using CSS variables, Tailwind dark mode, React context, and system preference detection. Use when users request "add dark mode", "theme toggle", "dark theme", "light mode switch", or "color scheme".Key capabilities
- →Choose a dark mode implementation strategy (CSS-only, Tailwind, React context)
- →Define semantic color tokens using CSS variables
- →Implement a theme switch component
- →Detect system color scheme preference (`prefers-color-scheme`)
- →Persist user's theme choice in `localStorage`
- →Prevent flash of incorrect theme on initial page load
How it works
The skill outlines a core workflow to choose a strategy, define color tokens, implement a toggle, detect system preference, persist choice, and prevent flash of wrong theme.
Inputs & outputs
When to use dark-mode-implementer
- →Adding dark mode support
- →Implementing a theme toggle
- →Configuring system-based color schemes
About this skill
Dark Mode Implementer
Build robust dark/light mode theming with system preference detection and persistent storage.
Core Workflow
- Choose strategy: CSS-only, Tailwind, or React context
- Define color tokens: Create semantic color variables
- Implement toggle: Add theme switch component
- Detect system preference: Respect
prefers-color-scheme - Persist choice: Store preference in localStorage
- Prevent flash: Handle initial load correctly
Strategy Comparison
| Strategy | Best For | Complexity |
|---|---|---|
Tailwind class | React/Vue/Svelte apps | Low |
CSS media | Simple static sites | Very Low |
| CSS Variables + JS | Framework-agnostic | Medium |
| React Context | Complex React apps | Medium |
Tailwind CSS Dark Mode
Enable Class Strategy
// tailwind.config.js
module.exports = {
darkMode: 'class', // or 'media' for system-only
theme: {
extend: {
colors: {
// Semantic color tokens
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: 'hsl(var(--primary))',
muted: 'hsl(var(--muted))',
},
},
},
};
CSS Variables Setup
/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222 47% 11%;
--primary: 221 83% 53%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96%;
--secondary-foreground: 222 47% 11%;
--muted: 210 40% 96%;
--muted-foreground: 215 16% 47%;
--accent: 210 40% 96%;
--accent-foreground: 222 47% 11%;
--destructive: 0 84% 60%;
--destructive-foreground: 210 40% 98%;
--border: 214 32% 91%;
--input: 214 32% 91%;
--ring: 221 83% 53%;
--radius: 0.5rem;
}
.dark {
--background: 222 47% 11%;
--foreground: 210 40% 98%;
--primary: 217 91% 60%;
--primary-foreground: 222 47% 11%;
--secondary: 217 33% 17%;
--secondary-foreground: 210 40% 98%;
--muted: 217 33% 17%;
--muted-foreground: 215 20% 65%;
--accent: 217 33% 17%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62% 30%;
--destructive-foreground: 210 40% 98%;
--border: 217 33% 17%;
--input: 217 33% 17%;
--ring: 224 76% 48%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
Using Dark Mode Classes
<!-- Component with dark mode variants -->
<div class="bg-white dark:bg-gray-900">
<h1 class="text-gray-900 dark:text-white">Title</h1>
<p class="text-gray-600 dark:text-gray-300">Description</p>
<button class="bg-blue-500 hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700">
Action
</button>
</div>
React Theme Provider
Complete Theme Context
// lib/theme-context.tsx
'use client';
import { createContext, useContext, useEffect, useState } from 'react';
type Theme = 'light' | 'dark' | 'system';
interface ThemeContextType {
theme: Theme;
setTheme: (theme: Theme) => void;
resolvedTheme: 'light' | 'dark';
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
const STORAGE_KEY = 'theme';
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setThemeState] = useState<Theme>('system');
const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('light');
const [mounted, setMounted] = useState(false);
// Get system preference
const getSystemTheme = (): 'light' | 'dark' => {
if (typeof window === 'undefined') return 'light';
return window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
};
// Apply theme to document
const applyTheme = (theme: Theme) => {
const root = document.documentElement;
const resolved = theme === 'system' ? getSystemTheme() : theme;
root.classList.remove('light', 'dark');
root.classList.add(resolved);
setResolvedTheme(resolved);
};
// Set theme and persist
const setTheme = (newTheme: Theme) => {
setThemeState(newTheme);
localStorage.setItem(STORAGE_KEY, newTheme);
applyTheme(newTheme);
};
// Initialize theme on mount
useEffect(() => {
const stored = localStorage.getItem(STORAGE_KEY) as Theme | null;
const initialTheme = stored || 'system';
setThemeState(initialTheme);
applyTheme(initialTheme);
setMounted(true);
}, []);
// Listen for system preference changes
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleChange = () => {
if (theme === 'system') {
applyTheme('system');
}
};
mediaQuery.addEventListener('change', handleChange);
return () => mediaQuery.removeEventListener('change', handleChange);
}, [theme]);
// Prevent hydration mismatch
if (!mounted) {
return <>{children}</>;
}
return (
<ThemeContext.Provider value={{ theme, setTheme, resolvedTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
Prevent Flash of Wrong Theme
// app/layout.tsx
import { ThemeProvider } from '@/lib/theme-context';
// Inline script to prevent flash
const themeScript = `
(function() {
const stored = localStorage.getItem('theme');
const theme = stored || 'system';
const resolved = theme === 'system'
? window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
: theme;
document.documentElement.classList.add(resolved);
})();
`;
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<script dangerouslySetInnerHTML={{ __html: themeScript }} />
</head>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
);
}
Theme Toggle Components
Simple Toggle Button
// components/ThemeToggle.tsx
'use client';
import { useTheme } from '@/lib/theme-context';
import { Moon, Sun } from 'lucide-react';
export function ThemeToggle() {
const { resolvedTheme, setTheme } = useTheme();
return (
<button
onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}
className="rounded-lg p-2 hover:bg-gray-100 dark:hover:bg-gray-800"
aria-label={`Switch to ${resolvedTheme === 'dark' ? 'light' : 'dark'} mode`}
>
{resolvedTheme === 'dark' ? (
<Sun className="h-5 w-5" />
) : (
<Moon className="h-5 w-5" />
)}
</button>
);
}
Three-Way Toggle (Light/Dark/System)
// components/ThemeSelector.tsx
'use client';
import { useTheme } from '@/lib/theme-context';
import { Monitor, Moon, Sun } from 'lucide-react';
const themes = [
{ value: 'light', icon: Sun, label: 'Light' },
{ value: 'dark', icon: Moon, label: 'Dark' },
{ value: 'system', icon: Monitor, label: 'System' },
] as const;
export function ThemeSelector() {
const { theme, setTheme } = useTheme();
return (
<div className="flex rounded-lg bg-gray-100 p-1 dark:bg-gray-800">
{themes.map(({ value, icon: Icon, label }) => (
<button
key={value}
onClick={() => setTheme(value)}
className={`
flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium
transition-colors
${theme === value
? 'bg-white text-gray-900 shadow dark:bg-gray-700 dark:text-white'
: 'text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white'
}
`}
aria-label={`Switch to ${label} theme`}
>
<Icon className="h-4 w-4" />
<span className="hidden sm:inline">{label}</span>
</button>
))}
</div>
);
}
Animated Toggle Switch
// components/ThemeSwitch.tsx
'use client';
import { useTheme } from '@/lib/theme-context';
import { motion } from 'framer-motion';
export function ThemeSwitch() {
const { resolvedTheme, setTheme } = useTheme();
const isDark = resolvedTheme === 'dark';
return (
<button
onClick={() => setTheme(isDark ? 'light' : 'dark')}
className="relative h-8 w-14 rounded-full bg-gray-200 p-1 dark:bg-gray-700"
aria-label={`Switch to ${isDark ? 'light' : 'dark'} mode`}
>
<motion.div
className="flex h-6 w-6 items-center justify-center rounded-full bg-white shadow-md"
animate={{ x: isDark ? 24 : 0 }}
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
>
<motion.span
initial={false}
animate={{ rotate: isDark ? 360 : 0 }}
transition={{ duration: 0.5 }}
>
{isDark ? '🌙' : '☀️'}
</motion.span>
</motion.div>
</button>
);
}
CSS-Only Dark Mode
Using prefers-color-scheme
/* For simple sites without JavaScript */
:root {
--bg: #ffffff;
--text: #1a1a1a;
--primary: #3b82f6;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0a0a0a;
--text: #fafafa;
--primary: #60a5fa;
}
}
body {
background-color: var(--bg);
color: var(--text);
}
Color Token System
Semantic Color Naming
:root {
/* Background colors */
--color-bg-primary: #ffffff;
--color-bg-secondary: #f9fafb;
--color-bg-tertiary: #f3f4f6;
--color-bg-inverse: #111827;
/* Text colors */
--color-text-primary: #111827;
--color-text-secondary: #4b5563;
--color-text-tertiary: #9ca3af;
--color-text-inverse: #ffffff;
/* Border colors */
--color-border-primary: #e5e7eb;
--color-border-secondary: #d1d5db;
/* Interactive colors */
--color-interactive-primary: #3b82f6;
--color-interactive-hove
---
*Content truncated.*
When not to use it
- →When dark/light mode theming is not required
- →When not using CSS variables, Tailwind dark mode, or React context for theming
Limitations
- →The skill focuses on CSS variables, Tailwind dark mode, and React context strategies
- →The implementation requires handling images and SVGs for dark mode variants
- →WCAG contrast ratios must be maintained for accessibility
How it compares
It provides a structured approach for implementing complete dark/light mode theming, including specific code examples for Tailwind CSS and React Context, which goes beyond basic CSS theme switching.
Compared to similar skills
dark-mode-implementer side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| dark-mode-implementer (this skill) | 0 | 5mo | Review | Intermediate |
| visual-design-foundations | 5 | 5mo | No flags | Beginner |
| frontend-design | 0 | 3mo | No flags | Advanced |
| premium-frontend-ui | 0 | 2mo | No flags | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by zinohome
View all by zinohome →You might also like
visual-design-foundations
wshobson
Apply typography, color theory, spacing systems, and iconography principles to create cohesive visual designs. Use when establishing design tokens, building style guides, or improving visual hierarchy and consistency.
frontend-design
riabtsev
Create distinctive, production-grade frontend interfaces with high design quality. Generates creative, polished code that avoids generic AI aesthetics. Use when the user asks to build web components, pages, artifacts, posters, or applications, or when any design skill requires project context.
premium-frontend-ui
TorchiaHub
A comprehensive guide for GitHub Copilot to craft immersive, high-performance web experiences with advanced motion, typography, and architectural craftsmanship.
UI/UX Myth
ildunari
|
screenshot-to-code
OneWave-AI
Convert UI screenshots into working HTML/CSS/React/Vue code. Detects design patterns, components, and generates responsive layouts. Use this when users provide screenshots of websites, apps, or UI designs and want code implementation.
ui-ux-pro-max
nextlevelbuilder
UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient.