feat(#64): dark mode — semantic tokens, ThemeToggle, and UI fixes #65

Merged
marcel merged 11 commits from feat/64-dark-mode into main 2026-03-25 13:50:17 +01:00
Owner

Summary

  • Semantic token system: Replaced all hardcoded colors (text-gray-*, bg-gray-*, border-gray-*, etc.) with semantic Tailwind tokens (text-ink, bg-surface, border-line, etc.) across all components. layout.css is now the single source of truth for theming.
  • ThemeToggle: Moon/sun icon button in the nav bar; persists choice to localStorage; no-flash script in app.html prevents light-mode flicker on load.
  • Form controls: Base layer rule ensures input/textarea/select always use bg-surface and text-ink instead of browser-default white backgrounds.
  • Conversation bubbles: Sender bubbles used hardcoded text-white; replaced with text-primary-fg which resolves to navy in dark mode (mint primary background is light).
  • Annotation drawing: Activating annotate mode now forces showAnnotations = true, so drawing works even if the user previously hid annotations. Also restores the missing documentFileHash prop threading through DocumentViewerPdfViewer (lost in rebase).
  • Login page: Fixed white background mismatch (bg-surfacebg-canvas) and removed the py-6 layout padding that made the page scrollable.
  • E2E self-heal: Admin password is reset to its configured value on every backend startup under the e2e profile, preventing test runs from permanently corrupting login credentials.
  • Annotation side panel: Annotation comment threads open in a 320 px slide-in panel at the right edge of the PDF viewer instead of the bottom drawer.
  • Document detail layout: Bottom panel, annotation panel, history inline diff, and various UX fixes (panel height persistence, close-on-annotate, viewport lock).

Test plan

  • Toggle dark mode — all pages should render correctly in both light and dark
  • Login page: no scrolling, background matches the rest of the site
  • Annotations: hide annotations, then activate annotate mode — drawing should work
  • Conversation page: sender bubbles readable in dark mode (navy text on mint bg)
  • Run e2e suite — admin login should self-heal across test runs
## Summary - **Semantic token system**: Replaced all hardcoded colors (`text-gray-*`, `bg-gray-*`, `border-gray-*`, etc.) with semantic Tailwind tokens (`text-ink`, `bg-surface`, `border-line`, etc.) across all components. `layout.css` is now the single source of truth for theming. - **ThemeToggle**: Moon/sun icon button in the nav bar; persists choice to `localStorage`; no-flash script in `app.html` prevents light-mode flicker on load. - **Form controls**: Base layer rule ensures `input`/`textarea`/`select` always use `bg-surface` and `text-ink` instead of browser-default white backgrounds. - **Conversation bubbles**: Sender bubbles used hardcoded `text-white`; replaced with `text-primary-fg` which resolves to navy in dark mode (mint primary background is light). - **Annotation drawing**: Activating annotate mode now forces `showAnnotations = true`, so drawing works even if the user previously hid annotations. Also restores the missing `documentFileHash` prop threading through `DocumentViewer` → `PdfViewer` (lost in rebase). - **Login page**: Fixed white background mismatch (`bg-surface` → `bg-canvas`) and removed the `py-6` layout padding that made the page scrollable. - **E2E self-heal**: Admin password is reset to its configured value on every backend startup under the `e2e` profile, preventing test runs from permanently corrupting login credentials. - **Annotation side panel**: Annotation comment threads open in a 320 px slide-in panel at the right edge of the PDF viewer instead of the bottom drawer. - **Document detail layout**: Bottom panel, annotation panel, history inline diff, and various UX fixes (panel height persistence, close-on-annotate, viewport lock). ## Test plan - [ ] Toggle dark mode — all pages should render correctly in both light and dark - [ ] Login page: no scrolling, background matches the rest of the site - [ ] Annotations: hide annotations, then activate annotate mode — drawing should work - [ ] Conversation page: sender bubbles readable in dark mode (navy text on mint bg) - [ ] Run e2e suite — admin login should self-heal across test runs
marcel added 11 commits 2026-03-25 13:48:11 +01:00
All color and font definitions live in layout.css via Tailwind 4 @theme.
Keeping only the content glob in the config file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
No logic changes — whitespace and indentation only. These were flagged
by the pre-commit hook when running lint after layout.css was modified.

Refs #64
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Inline <script> in app.html applies saved localStorage theme before first
  paint to prevent flash of wrong theme
- ThemeToggle.svelte: moon/sun button, localStorage persistence, sets
  data-theme on <html>, defaults to system preference on first visit
- Placed in +layout.svelte between language selector and user menu
- E2E tests cover visibility, toggle, reverse toggle, persistence, and
  no-flash behaviour — all 6 passing

Refs #64
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces bg-white, text-brand-navy, border-brand-sand, text-gray-*, bg-[#2A2A2A],
bg-brand-purple/15, hover:bg-brand-sand, etc. across all 35 .svelte files with
semantic token utilities (bg-surface, text-ink, border-line, bg-pdf-bg, bg-nav-active,
bg-muted, text-accent, bg-primary, ...).

Also adds CSS filter: invert(1) in layout.css for De Gruyter <img> icons in dark mode,
excluding icons that carry .invert already (to prevent double-inversion).

Closes #64
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace text-gray-*, bg-gray-*, border-gray-*, divide-gray-*, placeholder-gray-*,
focus:border-blue-*, focus:ring-blue-*, hover:bg-blue-*, and ring-brand-mint with
their semantic-token equivalents (text-ink, bg-muted, border-line, etc.) across
all pages and shared components so dark mode renders correctly everywhere.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Browser-default form controls (input, textarea, select) render with a white
background that ignores CSS custom properties in dark mode. Adding bg-surface
and text-ink to the base layer ensures they theme correctly without touching
every component.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The password-reset E2E test changes the admin password mid-test and relies on a
UI step to restore it. If that step fails or the test is interrupted the account
is left with the wrong password, locking out all subsequent runs.

Fix: in DataInitializer.initE2EData (e2e profile only), always reset the admin
password to the value from ${app.admin.password} (default: admin123) on startup.
This is idempotent — it is safe to run even when the password is already correct.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In dark mode --c-primary is mint (#a1dcd8), a light colour, making hardcoded
white text barely readable. Replacing text-white/text-blue-100 with
text-primary-fg (white in light, navy in dark) restores contrast in both modes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PdfViewer: add $effect that forces showAnnotations=true when annotateMode
  becomes true, so hiding annotations before drawing no longer breaks drawing
- DocumentViewer: restore missing fileHash field on Doc type and pass
  documentFileHash to PdfViewer (lost when rebase dropped the merge commit)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The login page used bg-surface (white) as its outer background.
The global layout already has bg-canvas (sand), so using bg-surface
created a visible white layer with a mismatched color.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix(login): remove py-6 padding from layout on auth pages to prevent scrolling
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 2m25s
CI / Backend Unit Tests (pull_request) Successful in 2m10s
CI / E2E Tests (pull_request) Failing after 29m35s
CI / Unit & Component Tests (push) Successful in 2m27s
CI / Backend Unit Tests (push) Successful in 2m17s
CI / E2E Tests (push) Failing after 30m42s
29a71f4421
The global layout wrapped all pages in <main class="py-6">, adding 48px
of vertical padding. Combined with min-h-screen on the login page div,
the total height exceeded 100vh and made the page scrollable.

Auth pages (/login, /forgot-password, /reset-password) now get no
padding from the layout — the same path check already used to hide
the nav header.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
marcel force-pushed feat/64-dark-mode from 0142ad9639 to 29a71f4421 2026-03-25 13:48:11 +01:00 Compare
marcel merged commit 29a71f4421 into main 2026-03-25 13:50:17 +01:00
marcel deleted branch feat/64-dark-mode 2026-03-25 13:50:19 +01:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#65