archive: add-dark-light-mode change complete

This commit is contained in:
Marko Djordjevic 2026-02-13 09:37:18 +01:00
parent a8f9327d19
commit 88e7347918
7 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-02-12

View file

@ -0,0 +1,69 @@
## Context
The app is a Next.js 16 candlestick chart annotation tool using Tailwind CSS 3 with shadcn-ui style CSS variables (HSL values in `:root`). Tailwind is already configured with `darkMode: ["class"]`, meaning the `.dark` class on `<html>` controls dark mode. Currently only light-mode CSS variables are defined in `globals.css`, and the hacker-theme spec describes a dark-only neon green aesthetic. The app needs to support both themes with system detection and manual override.
## Goals / Non-Goals
**Goals:**
- Detect system color scheme preference (`prefers-color-scheme`) and apply matching theme on first load
- Provide a manual toggle in the UI to switch between dark, light, and system modes
- Persist the user's choice in `localStorage`
- Avoid flash of wrong theme (FOUC) on page load
- Define dark-mode CSS variables that maintain the hacker-theme aesthetic
- Define light-mode CSS variables that provide a clean, readable alternative while keeping monospace typography and terminal feel
**Non-Goals:**
- Custom user-defined color themes beyond dark/light
- Per-component theme overrides
- Server-side theme detection (SSR will use system default, hydration applies preference)
## Decisions
### 1. Use `next-themes` for theme management
**Choice**: Install and use `next-themes` library.
**Rationale**: `next-themes` is the standard solution for Next.js theme switching. It handles:
- System preference detection via `prefers-color-scheme` media query
- Adding/removing the `.dark` class on `<html>` (matches our existing `darkMode: ["class"]` Tailwind config)
- `localStorage` persistence
- FOUC prevention via inline script injection
- SSR compatibility
**Alternatives considered**:
- **Custom React context + useEffect**: Would work but requires reimplementing FOUC prevention, system detection, and localStorage sync. `next-themes` is battle-tested and small (~2KB).
- **CSS-only with `prefers-color-scheme`**: No manual toggle capability, which is a requirement.
### 2. Three-mode toggle: System / Light / Dark
**Choice**: Offer three modes — System (follows OS), Light (forced), Dark (forced).
**Rationale**: Users who want to match their OS get automatic switching. Users who prefer a specific theme can lock it in. This is the standard UX pattern.
### 3. CSS variable approach for both themes
**Choice**: Define `:root` (light) and `.dark` (dark) CSS variable sets in `globals.css`.
**Rationale**: The existing setup already uses HSL CSS variables consumed by Tailwind. Adding a `.dark` selector block with dark-mode values requires zero changes to component code — all `bg-background`, `text-foreground`, etc. classes automatically switch. The hacker-theme neon green palette maps to the dark variant. The light variant uses neutral/slate tones with green accents.
### 4. Theme toggle placement
**Choice**: Place the theme toggle button in the sidebar, near the top or bottom of the tool list.
**Rationale**: The sidebar is always visible and is the existing control surface. A small icon button (sun/moon/monitor icons from lucide-react) keeps it unobtrusive. Three-state cycle: click rotates through system → light → dark.
### 5. ThemeProvider wraps the app in layout.tsx
**Choice**: Wrap `{children}` with `<ThemeProvider>` from `next-themes` in the root layout.
**Rationale**: This is the standard integration point. The provider must be a client component, so we'll create a thin `ThemeProvider` wrapper component.
## Risks / Trade-offs
- **FOUC on first load**: `next-themes` injects a blocking script to prevent this, but in rare edge cases (slow JS) users might see a brief flash. → Mitigation: `next-themes` handles this well out of the box; `attribute="class"` config ensures Tailwind picks it up immediately.
- **Chart library theming**: `lightweight-charts` has its own color configuration that's set programmatically, not via CSS. → Mitigation: The chart component will need to read the current theme and pass appropriate colors to the chart instance. Listen to theme changes and update chart colors accordingly.
- **Hacker theme divergence**: The light theme won't have neon glow effects or scanlines — these only make sense on dark backgrounds. → Mitigation: This is intentional. The light theme keeps monospace fonts and terminal structure but uses conventional colors for readability.
- **Component styles relying on hardcoded colors**: If any components use hardcoded hex colors instead of CSS variables, they won't switch. → Mitigation: Audit components during implementation and replace hardcoded values with CSS variable references.

View file

@ -0,0 +1,28 @@
## Why
The application currently uses a fixed hacker theme with a dark background. Users may prefer a light theme for better visibility in bright environments or to match their system preferences. Adding dark/light mode with system theme detection and manual override provides flexibility and improves accessibility.
## What Changes
- Add system theme detection that automatically applies dark or light mode based on the user's OS/browser preference
- Add a manual theme toggle control that allows users to override the system preference
- Persist the user's theme preference in localStorage so it survives page refreshes
- Update the hacker theme implementation to support both light and dark variants while maintaining the monospace, neon-accent aesthetic
- Ensure all UI components (sidebar, chart, buttons, inputs, modals) adapt correctly to both themes
## Capabilities
### New Capabilities
- `theme-switching`: System for detecting OS theme preference, providing manual theme toggle, and persisting user preference across sessions
### Modified Capabilities
- `hacker-theme`: Extend existing hacker theme to support both dark and light color variants while preserving monospace typography, neon accents, and terminal aesthetic
- `ui-shell`: Add theme toggle control to the UI layout
## Impact
- **Frontend**: New theme provider/context in Next.js app, updated Tailwind config with light mode colors, theme toggle component
- **CSS**: Extended color palette in globals.css and tailwind.config.ts for light mode variants
- **Components**: All components must support both themes (sidebar, chart canvas, toolbox, modals, inputs)
- **Dependencies**: May require next-themes or similar library for robust theme management
- **User preferences**: localStorage usage for theme persistence

View file

@ -0,0 +1,121 @@
## MODIFIED Requirements
### Requirement: Matrix-style color scheme
The application SHALL use a dark background with neon green (#00ff41) accent color scheme in dark mode and a light background with muted green accents in light mode.
#### Scenario: Dark mode background colors
- **WHEN** rendering main layout in dark mode
- **THEN** page background is #0a0e0a (near black), sidebar background is #0d110d, and chart background is #000000
#### Scenario: Dark mode primary accent color
- **WHEN** highlighting interactive elements in dark mode
- **THEN** uses #00ff41 (matrix green) as the primary accent color
#### Scenario: Dark mode secondary accent colors
- **WHEN** rendering labels or status indicators in dark mode
- **THEN** uses #ff0040 (neon red) for break_down/destructive, #00ff41 (neon green) for break_up/success, #00d4ff (neon cyan) for informational, #ffff00 (neon yellow) for warnings
#### Scenario: Dark mode text colors
- **WHEN** rendering text in dark mode
- **THEN** primary text is #00ff41, secondary text is #00cc33, and disabled text is #003311
#### Scenario: Dark mode border colors
- **WHEN** rendering borders in dark mode
- **THEN** uses #00ff41 with 1px solid for active elements, #003311 for inactive borders
#### Scenario: Light mode background colors
- **WHEN** rendering main layout in light mode
- **THEN** page background is #f8faf8 (off-white with green tint), sidebar background is #f0f4f0, and chart background is #ffffff
#### Scenario: Light mode primary accent color
- **WHEN** highlighting interactive elements in light mode
- **THEN** uses #16a34a (green-600) as the primary accent color
#### Scenario: Light mode text colors
- **WHEN** rendering text in light mode
- **THEN** primary text is #1a1a2e (near-black), secondary text is #4a5568 (gray-600), and disabled text is #a0aec0 (gray-400)
#### Scenario: Light mode border colors
- **WHEN** rendering borders in light mode
- **THEN** uses #d1d5db (gray-300) for borders, #16a34a for active element borders
### Requirement: Neon glow effects
The application SHALL apply glow effects to active and interactive elements in dark mode only.
#### Scenario: Button hover glow in dark mode
- **WHEN** user hovers over any button in dark mode
- **THEN** button shows box-shadow: 0 0 10px #00ff41, 0 0 20px #00ff4180
#### Scenario: No glow in light mode
- **WHEN** user hovers over any button in light mode
- **THEN** button shows standard hover state (subtle background change) without neon glow effects
#### Scenario: Active tool glow in dark mode
- **WHEN** a tool is active (selected) in dark mode
- **THEN** button shows pulsing glow animation
#### Scenario: Active tool highlight in light mode
- **WHEN** a tool is active (selected) in light mode
- **THEN** button shows solid green background/border highlight without glow animation
### Requirement: Responsive dark theme
The application SHALL maintain theme consistency across all components and states in both dark and light modes.
#### Scenario: Modal dialogs in dark mode
- **WHEN** displaying confirmation dialogs in dark mode
- **THEN** modal has dark background (#0d110d), neon green border, monospace font
#### Scenario: Modal dialogs in light mode
- **WHEN** displaying confirmation dialogs in light mode
- **THEN** modal has white background, gray border, monospace font
#### Scenario: Input fields in dark mode
- **WHEN** rendering text inputs in dark mode
- **THEN** inputs have transparent background, #00ff41 border, #00ff41 text
#### Scenario: Input fields in light mode
- **WHEN** rendering text inputs in light mode
- **THEN** inputs have white background, gray-300 border, near-black text
#### Scenario: Scrollbars in dark mode
- **WHEN** content requires scrolling in dark mode
- **THEN** scrollbar track is #003311, thumb is #00ff41
#### Scenario: Scrollbars in light mode
- **WHEN** content requires scrolling in light mode
- **THEN** scrollbar track is #e2e8f0, thumb is #94a3b8
### Requirement: Tailwind CSS configuration
The Tailwind config SHALL support theme tokens for both dark and light modes via CSS variables.
#### Scenario: CSS variables for dark mode
- **WHEN** the `dark` class is on `<html>`
- **THEN** CSS variables resolve to hacker-theme dark palette values
#### Scenario: CSS variables for light mode
- **WHEN** the `dark` class is NOT on `<html>`
- **THEN** CSS variables resolve to light theme palette values
### Requirement: globals.css updates
The globals.css file SHALL define CSS variables for both light and dark themes.
#### Scenario: Light mode CSS variables
- **WHEN** globals.css is loaded without `dark` class
- **THEN** `:root` defines light theme color variables
#### Scenario: Dark mode CSS variables
- **WHEN** globals.css is loaded with `dark` class on `<html>`
- **THEN** `.dark` selector overrides with hacker-theme dark palette variables
## ADDED Requirements
### Requirement: Cyber-retro effects dark-mode only
The application SHALL only apply CRT scanline effects, noise textures, and flicker animations in dark mode.
#### Scenario: Scanlines in dark mode
- **WHEN** rendering main container in dark mode
- **THEN** applies subtle CRT scanline overlay at 5% opacity
#### Scenario: No scanlines in light mode
- **WHEN** rendering main container in light mode
- **THEN** no scanline or CRT effects are applied

View file

@ -0,0 +1,72 @@
## ADDED Requirements
### Requirement: System theme detection
The application SHALL detect the user's operating system color scheme preference on initial load and apply the matching theme (dark or light).
#### Scenario: System prefers dark mode
- **WHEN** the user's OS is set to dark mode and no manual preference is stored
- **THEN** the application renders with the dark theme
#### Scenario: System prefers light mode
- **WHEN** the user's OS is set to light mode and no manual preference is stored
- **THEN** the application renders with the light theme
#### Scenario: System preference changes while app is open
- **WHEN** the user changes their OS color scheme while the app is open and theme is set to "system"
- **THEN** the application switches to match the new system preference without page reload
### Requirement: Manual theme toggle
The application SHALL provide a toggle control that cycles through three modes: System, Light, and Dark.
#### Scenario: Toggle from system to light
- **WHEN** user clicks the theme toggle while in "system" mode
- **THEN** the theme switches to "light" mode regardless of system preference
#### Scenario: Toggle from light to dark
- **WHEN** user clicks the theme toggle while in "light" mode
- **THEN** the theme switches to "dark" mode regardless of system preference
#### Scenario: Toggle from dark to system
- **WHEN** user clicks the theme toggle while in "dark" mode
- **THEN** the theme switches to "system" mode and applies the current OS preference
#### Scenario: Toggle icon reflects current mode
- **WHEN** the theme is set to "system"
- **THEN** the toggle displays a monitor icon
- **WHEN** the theme is set to "light"
- **THEN** the toggle displays a sun icon
- **WHEN** the theme is set to "dark"
- **THEN** the toggle displays a moon icon
### Requirement: Theme preference persistence
The application SHALL persist the user's theme choice in localStorage so it survives page refreshes and browser restarts.
#### Scenario: Preference survives refresh
- **WHEN** user selects "dark" mode and refreshes the page
- **THEN** the application loads in dark mode without flashing light mode first
#### Scenario: Clearing preference
- **WHEN** user selects "system" mode
- **THEN** the stored preference is set to "system" and the OS preference is followed
### Requirement: No flash of incorrect theme (FOUC prevention)
The application SHALL apply the correct theme before the first paint to prevent a visible flash of the wrong color scheme.
#### Scenario: Dark mode user loads page
- **WHEN** a user with dark mode preference stored loads the page
- **THEN** the page renders dark from the first visible frame with no white flash
#### Scenario: Light mode user loads page
- **WHEN** a user with light mode preference stored loads the page
- **THEN** the page renders light from the first visible frame with no dark flash
### Requirement: ThemeProvider integration
The application SHALL use a ThemeProvider component wrapping the app in the root layout. The provider SHALL use the `class` strategy to add/remove the `dark` class on the `<html>` element.
#### Scenario: Dark class applied
- **WHEN** the active theme is dark
- **THEN** the `<html>` element has the `dark` class
#### Scenario: Dark class removed
- **WHEN** the active theme is light
- **THEN** the `<html>` element does NOT have the `dark` class

View file

@ -0,0 +1,20 @@
## ADDED Requirements
### Requirement: Theme toggle in sidebar
The UI shell SHALL include a theme toggle button in the sidebar. The button SHALL be positioned at the bottom of the sidebar, visually separated from the tool buttons.
#### Scenario: Toggle button renders
- **WHEN** the application loads
- **THEN** a theme toggle button is visible at the bottom of the sidebar
#### Scenario: Toggle button displays correct icon
- **WHEN** the current theme mode is "system"
- **THEN** the button shows a monitor icon (lucide-react `Monitor`)
- **WHEN** the current theme mode is "light"
- **THEN** the button shows a sun icon (lucide-react `Sun`)
- **WHEN** the current theme mode is "dark"
- **THEN** the button shows a moon icon (lucide-react `Moon`)
#### Scenario: Toggle button has tooltip
- **WHEN** user hovers over the theme toggle button
- **THEN** a tooltip displays the current mode name (e.g., "Theme: System", "Theme: Light", "Theme: Dark")

View file

@ -0,0 +1,45 @@
## 1. Install and configure next-themes
- [x] 1.1 Install `next-themes` package
- [x] 1.2 Create `src/components/ThemeProvider.tsx` client component wrapping `next-themes` ThemeProvider with `attribute="class"`, `defaultTheme="system"`, `enableSystem=true`
- [x] 1.3 Update `src/app/layout.tsx` to wrap children with ThemeProvider and add `suppressHydrationWarning` to `<html>`
## 2. Define dark/light CSS variables
- [x] 2.1 Update `src/app/globals.css` `:root` block with light theme HSL values (off-white backgrounds, green-600 accents, near-black text, gray borders)
- [x] 2.2 Add `.dark` block in `src/app/globals.css` with hacker-theme dark palette values (terminal blacks, matrix green #00ff41 accents, neon text)
- [x] 2.3 Update scrollbar styles in `globals.css` to use theme-aware colors for both light and dark modes
- [x] 2.4 Conditionally apply CRT scanline/glow CSS effects only inside `.dark` selector
## 3. Update Tailwind config
- [x] 3.1 Verify `tailwind.config.ts` has `darkMode: ["class"]` (already set)
- [x] 3.2 Ensure all custom color tokens reference CSS variables so they auto-switch with theme
## 4. Add theme toggle to sidebar
- [x] 4.1 Create theme toggle button component using `useTheme()` from `next-themes` that cycles system → light → dark
- [x] 4.2 Display correct icon per mode: `Monitor` (system), `Sun` (light), `Moon` (dark) from lucide-react
- [x] 4.3 Add tooltip showing current mode name on hover
- [x] 4.4 Place the toggle button at the bottom of `src/components/Toolbox.tsx`, visually separated from tool buttons
## 5. Update chart theming
- [x] 5.1 Update `src/components/CandleChart.tsx` to read current theme and apply matching chart colors (background, grid, text, crosshair)
- [x] 5.2 Listen for theme changes and re-apply chart colors when theme switches without full remount
## 6. Audit and fix hardcoded colors
- [x] 6.1 Audit `src/components/Toolbox.tsx` for hardcoded color values and replace with CSS variable references
- [x] 6.2 Audit `src/components/SvgOverlay.tsx` for hardcoded colors and update for theme compatibility
- [x] 6.3 Audit `src/app/page.tsx` and `src/app/annotation-types/page.tsx` for hardcoded colors
- [x] 6.4 Verify shadcn-ui components (`button.tsx`, `input.tsx`, `tooltip.tsx`) use theme variables correctly
## 7. Test and verify
- [x] 7.1 Verify no FOUC — page loads in correct theme without flash
- [x] 7.2 Test toggle cycles correctly through system → light → dark → system
- [x] 7.3 Verify localStorage persistence survives page refresh
- [x] 7.4 Verify system preference detection works (change OS theme while set to "system")
- [x] 7.5 Verify chart colors update when theme switches
- [x] 7.6 Verify all components render correctly in both themes