📦 deps(skills): sync pathfinding dependency for codebase-recon
This commit is contained in:
@@ -0,0 +1,344 @@
|
||||
# Example: Early Delivery with Caveats
|
||||
|
||||
User requests delivery before reaching full confidence — demonstrates `△ Caveats` and mid-stream concerns.
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
I want to add real-time notifications to our app. Users should get notified when someone comments on their posts.
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
[Task] Getting oriented
|
||||
- Prep notification requirements (in_progress)
|
||||
- Explore delivery mechanisms (pending)
|
||||
- Clarify scale and persistence (pending)
|
||||
- Deliver implementation plan (pending)
|
||||
```
|
||||
|
||||
```text
|
||||
▓▓░░░ Exploring
|
||||
|
||||
Assumptions:
|
||||
- Existing app with posts and comments
|
||||
- User accounts with some form of auth
|
||||
- Web and/or mobile clients
|
||||
|
||||
Unknowns:
|
||||
- Current tech stack?
|
||||
- Scale (users, notifications/day)?
|
||||
- Notification types (just comments, or more)?
|
||||
- Persistence (history, read/unread)?
|
||||
- Push notifications (mobile) needed?
|
||||
|
||||
Decisions:
|
||||
- Real-time comment notifications
|
||||
```
|
||||
|
||||
**Q1**: What's your current tech stack?
|
||||
|
||||
*Why it matters — determines which real-time approach fits best*
|
||||
|
||||
```text
|
||||
[EnterPlanMode]
|
||||
1. Node.js backend — Socket.io, ws, or SSE
|
||||
2. Rust backend — Axum with WebSockets
|
||||
3. Serverless (Vercel, AWS Lambda) — needs external service (Pusher, Ably)
|
||||
4. Supabase — built-in realtime, minimal setup
|
||||
5. Something else — describe your stack
|
||||
```
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
Node.js with Express, React frontend, PostgreSQL
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
Classic PERN stack — lots of good options for real-time.
|
||||
|
||||
▓▓▓░░ Charting
|
||||
|
||||
Assumptions:
|
||||
- Can add WebSocket server alongside Express
|
||||
- React can handle WebSocket connections
|
||||
- PostgreSQL for notification storage
|
||||
|
||||
Unknowns:
|
||||
- Scale expectations?
|
||||
- Mobile app too, or web only?
|
||||
- Need notification history/persistence?
|
||||
|
||||
Decisions:
|
||||
- Node.js + Express + React + PostgreSQL
|
||||
- Real-time comment notifications
|
||||
|
||||
Concerns:
|
||||
- WebSocket scaling can get tricky with multiple server instances — may need Redis pub/sub or sticky sessions
|
||||
```
|
||||
|
||||
```text
|
||||
[Task] Shape emerging
|
||||
- Prep notification requirements (completed)
|
||||
- Explore delivery mechanisms (in_progress)
|
||||
- Clarify scale and persistence (pending)
|
||||
- Deliver implementation plan (pending)
|
||||
```
|
||||
|
||||
**Q2**: What scale are you expecting?
|
||||
|
||||
*Why it matters — affects architecture complexity significantly*
|
||||
|
||||
```text
|
||||
[EnterPlanMode]
|
||||
1. Small (< 1k users) [★] — single server, simple WebSockets *start here, scale later*
|
||||
2. Medium (1k-50k users) — needs Redis pub/sub for multi-server
|
||||
3. Large (50k+ users) — dedicated service (Pusher, Ably, or self-hosted)
|
||||
5. Something else — different constraints
|
||||
```
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
We're small now but hoping to grow. Can we just get something working? I can come back for scaling later.
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
Makes sense — ship something simple, optimize when needed.
|
||||
|
||||
▓▓▓░░ Charting
|
||||
|
||||
△ This assumes single-server deployment works for now — if you're already multi-instance, we'd need Redis pub/sub from the start. Clarify now, note for later, or proceed?
|
||||
```
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
Single server for now, let's proceed
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
Good — simple path forward.
|
||||
|
||||
▓▓▓▓░ Mapped
|
||||
|
||||
Assumptions:
|
||||
- Single server deployment
|
||||
- Web only (no mobile push for now)
|
||||
- Basic notification (no persistence/history initially)
|
||||
|
||||
Unknowns:
|
||||
- Want notification persistence (history, read/unread)?
|
||||
|
||||
Decisions:
|
||||
- Socket.io on Node.js (simplest for Express)
|
||||
- Single-server architecture
|
||||
- Real-time comment notifications
|
||||
|
||||
Concerns:
|
||||
- No persistence means notifications lost on refresh — might frustrate users
|
||||
```
|
||||
|
||||
```text
|
||||
[Task] Getting close
|
||||
- Prep notification requirements (completed)
|
||||
- Explore delivery mechanisms (completed)
|
||||
- Clarify scale and persistence (in_progress)
|
||||
- Deliver implementation plan (pending)
|
||||
```
|
||||
|
||||
**Q3**: Should notifications persist? (history, read/unread state)
|
||||
|
||||
*Why it matters — affects database schema and UX significantly*
|
||||
|
||||
```text
|
||||
[EnterPlanMode]
|
||||
1. No persistence — real-time only, notifications disappear on refresh
|
||||
2. Basic persistence [★] — store in DB, show unread count *better UX, moderate effort*
|
||||
3. Full history — notification center, mark as read, pagination
|
||||
5. Something else — different approach
|
||||
```
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
Actually, can you just give me what you have? I need to ship something today and can iterate.
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
Understood — shipping now, iterating later.
|
||||
|
||||
▓▓▓░░ Charting → Deliver (early)
|
||||
|
||||
Skipping to delivery at level 3. Including caveats for what's unresolved.
|
||||
```
|
||||
|
||||
```text
|
||||
[Task] Early delivery — shipping with caveats
|
||||
- Prep notification requirements (completed)
|
||||
- Explore delivery mechanisms (completed)
|
||||
- Clarify scale and persistence (completed)
|
||||
- Deliver implementation plan (in_progress)
|
||||
```
|
||||
|
||||
## Real-Time Notifications — MVP Plan
|
||||
|
||||
**Architecture**:
|
||||
|
||||
```text
|
||||
┌─────────┐ WebSocket ┌─────────────┐
|
||||
│ React │◄──────────────────►│ Express │
|
||||
│ Client │ │ + Socket.io│
|
||||
└─────────┘ └──────┬──────┘
|
||||
│
|
||||
┌──────▼──────┐
|
||||
│ PostgreSQL │
|
||||
│ (comments) │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
**Server Setup** (add to existing Express app):
|
||||
|
||||
```typescript
|
||||
// server.ts
|
||||
import { Server } from 'socket.io';
|
||||
import { createServer } from 'http';
|
||||
|
||||
const httpServer = createServer(app);
|
||||
const io = new Server(httpServer, {
|
||||
cors: { origin: process.env.CLIENT_URL }
|
||||
});
|
||||
|
||||
// Track connected users
|
||||
const userSockets = new Map<string, string>(); // userId -> socketId
|
||||
|
||||
io.on('connection', (socket) => {
|
||||
const userId = socket.handshake.auth.userId;
|
||||
userSockets.set(userId, socket.id);
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
userSockets.delete(userId);
|
||||
});
|
||||
});
|
||||
|
||||
// Export for use in routes
|
||||
export { io, userSockets };
|
||||
```
|
||||
|
||||
**Emit on Comment Creation**:
|
||||
|
||||
```typescript
|
||||
// routes/comments.ts
|
||||
import { io, userSockets } from '../server';
|
||||
|
||||
app.post('/posts/:postId/comments', async (req, res) => {
|
||||
const comment = await createComment(req.body);
|
||||
const post = await getPost(req.params.postId);
|
||||
|
||||
// Notify post author (if not self-comment)
|
||||
if (post.authorId !== req.user.id) {
|
||||
const socketId = userSockets.get(post.authorId);
|
||||
if (socketId) {
|
||||
io.to(socketId).emit('notification', {
|
||||
type: 'comment',
|
||||
postId: post.id,
|
||||
commentId: comment.id,
|
||||
fromUser: req.user.name,
|
||||
message: `${req.user.name} commented on your post`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
res.json(comment);
|
||||
});
|
||||
```
|
||||
|
||||
**React Client**:
|
||||
|
||||
```typescript
|
||||
// hooks/useNotifications.ts
|
||||
import { useEffect } from 'react';
|
||||
import { io } from 'socket.io-client';
|
||||
import { toast } from 'your-toast-library';
|
||||
|
||||
export function useNotifications(userId: string) {
|
||||
useEffect(() => {
|
||||
const socket = io(process.env.REACT_APP_API_URL, {
|
||||
auth: { userId }
|
||||
});
|
||||
|
||||
socket.on('notification', (data) => {
|
||||
toast(data.message);
|
||||
// Could also update a notification count in state
|
||||
});
|
||||
|
||||
return () => { socket.disconnect(); };
|
||||
}, [userId]);
|
||||
}
|
||||
```
|
||||
|
||||
**Next Steps**:
|
||||
|
||||
1. `npm install socket.io` (server) and `socket.io-client` (client)
|
||||
2. Add Socket.io to your Express server
|
||||
3. Wire up comment creation to emit notifications
|
||||
4. Add `useNotifications` hook to your app root
|
||||
|
||||
---
|
||||
|
||||
## △ Caveats
|
||||
|
||||
**Open questions**:
|
||||
- Notification persistence — currently lost on refresh
|
||||
- Mobile push — not addressed, would need separate implementation
|
||||
|
||||
**Assumed decisions**:
|
||||
- Single server deployment — will break with multiple instances
|
||||
- No read/unread tracking — no notification history
|
||||
- Toast-only UI — no notification center
|
||||
|
||||
**Known concerns**:
|
||||
- User might miss notifications if offline when sent
|
||||
- No retry mechanism for failed deliveries
|
||||
- Socket reconnection handling is minimal
|
||||
|
||||
**Deferred items**:
|
||||
- Redis pub/sub for multi-server (when you scale)
|
||||
- Notification persistence in PostgreSQL (soon — improves UX)
|
||||
- Mobile push notifications (when you add mobile app)
|
||||
- Notification preferences (mute, frequency)
|
||||
|
||||
```text
|
||||
[Task] Done — shipped with caveats
|
||||
- Prep notification requirements (completed)
|
||||
- Explore delivery mechanisms (completed)
|
||||
- Clarify scale and persistence (completed)
|
||||
- Deliver implementation plan (completed)
|
||||
```
|
||||
|
||||
Shipped — MVP real-time notifications ready to go. Come back when you need persistence or scaling.
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
@@ -0,0 +1,342 @@
|
||||
# Example: Greenfield API Design
|
||||
|
||||
Starting from zero — vague request, no context.
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
I need to build an API
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
[Task] Getting oriented
|
||||
- Prep API requirements (in_progress)
|
||||
- Explore architecture options (pending)
|
||||
- Clarify data and auth needs (pending)
|
||||
- Deliver API design doc (pending)
|
||||
```
|
||||
|
||||
```text
|
||||
░░░░░ Prepping
|
||||
|
||||
Unknowns:
|
||||
- What domain/purpose?
|
||||
- Who consumes it?
|
||||
- What data?
|
||||
- Performance/scale needs?
|
||||
- Existing infrastructure?
|
||||
```
|
||||
|
||||
**Q1**: Before we dive in — what kind of session are you looking for?
|
||||
|
||||
*Why it matters — sets the right depth and pace*
|
||||
|
||||
```text
|
||||
[EnterPlanMode]
|
||||
1. Quick pulse check [★] — nail down key decisions, move fast *good for exploring direction*
|
||||
2. Deep dive — thorough exploration, comprehensive plan
|
||||
3. Specific problem — I know what I want, need help with one aspect
|
||||
5. Something else — describe what you're after
|
||||
```
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
1, I have a rough idea but need to figure out the shape
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
Got it — quick pulse to find the shape, then we can go deeper where needed.
|
||||
|
||||
▓░░░░ Scouting
|
||||
|
||||
Decisions:
|
||||
- Quick pulse session, fast iteration
|
||||
```
|
||||
|
||||
**Q2**: What's this API for?
|
||||
|
||||
*Why it matters — determines data model, auth strategy, and scaling approach*
|
||||
|
||||
```text
|
||||
[EnterPlanMode]
|
||||
1. Internal tool — dashboard, admin, ops
|
||||
2. Public product API — third-party developers
|
||||
3. Mobile/web backend — your own apps
|
||||
4. B2B integration — partner systems
|
||||
5. Something else — describe the use case
|
||||
```
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
3, it's for a fitness tracking app
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
Fitness tracking backend — workouts, metrics, user data. Classic mobile backend pattern.
|
||||
|
||||
▓▓░░░ Exploring
|
||||
|
||||
Assumptions:
|
||||
- User accounts with personal data
|
||||
- Workout/activity logging
|
||||
- Progress tracking over time
|
||||
- Mobile-first (iOS/Android)
|
||||
|
||||
Unknowns:
|
||||
- Social features? (friends, sharing, leaderboards)
|
||||
- Device integrations? (Apple Health, Fitbit, etc.)
|
||||
- Real-time needs? (live workouts, streaming)
|
||||
- Offline sync requirements?
|
||||
|
||||
Decisions:
|
||||
- Mobile/web backend for fitness app
|
||||
- Quick pulse session
|
||||
```
|
||||
|
||||
**Q3**: What's the core loop users will do most often?
|
||||
|
||||
*Why it matters — shapes the data model and API surface*
|
||||
|
||||
```text
|
||||
[EnterPlanMode]
|
||||
1. Log workouts manually [★] — exercises, sets, reps, weights *simplest starting point*
|
||||
2. Track metrics — weight, body measurements, progress photos
|
||||
3. Follow programs — structured plans, scheduled workouts
|
||||
4. Social/compete — friends, challenges, leaderboards
|
||||
5. Something else — different core interaction
|
||||
```
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
1 and 2, logging workouts and tracking body metrics
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
Core loop: log workouts + track body metrics. Good foundation — can layer social/programs later.
|
||||
|
||||
▓▓▓░░ Charting
|
||||
|
||||
Assumptions:
|
||||
- User model with profile data
|
||||
- Workout sessions with exercises
|
||||
- Body metrics (weight, measurements) with timestamps
|
||||
- Historical data for progress charts
|
||||
|
||||
Unknowns:
|
||||
- Auth approach?
|
||||
- Exercise library (predefined vs custom)?
|
||||
- Offline-first or always-online?
|
||||
|
||||
Decisions:
|
||||
- Mobile backend for fitness app
|
||||
- Core: workout logging + body metrics
|
||||
- Start simple, extensible for social/programs later
|
||||
|
||||
Concerns:
|
||||
- Exercise data can get complex (variations, equipment, muscle groups) — start with simple model, don't over-engineer
|
||||
```
|
||||
|
||||
```text
|
||||
[Task] Shape emerging — exploring options
|
||||
- Prep API requirements (completed)
|
||||
- Explore architecture options (in_progress)
|
||||
- Clarify data and auth needs (pending)
|
||||
- Deliver API design doc (pending)
|
||||
```
|
||||
|
||||
**Q4**: How should users authenticate?
|
||||
|
||||
*Why it matters — affects security model, user experience, and implementation complexity*
|
||||
|
||||
```text
|
||||
[EnterPlanMode]
|
||||
1. Email/password [★] — simple, universal *good starting point*
|
||||
2. Social login (Google/Apple) — better UX, less friction
|
||||
3. Both — maximum flexibility
|
||||
4. Magic link — passwordless, email-based
|
||||
5. Something else — different auth approach
|
||||
```
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
3, I want both options
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
Both email/password and social login — covers all users.
|
||||
|
||||
▓▓▓▓░ Mapped
|
||||
|
||||
Assumptions:
|
||||
- JWT tokens for mobile auth
|
||||
- Email as primary identifier (link accounts by email)
|
||||
- Refresh token rotation for security
|
||||
|
||||
Unknowns:
|
||||
- Specific social providers (Google, Apple, both)?
|
||||
|
||||
Decisions:
|
||||
- Dual auth: email/password + social login
|
||||
- Workout logging + body metrics core
|
||||
- Mobile backend architecture
|
||||
|
||||
Concerns:
|
||||
- Account linking edge cases (same email, different providers) — handle gracefully
|
||||
```
|
||||
|
||||
```text
|
||||
[Task] Getting close — one more detail
|
||||
- Prep API requirements (completed)
|
||||
- Explore architecture options (completed)
|
||||
- Clarify data and auth needs (in_progress)
|
||||
- Deliver API design doc (pending)
|
||||
```
|
||||
|
||||
Can proceed now, or one more question would nail down the social providers. Your call — deliver now or refine?
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
Let's just do Google and Apple for now
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
Google + Apple — covers iOS and Android users well.
|
||||
|
||||
▓▓▓▓▓ Ready
|
||||
|
||||
Decisions:
|
||||
- Dual auth: email/password + Google + Apple Sign-In
|
||||
- Core features: workout logging, body metrics tracking
|
||||
- JWT tokens with refresh rotation
|
||||
- Mobile-first REST API
|
||||
```
|
||||
|
||||
```text
|
||||
[Task] Clear path — delivering design
|
||||
- Prep API requirements (completed)
|
||||
- Explore architecture options (completed)
|
||||
- Clarify data and auth needs (completed)
|
||||
- Deliver API design doc (in_progress)
|
||||
```
|
||||
|
||||
## Fitness Tracking API Design
|
||||
|
||||
**Core Resources**:
|
||||
|
||||
```text
|
||||
/auth
|
||||
POST /register — email/password signup
|
||||
POST /login — email/password login
|
||||
POST /oauth/google — Google Sign-In
|
||||
POST /oauth/apple — Apple Sign-In
|
||||
POST /refresh — refresh access token
|
||||
POST /logout — invalidate tokens
|
||||
|
||||
/users
|
||||
GET /me — current user profile
|
||||
PUT /me — update profile
|
||||
DELETE /me — delete account
|
||||
|
||||
/workouts
|
||||
GET / — list workouts (paginated)
|
||||
POST / — create workout
|
||||
GET /:id — get workout details
|
||||
PUT /:id — update workout
|
||||
DELETE /:id — delete workout
|
||||
|
||||
/exercises
|
||||
GET / — list exercises (library)
|
||||
POST / — create custom exercise
|
||||
|
||||
/metrics
|
||||
GET / — list body metrics (paginated)
|
||||
POST / — log new metric
|
||||
GET /summary — progress summary (charts data)
|
||||
```
|
||||
|
||||
**Data Models**:
|
||||
|
||||
```text
|
||||
User {
|
||||
id, email, name, avatar_url,
|
||||
auth_provider: "email" | "google" | "apple",
|
||||
created_at, updated_at
|
||||
}
|
||||
|
||||
Workout {
|
||||
id, user_id, name, started_at, ended_at,
|
||||
exercises: [{ exercise_id, sets: [{ reps, weight, duration }] }],
|
||||
notes, created_at
|
||||
}
|
||||
|
||||
Exercise {
|
||||
id, name, category, muscle_groups[],
|
||||
is_custom, user_id (if custom)
|
||||
}
|
||||
|
||||
BodyMetric {
|
||||
id, user_id, type: "weight" | "measurement",
|
||||
value, unit, measured_at
|
||||
}
|
||||
```
|
||||
|
||||
**Tech Stack Suggestion**:
|
||||
|
||||
- **API**: Hono or Express on Bun
|
||||
- **Database**: PostgreSQL (Supabase or Neon)
|
||||
- **Auth**: Custom JWT or Supabase Auth
|
||||
- **Hosting**: Railway, Fly.io, or Vercel
|
||||
|
||||
**Next Steps**:
|
||||
|
||||
1. Set up project with Bun + Hono
|
||||
2. Configure PostgreSQL with migrations
|
||||
3. Implement auth endpoints first
|
||||
4. Build workout CRUD
|
||||
5. Add body metrics
|
||||
6. Deploy MVP
|
||||
|
||||
```text
|
||||
[Task] Done — design shipped
|
||||
- Prep API requirements (completed)
|
||||
- Explore architecture options (completed)
|
||||
- Clarify data and auth needs (completed)
|
||||
- Deliver API design doc (completed)
|
||||
```
|
||||
|
||||
All set — went from "I need an API" to a concrete design. Ready to start building when you are.
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
@@ -0,0 +1,242 @@
|
||||
# Example: High Start (Level 4)
|
||||
|
||||
Clear request with context — minimal clarification needed, fast to delivery.
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
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.
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
[Task] Clear requirements — fast path
|
||||
- Clarify implementation details (in_progress)
|
||||
- Deliver dark mode implementation (pending)
|
||||
```
|
||||
|
||||
```text
|
||||
▓▓▓▓░ 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.
|
||||
|
||||
```text
|
||||
[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
|
||||
```
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
|
||||
<turn>
|
||||
<user>
|
||||
|
||||
1, I'll put it in my header
|
||||
|
||||
</user>
|
||||
<claude>
|
||||
|
||||
```text
|
||||
▓▓▓▓▓ Ready
|
||||
|
||||
All clear — delivering implementation.
|
||||
```
|
||||
|
||||
```text
|
||||
[Task] Delivering
|
||||
- Clarify implementation details (completed)
|
||||
- Deliver dark mode implementation (in_progress)
|
||||
```
|
||||
|
||||
## Dark Mode Implementation
|
||||
|
||||
**1. Configure Tailwind** (`tailwind.config.js`):
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
darkMode: 'class',
|
||||
// ... rest of config
|
||||
}
|
||||
```
|
||||
|
||||
**2. Theme Provider** (`components/theme-provider.tsx`):
|
||||
|
||||
```typescript
|
||||
'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`):
|
||||
|
||||
```typescript
|
||||
'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`):
|
||||
|
||||
```typescript
|
||||
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):
|
||||
|
||||
```typescript
|
||||
<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**:
|
||||
|
||||
```typescript
|
||||
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
|
||||
|
||||
```text
|
||||
[Task] Done
|
||||
- Clarify implementation details (completed)
|
||||
- Deliver dark mode implementation (completed)
|
||||
```
|
||||
|
||||
All set — drop `<ThemeToggle />` in your header and you're good.
|
||||
|
||||
</claude>
|
||||
</turn>
|
||||
Reference in New Issue
Block a user