playbook/skills/thirdparty/pathfinding/examples/high-start.md

5.6 KiB

Example: High Start (Level 4)

Clear request with context — minimal clarification needed, fast to delivery.

I need to add a dark mode toggle to my Next.js app. I'm using Tailwind CSS and want to persist the preference in localStorage. Should work with system preference as a default.

[Task] Clear requirements — fast path
- Clarify implementation details (in_progress)
- Deliver dark mode implementation (pending)
▓▓▓▓░ Mapped

You've given me most of what I need:
- Next.js + Tailwind CSS
- Toggle component
- localStorage persistence
- System preference default

Assumptions:
- Tailwind's built-in dark mode (class strategy)
- Client-side toggle (no server preference)
- Standard light/dark, no custom themes

Decisions:
- Dark mode with localStorage + system fallback
- Tailwind class strategy
- Toggle UI component

Can deliver now, or one quick question: where does the toggle live? (header, settings page, floating button)

[★] If you just want the implementation and will place it yourself, I can skip that question.

[EnterPlanMode]
1. Skip — just give me the implementation
2. Header/navbar — always visible
3. Settings page — dedicated settings area
4. Floating button — corner of screen
5. Something else — different placement

1, I'll put it in my header

▓▓▓▓▓ Ready

All clear — delivering implementation.
[Task] Delivering
- Clarify implementation details (completed)
- Deliver dark mode implementation (in_progress)

Dark Mode Implementation

1. Configure Tailwind (tailwind.config.js):

module.exports = {
  darkMode: 'class',
  // ... rest of config
}

2. Theme Provider (components/theme-provider.tsx):

'use client';

import { createContext, useContext, useEffect, useState } from 'react';

type Theme = 'light' | 'dark' | 'system';

const ThemeContext = createContext<{
  theme: Theme;
  setTheme: (theme: Theme) => void;
}>({ theme: 'system', setTheme: () => {} });

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = useState<Theme>('system');

  useEffect(() => {
    const stored = localStorage.getItem('theme') as Theme | null;
    if (stored) setTheme(stored);
  }, []);

  useEffect(() => {
    const root = document.documentElement;
    const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

    root.classList.remove('light', 'dark');

    if (theme === 'system') {
      root.classList.add(systemDark ? 'dark' : 'light');
    } else {
      root.classList.add(theme);
    }

    localStorage.setItem('theme', theme);
  }, [theme]);

  // Listen for system preference changes
  useEffect(() => {
    const media = window.matchMedia('(prefers-color-scheme: dark)');
    const handler = () => {
      if (theme === 'system') {
        document.documentElement.classList.remove('light', 'dark');
        document.documentElement.classList.add(media.matches ? 'dark' : 'light');
      }
    };
    media.addEventListener('change', handler);
    return () => media.removeEventListener('change', handler);
  }, [theme]);

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export const useTheme = () => useContext(ThemeContext);

3. Toggle Component (components/theme-toggle.tsx):

'use client';

import { useTheme } from './theme-provider';
import { Sun, Moon, Monitor } from 'lucide-react'; // or your icon library

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

  const cycle = () => {
    const next = theme === 'light' ? 'dark' : theme === 'dark' ? 'system' : 'light';
    setTheme(next);
  };

  return (
    <button
      onClick={cycle}
      className="p-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800"
      aria-label="Toggle theme"
    >
      {theme === 'light' && <Sun className="w-5 h-5" />}
      {theme === 'dark' && <Moon className="w-5 h-5" />}
      {theme === 'system' && <Monitor className="w-5 h-5" />}
    </button>
  );
}

4. Wrap App (app/layout.tsx):

import { ThemeProvider } from '@/components/theme-provider';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider>
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

5. Prevent Flash (add to <head> in layout):

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

6. Use in Header:

import { ThemeToggle } from '@/components/theme-toggle';

export function Header() {
  return (
    <header className="flex items-center justify-between p-4">
      <Logo />
      <nav>{/* ... */}</nav>
      <ThemeToggle />
    </header>
  );
}

Usage:

  • Click toggle: cycles light → dark → system → light
  • System mode: follows OS preference automatically
  • Persists in localStorage across sessions
  • No flash on page load
[Task] Done
- Clarify implementation details (completed)
- Deliver dark mode implementation (completed)

All set — drop <ThemeToggle /> in your header and you're good.