ocd-website/cline_docs/themeImplementation.md
TheMaddax e8ba34e3b7 feat: Implement comprehensive CSS variable theme system
- Fix critical navigation dark theme issues (white background on scroll, poor text contrast)
- Replace all hardcoded Tailwind colors with semantic CSS classes throughout Footer and Navigation
- Add complete set of CSS variable classes for consistent theming (.nav-bar, .footer, .nav-link, etc.)
- Eliminate hardcoded colors from home page and add missing CTA button classes
- Ensure proper light/dark theme support with CSS-only switching for optimal performance
- Update Hero component to use clean backgrounds instead of gradients
- Set light theme as default for better user experience
- Add comprehensive theme implementation documentation
- Update memory bank with current implementation status

Files modified:
- globals.css: Added Navigation and Footer CSS classes using CSS variables
- Navigation.tsx: Fixed dark theme background and text contrast issues
- Footer.tsx: Complete conversion from hardcoded colors to semantic classes
- page.tsx: Fixed CTA section and removed hardcoded colors
- Hero.tsx: Clean background implementation
- layout.tsx: Set light theme as default
- activeContext.md: Updated current work status
- themeImplementation.md: Added comprehensive theme methodology guide
2025-06-02 10:27:34 -06:00

350 lines
8.2 KiB
Markdown

# Theme Implementation Guide: Dark and Light Themes
## Overview
This guide outlines critical implementation patterns for dark and light themes using next-themes in Next.js applications, based on best practices and real-world implementation experience.
## Core Architecture Principles
### 1. CSS Variable Strategy (Primary Approach)
**ALWAYS USE**: CSS variables with semantic naming for theme switching
**AVOID**: Inline `dark:` classes throughout components
```css
:root {
/* Semantic color variables - light theme defaults */
--color-text: #1e293b;
--color-text-light: #64748b;
--color-bg: #ffffff;
--color-bg-secondary: #f8fafc;
--color-primary: #1a56db;
--color-border: #e2e8f0;
}
.dark {
/* Dark theme overrides */
--color-text: #f3f4f6;
--color-text-light: #e5e7eb;
--color-bg: #0f172a;
--color-bg-secondary: #1e293b;
--color-primary: #60a5fa;
--color-border: #475569;
}
```
### 2. Component Class Pattern
Create semantic CSS classes that reference variables:
```css
.card {
background-color: var(--color-bg-secondary);
color: var(--color-text);
border: 1px solid var(--color-border);
}
.card-title {
color: var(--color-text);
}
.card-description {
color: var(--color-text-light);
}
```
**Component Usage**:
```jsx
// ✅ CORRECT - Uses semantic classes
<div className="card">
<h3 className="card-title">Title</h3>
<p className="card-description">Description</p>
</div>
// ❌ AVOID - Inline dark: classes
<div className="bg-white dark:bg-gray-800 text-gray-900 dark:text-white">
<h3 className="text-gray-900 dark:text-white">Title</h3>
<p className="text-gray-600 dark:text-gray-300">Description</p>
</div>
```
## next-themes Implementation Checklist
### 1. Essential Setup
```jsx
// app/layout.tsx
import { ThemeProvider } from 'next-themes'
export default function Layout({ children }) {
return (
<html suppressHydrationWarning>
<body>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem={true}
storageKey="theme"
themes={['light', 'dark', 'system']}
>
{children}
</ThemeProvider>
</body>
</html>
)
}
```
### 2. Theme Toggle Component
```jsx
'use client'
import { useTheme } from 'next-themes'
import { useEffect, useState } from 'react'
export function ThemeToggle() {
const [mounted, setMounted] = useState(false)
const { theme, setTheme } = useTheme()
// Prevent hydration mismatch
useEffect(() => setMounted(true), [])
if (!mounted) return null // Critical for SSR
return (
<select value={theme} onChange={(e) => setTheme(e.target.value)}>
<option value="system">System</option>
<option value="dark">Dark</option>
<option value="light">Light</option>
</select>
)
}
```
## Critical Implementation Rules
### 1. NEVER Access Theme During SSR
```jsx
// ❌ WRONG - Causes hydration mismatch
function Component() {
const { theme } = useTheme()
return (
<div className={theme === 'dark' ? 'dark-styles' : 'light-styles'}>
Content
</div>
)
}
// ✅ CORRECT - Use CSS variables only
function Component() {
return (
<div className="themed-container">
Content
</div>
)
}
```
### 2. Always Check Mount State
```jsx
// ✅ CORRECT Pattern
function ThemeAwareComponent() {
const [mounted, setMounted] = useState(false)
const { theme } = useTheme()
useEffect(() => setMounted(true), [])
if (!mounted) {
return <div className="skeleton-loader" /> // Fallback UI
}
// Safe to use theme here
return <div>Theme: {theme}</div>
}
```
### 3. CSS Variable Hierarchy
Structure variables from general to specific:
```css
:root {
/* 1. Brand Colors (theme-independent) */
--brand-primary: #1a56db;
--brand-secondary: #7e3af2;
/* 2. Semantic Colors (theme-dependent) */
--color-text: #1e293b;
--color-text-light: #64748b;
--color-text-muted: #94a3b8;
/* 3. Component Colors (derived from semantic) */
--card-bg: var(--color-bg-secondary);
--card-text: var(--color-text);
--card-border: var(--color-border);
/* 4. State Colors */
--color-success: #059669;
--color-warning: #d97706;
--color-error: #dc2626;
}
```
## Accessibility Requirements
### 1. Contrast Ratios
Ensure WCAG AA compliance (4.5:1 for normal text, 3:1 for large text):
```css
:root {
--color-text: #1e293b; /* 16.8:1 ratio on white */
--color-text-light: #64748b; /* 7.2:1 ratio on white */
}
.dark {
--color-text: #f3f4f6; /* 17.4:1 ratio on dark bg */
--color-text-light: #e5e7eb; /* 13.8:1 ratio on dark bg */
}
```
### 2. Focus Management
```css
*:focus-visible {
outline: none;
box-shadow: 0 0 0 3px var(--color-primary);
}
.dark *:focus-visible {
box-shadow: 0 0 0 3px var(--color-primary-light);
}
```
### 3. Reduced Motion Support
```css
@media (prefers-reduced-motion: reduce) {
.card {
transition: none;
}
}
```
## Common Pitfalls & Solutions
### 1. Flash of Unstyled Content (FOUC)
**Problem**: Theme flashes on page load
**Solution**: Use `suppressHydrationWarning` and CSS-only theme switching
### 2. Hydration Mismatches
**Problem**: Server renders light theme, client uses dark theme
**Solution**: Never conditionally render based on theme in components
### 3. CSS Specificity Issues
**Problem**: `dark:` classes not applying
**Solution**: Use CSS variables and `.dark` attribute selectors
### 4. Performance Issues
**Problem**: Re-rendering on theme change
**Solution**: CSS-only theme switching, avoid theme-dependent useEffect
## Testing Checklist
### 1. Theme Switching
- [ ] Light to dark transition works smoothly
- [ ] Dark to light transition works smoothly
- [ ] System preference detection works
- [ ] Theme persists across page refreshes
- [ ] Theme syncs across browser tabs
### 2. SSR & Hydration
- [ ] No hydration warnings in console
- [ ] No FOUC on page load
- [ ] Server-rendered HTML matches client
- [ ] Theme toggle works immediately after mount
### 3. Accessibility
- [ ] All text meets contrast requirements
- [ ] Focus indicators visible in both themes
- [ ] Screen reader compatibility maintained
- [ ] Keyboard navigation works in both themes
## Debugging Guide
### 1. Hydration Issues
```bash
# Check for hydration warnings
npm run dev
# Look for: "Warning: Text content did not match"
```
### 2. CSS Variable Debugging
```css
/* Add to debug CSS variables */
.debug-vars::before {
content: "text: " var(--color-text) " | bg: " var(--color-bg);
position: fixed;
top: 0;
left: 0;
background: red;
color: white;
z-index: 9999;
}
```
### 3. Theme State Debugging
```jsx
// Add to components for debugging
const { theme, resolvedTheme, systemTheme } = useTheme()
console.log({ theme, resolvedTheme, systemTheme })
```
## Performance Best Practices
### 1. CSS Variable Optimization
- Use HSL values for easier manipulation
- Group related variables together
- Minimize number of custom properties
- Use inheritance where possible
### 2. Component Optimization
- Avoid theme-dependent useEffect hooks
- Use CSS-only animations and transitions
- Minimize JavaScript theme logic
- Prefer CSS variables over conditional classes
## Migration Strategy
### From Inline Classes to CSS Variables
1. **Audit**: Find all `dark:` classes in codebase
2. **Extract**: Create semantic CSS variables
3. **Replace**: Convert components to use CSS classes
4. **Test**: Verify theme switching works
5. **Cleanup**: Remove unused dark: classes
### Example Migration
```jsx
// Before
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Title</h1>
<p className="text-gray-600 dark:text-gray-300">Description</p>
</div>
// After
<div className="content-container">
<h1 className="content-title text-2xl font-bold">Title</h1>
<p className="content-description">Description</p>
</div>
```
```css
.content-container {
background-color: var(--color-bg);
color: var(--color-text);
}
.content-title {
color: var(--color-text);
}
.content-description {
color: var(--color-text-light);
}
```
## Conclusion
Always prioritize CSS variables over inline dark: classes, ensure proper SSR handling, maintain accessibility standards, and test thoroughly across all theme combinations. This approach provides better performance, maintainability, and user experience.