|
| 1 | +## Dark Mode for CodeX Docs |
| 2 | + |
| 3 | +### Summary |
| 4 | + |
| 5 | +Adds a complete dark mode feature to CodeX Docs with system preference detection, manual toggle, localStorage persistence, and WCAG 2.1 AA accessibility compliance. Includes comprehensive test coverage (139 Playwright e2e + 30 Mocha unit tests) across Chromium, Firefox, and WebKit. |
| 6 | + |
| 7 | +Built against the feature specification in [`.github/specs/dark-mode/`](.github/specs/dark-mode/README.md), implementing all functional requirements (FR-2.1 through FR-2.5) and non-functional requirements (NFR-3.1 through NFR-3.5) defined in [Requirements.md](.github/specs/dark-mode/Requirements.md). |
| 8 | + |
| 9 | +### What Changed |
| 10 | + |
| 11 | +**Theme System (Phase 1)** |
| 12 | +- `ThemeManager` singleton module handles initialization, preference detection, persistence, and theme switching |
| 13 | +- CSS custom properties architecture: light defaults in `vars.pcss`, dark overrides via `[data-theme="dark"]` selector in new `dark-mode.pcss` |
| 14 | +- Synchronous initialization before other modules to prevent flash of unstyled content (FOUC) |
| 15 | +- `@media (prefers-color-scheme: dark)` fallback for JS-disabled users, with `:not([data-theme="light"])` guard to respect explicit user choice |
| 16 | + |
| 17 | +**UI Components (Phase 2)** |
| 18 | +- Sun/moon toggle button in the header with animated icon swap |
| 19 | +- All 11 component stylesheets audited and migrated to CSS variables — zero hardcoded colors remain ([2.8 audit results](.github/specs/dark-mode/documentation/2.8-remaining-component-styles/ImplementationSummary.md)) |
| 20 | +- Components covered: header, sidebar, navigator, page, writing, button, copy-button, auth, table-of-content, error, greeting |
| 21 | + |
| 22 | +**Color Palette** |
| 23 | +- Tailwind zinc neutrals: `#18181B` background, `#27272A` surfaces, `#71717A` borders, `#E4E4E7` primary text |
| 24 | +- All color pairs verified against WCAG 2.1 AA contrast thresholds (4.5:1 for text, 3:1 for UI boundaries) |
| 25 | +- 5 initial contrast failures found and fixed during accessibility testing |
| 26 | + |
| 27 | +**Database Migration** |
| 28 | +- Replaced unmaintained `nedb` (last updated 2016) with `@seald-io/nedb` — the original package crashes on Node.js ≥ 24 due to removed `util.isDate()` / `util.isRegExp()` functions |
| 29 | +- Drop-in replacement, identical on-disk format, no data migration needed |
| 30 | + |
| 31 | +### Test Coverage |
| 32 | + |
| 33 | +| Suite | Tests | Scope | |
| 34 | +|-------|-------|-------| |
| 35 | +| Theme toggle | 6 | Button visibility, click, keyboard (Enter/Space), ARIA labels | |
| 36 | +| Persistence | 14 | Save/restore, reload, clear, invalid values, rapid toggles, format validation | |
| 37 | +| System preference | 4 | `prefers-color-scheme` emulation, saved pref overrides system | |
| 38 | +| Components | 16 | CSS variable values per theme, rendered body/header/text colors | |
| 39 | +| No-FOUC | 4 | `data-theme` present on load, DOM consistent with localStorage | |
| 40 | +| Accessibility | 26 | WCAG AA contrast ratios, keyboard nav, focus visibility, ARIA semantics | |
| 41 | +| Performance | 12 | Toggle < 100ms, CLS < 0.05, no FOUC, CSS architecture validation | |
| 42 | +| Browser compat | 19 × 3 | CSS vars, toggle, localStorage, system pref, colors, events (Chromium + Firefox + WebKit) | |
| 43 | +| Unit (Mocha) | 30 | ThemeManager init, setTheme, getCurrentTheme, getSystemPreference, persistence, events | |
| 44 | + |
| 45 | +**Total: 169 tests, all passing** |
| 46 | + |
| 47 | +### Bugs Found & Fixed During Testing |
| 48 | + |
| 49 | +| Bug | Root Cause | Fix | |
| 50 | +|-----|-----------|-----| |
| 51 | +| Toggle icons never updated | Wrong event name in listener | Listen to `themeChange` | |
| 52 | +| Page white in dark mode | Hardcoded `background: white` on body/header/copy-button | Replaced with `var(--color-bg-main)` | |
| 53 | +| Greeting page lost dark mode | Missing `main.bundle.js` in `index.twig` | Added script tag | |
| 54 | +| Alias 500 error on page save | Missing `await` on `alias.save()` | Added `await` in pages controller | |
| 55 | +| Server crash on Node 24 | `nedb` uses removed `util.isDate()` | Migrated to `@seald-io/nedb` | |
| 56 | +| `applyTheme(null)` on invalid localStorage | `hasSavedPreference()` true but value invalid | Fixed `init()` fallback chain | |
| 57 | +| System dark overrides explicit light choice | CSS `@media` `:root` ties with `[data-theme="light"]` | Added `:not([data-theme="light"])` guard | |
| 58 | + |
| 59 | +### Files Changed (excluding lock files and spec docs) |
| 60 | + |
| 61 | +- **35 files changed**, ~2,700 lines added, ~45 removed |
| 62 | +- **New source files:** `themeManager.js`, `themeToggle.js`, `dark-mode.pcss`, `playwright.config.ts`, 8 e2e spec files, 1 fixture, 1 unit test file |
| 63 | +- **Modified source files:** `app.js`, `vars.pcss`, `header.pcss`, `sidebar.pcss`, `page.pcss`, `navigator.pcss`, `writing.pcss`, `copy-button.pcss`, `diff.pcss`, `main.pcss`, `header.twig`, `index.twig`, `local.ts`, `pages.ts`, `database.ts`, `package.json` |
| 64 | +- **Project docs:** `README.md` (feature list), `DEVELOPMENT.md` (prerequisites, dark mode, testing, nedb migration) |
| 65 | + |
| 66 | +### Documentation |
| 67 | + |
| 68 | +Full implementation documentation is in [`.github/specs/dark-mode/documentation/`](.github/specs/dark-mode/documentation/progress-report.md): |
| 69 | + |
| 70 | +- **Phase 1.3** |
| 71 | +App initialization [Quick Reference](https://github.com/Hunta/codex.docs/blob/3deaad7a0eff76bef4bbb01e21a2d54f2493aa7b/.github/specs/dark-mode/documentation/1.3-app-initialization/QuickReference.md): FOUC prevention, synchronous init, DOM timing |
| 72 | +- **Phase 2.4–2.8** |
| 73 | +Component style migration docs for: |
| 74 | +Page [Quick Reference](https://github.com/Hunta/codex.docs/blob/3deaad7a0eff76bef4bbb01e21a2d54f2493aa7b/.github/specs/dark-mode/documentation/2.4-page-component-styles/QuickReference.md) |
| 75 | +Sidebar [Quick reference](https://github.com/Hunta/codex.docs/blob/3deaad7a0eff76bef4bbb01e21a2d54f2493aa7b/.github/specs/dark-mode/documentation/2.5-sidebar-component-styles/QuickReference.md) |
| 76 | +Button [Quick Reference](https://github.com/Hunta/codex.docs/blob/3deaad7a0eff76bef4bbb01e21a2d54f2493aa7b/.github/specs/dark-mode/documentation/2.6-button-component-styles/QuickReference.md) |
| 77 | +Input / Form [Quick Reference](https://github.com/Hunta/codex.docs/blob/3deaad7a0eff76bef4bbb01e21a2d54f2493aa7b/.github/specs/dark-mode/documentation/2.7-input-form-component-styles/QuickReference.md) |
| 78 | +Remaining [Quick Reference](https://github.com/Hunta/codex.docs/blob/3deaad7a0eff76bef4bbb01e21a2d54f2493aa7b/.github/specs/dark-mode/documentation/2.8-remaining-component-styles/QuickReference.md) |
| 79 | +- **Phase 3** |
| 80 | +Testing [Quick Reference](https://github.com/Hunta/codex.docs/blob/3deaad7a0eff76bef4bbb01e21a2d54f2493aa7b/.github/specs/dark-mode/documentation/3-testing/QuickReference.md): test architecture, WCAG methodology, cross-browser strategy, root cause analyses |
| 81 | + |
| 82 | +### How to Test |
| 83 | + |
| 84 | +```bash |
| 85 | +# Start dev server |
| 86 | +yarn dev |
| 87 | + |
| 88 | +# Run all e2e tests (auto-starts server) |
| 89 | +yarn test:e2e |
| 90 | + |
| 91 | +# Run unit tests |
| 92 | +yarn test |
| 93 | + |
| 94 | +# Interactive Playwright UI |
| 95 | +yarn test:e2e:ui |
| 96 | +``` |
| 97 | + |
| 98 | +Toggle the sun/moon button in the header to switch themes. Preference persists across reloads and pages. Remove localStorage key `codex-docs-theme` to reset to system default. |
0 commit comments