home / skills / fusengine / agents / dark-light-modes

This skill helps implement robust dark-light theming using CSS variables, system detection, and a reliable no-FOUC toggle across Next.js apps.

npx playbooks add skill fusengine/agents --skill dark-light-modes

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
3.1 KB
---
name: dark-light-modes
description: Use when implementing theme modes, color schemes, or system preference detection. Covers prefers-color-scheme, CSS variables, next-themes.
versions:
  next-themes: "0.4"
user-invocable: true
allowed-tools: Read, Write, Edit, Glob, Grep
related-skills: theming-tokens, designing-systems
---

# Dark/Light Modes

## Agent Workflow (MANDATORY)

Before implementation, use `TeamCreate` to spawn 3 agents:

1. **fuse-ai-pilot:explore-codebase** - Check existing theme setup
2. **fuse-ai-pilot:research-expert** - next-themes or theme provider docs

After: Run **fuse-ai-pilot:sniper** for validation.

---

## Overview

| Feature | Description |
|---------|-------------|
| **CSS Variables** | Token-based theming |
| **System Detection** | prefers-color-scheme |
| **Manual Toggle** | User preference |
| **No FOUC** | Prevent flash |

---

## Quick Reference

### CSS Variables

```css
:root {
  --background: oklch(100% 0 0);
  --foreground: oklch(10% 0 0);
  --glass-bg: rgba(255, 255, 255, 0.8);
}

.dark {
  --background: oklch(10% 0.01 260);
  --foreground: oklch(98% 0 0);
  --glass-bg: rgba(0, 0, 0, 0.4);
}
```

### System Preference

```css
@media (prefers-color-scheme: dark) {
  :root:not(.light) {
    --background: oklch(10% 0.01 260);
  }
}
```

### Next.js with next-themes

```tsx
// app/providers.tsx
"use client";
import { ThemeProvider } from "next-themes";

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
      {children}
    </ThemeProvider>
  );
}
```

### Theme Toggle

```tsx
"use client";
import { Moon, Sun, Monitor } from "lucide-react";
import { useTheme } from "next-themes";

export function ThemeToggle() {
  const { theme, setTheme } = useTheme();

  return (
    <div className="flex gap-1 p-1 bg-muted rounded-lg">
      <button onClick={() => setTheme("light")}>
        <Sun className="h-4 w-4" />
      </button>
      <button onClick={() => setTheme("dark")}>
        <Moon className="h-4 w-4" />
      </button>
      <button onClick={() => setTheme("system")}>
        <Monitor className="h-4 w-4" />
      </button>
    </div>
  );
}
```

### Prevent FOUC

```tsx
<html suppressHydrationWarning>
  <head>
    <script dangerouslySetInnerHTML={{
      __html: `
        (function() {
          const theme = localStorage.getItem('theme') || 'system';
          const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
          if (theme === 'dark' || (theme === 'system' && systemDark)) {
            document.documentElement.classList.add('dark');
          }
        })();
      `,
    }} />
  </head>
```

---

## Validation Checklist

```
[ ] CSS variables for both modes
[ ] prefers-color-scheme respected
[ ] Manual toggle available
[ ] No FOUC (flash of unstyled content)
[ ] Glass variants for both modes
[ ] Stored preference in localStorage
```

---

## Best Practices

### DO
- Use CSS variables for theming
- Support system preference
- Provide manual toggle
- Prevent FOUC with script

### DON'T
- Hard-code dark: classes everywhere
- Ignore system preference
- Forget localStorage
- Allow flash on load

Overview

This skill helps implement robust dark and light theme modes for web apps, including system preference detection, CSS variable tokenization, and integration with next-themes. It focuses on preventing flash-of-unstyled-content (FOUC), offering a reliable manual toggle, and storing user preference. Use it to standardize theme behavior across client-side frameworks and Next.js apps.

How this skill works

It inspects and defines CSS variables for both light and dark palettes and uses prefers-color-scheme media queries to honor system settings. For Next.js, it recommends next-themes with attribute="class" and a small hydration-safe initialization script to avoid FOUC. It also includes a simple toggle component that writes the choice to localStorage and updates the document class.

When to use it

  • Building or refactoring a site to support light/dark themes
  • Supporting users who prefer system-level color schemes
  • Preventing FOUC during initial page load
  • Providing an accessible, persistent theme toggle
  • Implementing token-based theming with CSS variables

Best practices

  • Define all color tokens with CSS variables and mirror values for .dark and :root
  • Respect prefers-color-scheme and fall back to a stored explicit user choice
  • Use next-themes with attribute="class" and enableSystem for Next.js apps
  • Run a small inline script before render to add .dark to documentElement to prevent FOUC
  • Store preference in localStorage and keep toggle options: light, dark, system

Example use cases

  • Next.js app using next-themes and a ThemeToggle component that supports light/dark/system
  • Static or client-rendered sites using CSS variables and @media (prefers-color-scheme) rules
  • Design systems where glass/translucent variants need separate tokens per mode
  • Progressive enhancement where default follows system but user choice persists
  • Accessibility audits to ensure contrast tokens change correctly between modes

FAQ

How do I prevent a flash of incorrect theme on first paint?

Run a tiny inline script in the head to read localStorage and match window.matchMedia, then add .dark to documentElement before hydration.

Should I use CSS variables or hard-coded classes?

Prefer CSS variables for tokenized theming; use a .dark class to switch values rather than hard-coding colors throughout components.