# Dark Mode Support Implementation Plan > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add cookie-persisted dark/light theming with a floating top-right toggle on all regular views, while keeping export always light. **Architecture:** Theme behavior is centralized in one reusable partial with two include modes (`head` and `body`) so every themed page gets identical bootstrap, cookie logic, and toggle UI. Existing views keep structure/layout classes and only adjust color-related classes needed for dark-mode readability. Export is explicitly excluded and remains light-only. **Tech Stack:** PHP 8, server-rendered PHP views, Tailwind via CDN, Bootstrap Icons, vanilla JavaScript, browser cookies. --- ## Chunk 1: Shared Theme Unit and Core Integration ### Task 1: Create shared theme partial with explicit section contract **Files:** - Create: `views/partials/theme.php` - Test: browser manual validation on `/login` after integration - [ ] **Step 1: Add section-gated partial skeleton** ```php (function() { function readThemeCookie() { var match = document.cookie.match(/(?:^|; )theme=([^;]+)/); if (!match) return null; var value = decodeURIComponent(match[1]); return (value === 'dark' || value === 'light') ? value : null; } var cookieTheme = readThemeCookie(); var systemDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; var resolved = cookieTheme || (systemDark ? 'dark' : 'light'); document.documentElement.classList.toggle('dark', resolved === 'dark'); document.documentElement.setAttribute('data-theme', resolved); })(); ``` Expected output: - `` has `dark` class before body render when resolved theme is dark. - `data-theme` is set to `dark` or `light`. - [ ] **Step 3: Implement `head` shared CSS tokens and semantic utility classes** ```php ``` Expected output: - Shared classes exist for page/surface/text/border/input mappings. - Toggle has fixed top-right placement and z-index 50. - [ ] **Step 4: Implement `body` output with toggle markup and no-JS fallback** ```php ``` - [ ] **Step 5: Implement `body` behavior script with cookie helpers and state sync** ```php ``` Expected output: - Click toggles theme immediately, updates icon, `aria-label`, and `title`. - Cookie write attempt does not block visual switch if persistence is restricted. - [ ] **Step 6: Run syntax check for new partial** Run: `php -l views/partials/theme.php` Expected: `No syntax errors detected in views/partials/theme.php`. - [ ] **Step 7: Commit Task 1** ```bash git add views/partials/theme.php git commit -m "feat: add shared theme partial with cookie-based dark mode" ``` ### Task 2: Integrate theme partial into login and setup views **Files:** - Modify: `views/login.php` - Modify: `views/setup.php` - Test: `/login`, `/setup` - [ ] **Step 1: Insert head include in `views/login.php`** ```php ``` Placement: in ``, after Tailwind/bootstrap includes, before ``. - [ ] **Step 2: Insert body include in `views/login.php`** ```php ``` Placement: before ``. - [ ] **Step 3: Apply color-only updates in `views/login.php`** Apply concrete replacements: - `` -> `` - login card `bg-white` -> `theme-surface` - heading `text-gray-800` -> `theme-text` - label text `text-gray-700` -> `theme-text` - input `border-gray-300` -> add `theme-input theme-border` Expected outcome: login card, labels, and input fields are readable in both themes without layout shift. - [ ] **Step 4: Insert head include in `views/setup.php`** ```php ``` Placement: in ``, after Tailwind/bootstrap includes, before ``. - [ ] **Step 5: Insert body include in `views/setup.php`** ```php ``` Placement: before ``. - [ ] **Step 6: Apply color-only updates in `views/setup.php`** Apply concrete replacements: - `` -> `` - setup card `bg-white` -> `theme-surface` - title and labels `text-gray-*` -> `theme-text` / `theme-text-muted` - password inputs `border-gray-300` -> add `theme-input theme-border` Expected outcome: setup page remains visually consistent, with readable text/inputs in both themes. - [ ] **Step 7: Run syntax checks for auth/setup views** Run: `php -l views/login.php && php -l views/setup.php` Expected: both report `No syntax errors detected`. - [ ] **Step 8: Manual verification on `/login` and `/setup`** Expected: - toggle appears fixed top-right - keyboard Tab focuses toggle with visible focus style - Enter/Space toggles theme - icon and `aria-label`/`title` change each toggle - reload preserves preference - [ ] **Step 9: Commit Task 2** ```bash git add views/login.php views/setup.php git commit -m "feat: integrate dark mode toggle on auth and setup views" ``` ## Chunk 2: Main Views, Export Exception, and Final Verification ### Task 3: Integrate theme partial into home view **Files:** - Modify: `views/index.php` - Test: `/home` - [ ] **Step 1: Add head include in `views/index.php`** ```php ``` Placement requirement: after Tailwind/bootstrap includes and before ``. Expected outcome: exactly one head include in required position. - [ ] **Step 2: Add body include in `views/index.php`** ```php ``` Placement requirement: before local page scripts and before ``. Expected outcome: exactly one body include in required position. - [ ] **Step 3: Update page and header surfaces in `views/index.php`** Concrete updates: - body wrapper `bg-gray-100` -> `theme-page` - header/card wrappers `bg-white` -> `theme-surface` - heading/body text `text-gray-*` -> `theme-text` / `theme-text-muted` Expected outcome: page shell and header are readable in both themes. - [ ] **Step 4: Update table surfaces in `views/index.php`** Concrete updates: - table header `` -> use `theme-surface-alt theme-border` - table body divider `divide-gray-200` -> `theme-border` - table cell text classes `text-gray-500`, `text-gray-600`, `text-gray-900` -> `theme-text`/`theme-text-muted` Expected outcome: table head/body text and borders remain readable in both themes. - [ ] **Step 5: Update vehicle cards/list rows in `views/index.php`** Concrete updates: - grid cards `bg-white` -> `theme-surface` - list container `bg-white` -> `theme-surface` - row hover `hover:bg-gray-50` -> `hover:opacity-95` and apply `theme-surface-alt` on row container where needed - small metadata text `text-gray-*` -> `theme-text-muted` Expected outcome: vehicle cards/list rows remain readable in both themes. - [ ] **Step 6: Update modal container and input surfaces in `views/index.php`** Concrete updates: - modal panel `bg-white` -> `theme-surface` - modal close/cancel gray text -> `theme-text-muted` - inputs with `border-gray-300` -> add `theme-input theme-border` - optional helper text gray shades -> `theme-text-muted` Expected outcome: cards, table, and modal inputs remain readable/usable in both themes. - [ ] **Step 7: Verify and adjust flash message contrast in `views/index.php`** Concrete updates: - keep existing light-mode classes for base state (`bg-red-100 border-red-400 text-red-700`, `bg-green-100 border-green-400 text-green-700`) - add dark-only variants (using `dark:` classes or equivalent conditional class strategy) for error/success contrast in dark mode - keep existing rounded/padding/layout classes unchanged Expected outcome: error and success flash messages are legible and visually distinct in both light and dark modes. - [ ] **Step 8: Run syntax check for `views/index.php`** Run: `php -l views/index.php` Expected: `No syntax errors detected in views/index.php`. - [ ] **Step 9: Manual verification for `/home`** Expected: - theme toggles correctly - theme persists after reload - cards/tables/modals remain readable in both themes - [ ] **Step 10: Commit Task 3** ```bash git add views/index.php git commit -m "feat: integrate dark mode support in home view" ``` ### Task 4: Integrate theme partial into vehicle detail view **Files:** - Modify: `views/vehicle.php` - Test: `/vehicles/{id}` - [ ] **Step 1: Add head include in `views/vehicle.php`** ```php ``` Placement requirement: after Tailwind/bootstrap includes and before ``. Expected outcome: exactly one head include in required position. - [ ] **Step 2: Add body include in `views/vehicle.php`** ```php ``` Placement requirement: before page-local scripts and before ``. Expected outcome: exactly one body include in required position. - [ ] **Step 3: Update page shell, vehicle card, and maintenance form in `views/vehicle.php`** Concrete updates: - body/header/card backgrounds `bg-white/bg-gray-*` -> theme classes - text grays on headings/body -> `theme-text` / `theme-text-muted` - form inputs/borders -> `theme-input theme-border` Expected outcome: top section and add-maintenance form are readable in both themes. - [ ] **Step 4: Update dropdown menu surfaces in `views/vehicle.php`** Concrete updates: - suggestions box `bg-white border-gray-300` -> `theme-surface theme-border` - export menu panel `bg-white` -> `theme-surface` - export menu links `hover:bg-gray-100` -> `hover:opacity-95` - export menu link text grays -> `theme-text` Expected outcome: dropdown menus remain readable and interactive in both themes. - [ ] **Step 5: Update maintenance history surfaces in `views/vehicle.php`** Concrete updates: - history container `bg-white` -> `theme-surface` - item cards `border-gray-200` -> `theme-border` - item card hover `hover:border-blue-300` -> `hover:border-blue-500` - item metadata text gray shades -> `theme-text-muted` Expected outcome: maintenance history cards remain readable in both themes. - [ ] **Step 6: Update modal panel and input surfaces in `views/vehicle.php`** Concrete updates: - edit modal panels `bg-white` -> `theme-surface` - modal input `border-gray-300` -> `theme-input theme-border` - modal close/cancel text grays -> `theme-text-muted` Expected outcome: dropdowns, history cards, and modals remain readable and interactive in both themes. - [ ] **Step 7: Run syntax check for `views/vehicle.php`** Run: `php -l views/vehicle.php` Expected: `No syntax errors detected in views/vehicle.php`. - [ ] **Step 8: Manual verification for `/vehicles/{id}`** Expected: - theme toggles and persists - forms/dropdowns/modals remain readable in both themes - navigation to/from `/home` keeps cookie-based preference - [ ] **Step 9: Commit Task 4** ```bash git add views/vehicle.php git commit -m "feat: integrate dark mode support in vehicle detail view" ``` ### Task 5: Integrate theme partial into settings view **Files:** - Modify: `views/settings.php` - Test: `/settings` - [ ] **Step 1: Add head include in `views/settings.php`** ```php ``` Placement requirement: after Tailwind/bootstrap includes and before ``. Expected outcome: exactly one head include in required position. - [ ] **Step 2: Add body include in `views/settings.php`** ```php ``` Placement requirement: before local scripts (if any) and before ``. Expected outcome: exactly one body include in required position. - [ ] **Step 3: Update page/header/card surfaces in `views/settings.php`** Concrete updates: - body/header/cards `bg-white/bg-gray-*` -> `theme-page`/`theme-surface`/`theme-surface-alt` - gray text classes -> `theme-text` / `theme-text-muted` Expected outcome: page shell and cards remain readable in both themes. - [ ] **Step 4: Update list rows and password form surfaces in `views/settings.php`** Concrete updates: - quick-task row hover/background and borders -> theme classes - password input borders/backgrounds -> `theme-input theme-border` Expected outcome: list rows and form fields are readable and distinguishable in both themes. - [ ] **Step 5: Verify action/footer contrast in `views/settings.php`** Concrete updates: - logout action danger colors may be adjusted if needed for dark-mode contrast while preserving danger semantics - powered footer card `bg-white` -> `theme-surface` - powered footer title/body grays -> `theme-text` / `theme-text-muted` - GitHub icon/link muted grays -> `theme-text-muted` while preserving blue link emphasis Expected outcome: call-to-action and footer text remain legible in both themes. - [ ] **Step 6: Run syntax check for `views/settings.php`** Run: `php -l views/settings.php` Expected: `No syntax errors detected in views/settings.php`. - [ ] **Step 7: Manual verification for `/settings`** Expected: - theme toggles and persists - cards/list rows/inputs remain readable in both themes - navigation to/from `/home` keeps cookie-based preference - [ ] **Step 8: Commit Task 5** ```bash git add views/settings.php git commit -m "feat: integrate dark mode support in settings view" ``` ### Task 6: Validate export exception and complete end-to-end checks **Files:** - Verify unchanged: `views/export.php` - Verify behavior: `views/partials/theme.php` - Verify integrated views: `views/login.php`, `views/setup.php`, `views/index.php`, `views/vehicle.php`, `views/settings.php` - [ ] **Step 1: Verify `views/export.php` has no theme include/toggle** Expected: - no `$themeSection` include - no `#themeToggle` markup - no dark-mode bootstrap script and no `html.dark` application path in export template - [ ] **Step 2: Verify cookie contract in devtools** Flow: - toggle theme on any themed page - inspect cookie attributes Expected: - cookie name `theme` - value `dark` or `light` - `Max-Age=31536000` - `Path=/` - `SameSite=Lax` - [ ] **Step 3: Verify accessibility state updates on toggle** Flow: - focus toggle via keyboard - activate twice Expected per click: - icon swaps moon/sun - `aria-label` and `title` update to next action - [ ] **Step 4: Verify export is always light** Flow: - set `theme=dark` - open `/vehicles/{id}/export/html` Expected: - export remains light styled - toggle not present - `` does not receive `dark` class on export, even when `theme=dark` - [ ] **Step 5: Verify toggle stays visible above modals/dropdowns** Flow: - open each page with modal/dropdown support (`/home`, `/vehicles/{id}`) - open add/edit modals and export/suggestions dropdowns Expected: - floating toggle remains visible and clickable above overlays - [ ] **Step 5a: Apply layering remediation if Step 5 fails** Flow: - if toggle is hidden or unclickable over overlays, adjust stacking order in `views/partials/theme.php` Concrete remediation: - increase `#themeToggle` z-index above conflicting overlay layer - if conflict persists, reduce non-critical overlay z-index where safe without breaking modal usability Expected: - toggle remains visible/clickable while modals/dropdowns still function correctly - [ ] **Step 6: Verify no-cookie fallback uses system preference** Flow: - delete `theme` cookie - load `/home` Expected: - resolved theme follows current `prefers-color-scheme` - [ ] **Step 7: Verify invalid-cookie fallback** Flow: - set `theme=invalid` - reload `/home` Expected: - invalid cookie ignored - resolved theme follows current system preference - [ ] **Step 8: Verify no-JS fallback** Flow: - disable JS in browser - open `/login` Expected: - light mode only - no visible floating toggle - login form remains usable - [ ] **Step 9: Verify cookie-write-failure behavior** Flow: - use a browser mode/policy that blocks cookie writes (or simulate via devtools/privacy settings) - click toggle on `/home` Expected: - theme still switches immediately on the current page - no error message is shown to the user - persistence across reload is not required in this scenario - [ ] **Step 10: Run final PHP syntax sweep** Run: ```bash php -l views/partials/theme.php && \ php -l views/login.php && \ php -l views/setup.php && \ php -l views/index.php && \ php -l views/vehicle.php && \ php -l views/settings.php ``` Expected: all report `No syntax errors detected`. - [ ] **Step 11: No additional commit for verification-only task** Expected: - Task 6 records verification results only. - No new commit is created unless this task introduces actual file changes.