Files
fete/specs/023-dark-mode/spec.md
nitrix 6aeb4b8bca Migrate project artifacts to spec-kit format
- Move cross-cutting docs (personas, design system, implementation phases,
  Ideen.md) to .specify/memory/
- Move cross-cutting research and plans to .specify/memory/research/ and
  .specify/memory/plans/
- Extract 5 setup tasks from spec/setup-tasks.md into individual
  specs/001-005/spec.md files with spec-kit template format
- Extract 20 user stories from spec/userstories.md into individual
  specs/006-026/spec.md files with spec-kit template format
- Relocate feature-specific research and plan docs into specs/[feature]/
- Add spec-kit constitution, templates, scripts, and slash commands
- Slim down CLAUDE.md to Claude-Code-specific config, delegate principles
  to .specify/memory/constitution.md
- Update ralph.sh with stream-json output and per-iteration logging
- Delete old spec/ and docs/agents/ directories
- Gitignore Ralph iteration JSONL logs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:19:41 +01:00

121 lines
8.2 KiB
Markdown

# Feature Specification: Dark/Light Mode
**Feature**: `023-dark-mode`
**Created**: 2026-03-06
**Status**: Draft
**Source**: Migrated from spec/userstories.md
## User Scenarios & Testing
### User Story 1 - System preference respected on first visit (Priority: P1)
A user opens the app for the first time. The app automatically adopts their operating system or browser dark/light preference without any manual configuration required. No preference data is transmitted to the server.
**Why this priority**: This is the baseline behavior — it works without any user interaction and provides the correct experience immediately.
**Independent Test**: Can be tested by opening the app in a browser with `prefers-color-scheme: dark` set at the OS level and verifying the dark theme is applied, then repeating with light preference.
**Acceptance Scenarios**:
1. **Given** a user opens the app for the first time with no manual preference stored, **When** the OS/browser preference is `prefers-color-scheme: dark`, **Then** the app renders in dark mode.
2. **Given** a user opens the app for the first time with no manual preference stored, **When** the OS/browser preference is `prefers-color-scheme: light`, **Then** the app renders in light mode.
3. **Given** the app is rendering in either mode, **When** the page is loaded, **Then** no server request is made and no preference data is transmitted.
---
### User Story 2 - Manual toggle overrides system preference (Priority: P1)
A user can switch between dark and light mode using a visible toggle available on any page. Their choice is persisted in localStorage and takes precedence over the OS preference on all subsequent visits.
**Why this priority**: The explicit user preference must be honoured and must persist — without this, the toggle would reset on every visit, making it unusable.
**Independent Test**: Can be tested by toggling the mode, closing and reopening the browser, and verifying the manually selected mode is still active even if it differs from the OS preference.
**Acceptance Scenarios**:
1. **Given** the app is in light mode (system preference), **When** the user activates the dark mode toggle, **Then** the UI immediately switches to dark mode and the preference is stored in localStorage.
2. **Given** the user has a dark mode preference stored in localStorage, **When** the user revisits the app, **Then** dark mode is applied regardless of the current OS/browser preference.
3. **Given** the user has a light mode preference stored in localStorage and the OS preference is dark, **When** the user revisits the app, **Then** light mode is applied (localStorage takes precedence).
4. **Given** the app is running, **When** the user toggles the mode, **Then** no server request is made and no preference data is transmitted.
---
### User Story 3 - Toggle accessible from any page (Priority: P2)
The dark/light mode toggle is reachable from every page of the app — event pages, local event overview, creation form, etc. — so the user never has to navigate away to change their preference.
**Why this priority**: This is a usability enhancement. The feature works without it (if the toggle were only on one page), but accessibility from any page is important for a good experience.
**Independent Test**: Can be tested by navigating to the event page, the local event overview, and the creation form and verifying the toggle is visible and functional on each.
**Acceptance Scenarios**:
1. **Given** the user is on the local event overview page (`/`), **When** they look for the mode toggle, **Then** it is visible and functional.
2. **Given** the user is on an event page, **When** they look for the mode toggle, **Then** it is visible and functional.
3. **Given** the user is on the event creation form, **When** they look for the mode toggle, **Then** it is visible and functional.
---
### User Story 4 - Dark/light mode does not affect event-level color themes (Priority: P2)
Dark/light mode affects only the app's global UI chrome (navigation, local event overview, forms, etc.). Individual event pages use their own color theme (US-15), which is independent of the app-level dark/light setting.
**Why this priority**: Necessary to define the boundary between app-level theming and event-level theming clearly, but secondary to the core toggle behaviour.
**Independent Test**: Can be tested by creating an event with a custom color theme, then toggling dark/light mode and verifying the event page theme is unaffected while the surrounding chrome does change.
**Acceptance Scenarios**:
1. **Given** an event page is rendered with a custom color theme (US-15), **When** the user switches the app to dark mode, **Then** the event page color theme remains unchanged (only surrounding chrome changes).
2. **Given** the app is in dark mode, **When** the user navigates to the local event overview, **Then** the overview uses the dark color scheme.
---
### User Story 5 - Both modes meet WCAG AA contrast (Priority: P1)
Both dark and light modes must meet accessibility contrast requirements at the WCAG AA minimum level, ensuring the app is usable for users with visual impairments in both modes.
**Why this priority**: Accessibility is a baseline requirement per the project statutes, not an afterthought.
**Independent Test**: Can be tested using automated contrast checking tools against both mode variants.
**Acceptance Scenarios**:
1. **Given** the app is in dark mode, **When** text and interactive elements are checked for contrast ratio, **Then** all text/background pairings meet WCAG AA minimum (4.5:1 for normal text, 3:1 for large text).
2. **Given** the app is in light mode, **When** text and interactive elements are checked for contrast ratio, **Then** all text/background pairings meet WCAG AA minimum.
---
### Edge Cases
- What happens when the OS `prefers-color-scheme` value changes while the app is open (e.g. user switches OS theme at runtime)? If no manual preference is stored in localStorage, should the app react? [NEEDS EXPANSION during implementation]
- What happens when localStorage is unavailable (private browsing with strict settings)? The system preference fallback must still work without crashing.
- How does app-level dark/light mode interact with the event-level color themes (US-15) when an event page is embedded in the app chrome? Themes should remain readable in both modes.
## Requirements
### Functional Requirements
- **FR-001**: The app MUST detect and apply `prefers-color-scheme` as the default on first visit when no manual preference is stored in localStorage.
- **FR-002**: The app MUST provide a visible toggle UI element to switch between dark and light mode, accessible from any page.
- **FR-003**: The app MUST persist the user's manual mode preference in localStorage and apply it on subsequent visits, overriding the system preference.
- **FR-004**: Dark/light mode MUST affect all global app chrome: navigation, local event overview, event creation/editing forms, and all non-event-page UI elements.
- **FR-005**: Dark/light mode MUST NOT affect individual event page color themes (US-15); event pages are styled independently.
- **FR-006**: The mode switch MUST be entirely client-side; no server request is made and no preference data is transmitted.
- **FR-007**: Both dark and light modes MUST meet WCAG AA contrast requirements for all text and interactive elements.
- **FR-008**: The toggle MUST be accessible (keyboard-navigable, labelled for screen readers).
### Key Entities
- **DarkLightPreference**: A client-side-only value (`"dark"` | `"light"` | absent) stored in localStorage. No server-side equivalent. Determines which CSS theme is applied to the global app chrome.
## Success Criteria
### Measurable Outcomes
- **SC-001**: On first visit with `prefers-color-scheme: dark`, the dark theme is applied without any user interaction.
- **SC-002**: A user's manual toggle selection persists across browser sessions and overrides the OS preference.
- **SC-003**: The mode toggle is visible and functional on all primary app pages (local event overview, event page, creation form).
- **SC-004**: Automated contrast checks pass WCAG AA thresholds for all text elements in both dark and light modes.
- **SC-005**: No network request is made when toggling the mode or when the stored preference is applied on page load.