Files
nextcraft/apps/web/components/theme-provider.tsx
T
CIAgent 810485ecc8 docs(milestone): complete v0.1-nextcraft-ui-prototype
---ci---
phase: 7
milestone: v0.1
status: complete
requirements:
  covered: [REQ-001, REQ-002, REQ-003, REQ-004, REQ-005, REQ-006, REQ-007, REQ-008, REQ-009, REQ-010, REQ-011, REQ-012, REQ-013, REQ-014, REQ-015, REQ-016, REQ-017, REQ-018, REQ-019, REQ-020, REQ-021, REQ-022, REQ-023, REQ-024, REQ-025, REQ-026, REQ-027, REQ-028]
  partial: []
---/ci---

Milestone v0.1 (nextcraft-ui-prototype) complete.

Summary:
- 7 phases (P0 pre-execution + P1-P6 execution + P7 final review)
- 28 requirements covered (all complete)
- 4 surfaces: Learner (7 pages), Marketplace (5), Employer Dashboard (4), Admin (4)
- 19 routes, 104 TypeScript files
- Shared component library (5 primitives + design tokens)
- Mock data: 5 competency stacks (70 competencies), 20 jobs, 15 candidates, 10 employers
- Tech: Next.js 15, Tailwind CSS v4, lucide-react, recharts, @xyflow/react, Inter font
- Storybook with 6 stories
- Dark mode, breadcrumbs, role switcher, responsive design
- Build passes, typecheck passes, Storybook build passes

Phases:
  P0 pre-execution     → v0.0.1
  P1 project-scaffolding → v0.0.2
  P2 learner-surface   → v0.0.3
  P3 marketplace-surface → v0.0.4
  P4 employer-dashboard → v0.0.5
  P5 admin-surface     → v0.0.6
  P6 polish-integration → v0.0.7
  P7 final-review-ship → v0.1.0 (milestone release)
2026-09-10 22:47:56 +00:00

43 lines
1.6 KiB
TypeScript

'use client';
import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';
import type { ThemeMode } from '@nextcraft/types';
interface ThemeContextValue {
mode: ThemeMode;
toggle: () => void;
setMode: (mode: ThemeMode) => void;
}
const ThemeContext = createContext<ThemeContextValue | null>(null);
export function ThemeProvider({ children }: { children: ReactNode }) {
const [mode, setModeState] = useState<ThemeMode>('light');
useEffect(() => {
const stored = typeof window !== 'undefined' ? window.localStorage.getItem('nextcraft-theme') : null;
const prefersDark =
typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches;
const initial: ThemeMode = stored === 'dark' || stored === 'light' ? stored : prefersDark ? 'dark' : 'light';
setModeState(initial);
}, []);
useEffect(() => {
const root = document.documentElement;
if (mode === 'dark') root.classList.add('dark');
else root.classList.remove('dark');
window.localStorage.setItem('nextcraft-theme', mode);
}, [mode]);
const setMode = useCallback((m: ThemeMode) => setModeState(m), []);
const toggle = useCallback(() => setModeState((m) => (m === 'dark' ? 'light' : 'dark')), []);
const value = useMemo(() => ({ mode, toggle, setMode }), [mode, toggle, setMode]);
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
}
export function useTheme(): ThemeContextValue {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error('useTheme must be used within ThemeProvider');
return ctx;
}