Skip to content

feat: per-user dark mode - #335

Open
duncanmcqueen wants to merge 1 commit into
Open-Legal-Products:mainfrom
duncanmcqueen:dark-mode
Open

feat: per-user dark mode#335
duncanmcqueen wants to merge 1 commit into
Open-Legal-Products:mainfrom
duncanmcqueen:dark-mode

Conversation

@duncanmcqueen

@duncanmcqueen duncanmcqueen commented Aug 13, 2026

Copy link
Copy Markdown

Summary

Adds an opt-in dark mode. Users enable it under Settings > Appearance > Dark Mode; the preference persists on the user profile and applies app-wide on subsequent loads.

This branch has been rebased onto current upstream main and the toggle is integrated with the new /settings page and SettingsSection / SettingsToggle components.

How it works

  • Storage: new dark_mode boolean not null default false column on user_profiles, following the existing per-column preference pattern. schema.sql and 20260813_02_user_dark_mode.sql keep fresh installs and upgraded deployments aligned.
  • API: PATCH /user/profile accepts boolean darkMode; serialized profiles expose it. Older databases without the column fall back to light mode until the migration is applied.
  • Frontend: UserProfileContext toggles the .dark class and color-scheme on the document root. Toggle changes apply immediately while persistence is in flight; failures restore the previous theme.
  • Styling: the .dark palette covers semantic app tokens plus legacy hardcoded Tailwind neutral surfaces (bg-white/*, gray text/borders/divides), avoiding page-by-page forks.

Tests

  • Backend route tests cover persistence/serialization and non-boolean rejection.
  • theme.test.ts covers applying light and dark root state.
  • UserProfileContext.test.tsx covers dark -> light -> dark switching and rollback after a failed save.

Validation completed:

  • backend full suite: 604 passed, 24 skipped
  • backend build/typecheck
  • frontend dark-mode tests: 4 passed
  • frontend typecheck
  • frontend production webpack build

The local full frontend suite has four pre-existing Node 26/jsdom Blob.text() failures in untouched mikeApi.test.ts; upstream CI uses its normal fresh Node 22 environment.

Deliberate behavior

  • Explicit toggle only; no prefers-color-scheme detection.
  • A returning dark-mode user may briefly see the light palette before the authenticated profile loads.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Adds an appearance preference (Account > Appearance > Dark Mode) that
persists on the user profile and applies a dark palette app-wide.

- user_profiles gains a dark_mode boolean (default false); schema.sql
  and a dated migration keep fresh and upgraded installs in agreement.
  Profile reads tolerate databases missing the column via the existing
  missing-column fallback, defaulting to light mode.
- PATCH /user/profile accepts darkMode (boolean-validated) and the
  serialized profile exposes it to the client.
- The web client applies the preference by toggling the .dark class and
  color-scheme on the document root whenever the profile loads or the
  toggle changes.
- The .dark palette adds dark values for the app surface tokens, plus
  override mappings so surfaces built with Tailwind's hardcoded neutral
  palette (bg-white/*, gray text/borders/divides) participate in the
  dark theme without page-by-page forks.
@willchen96

Copy link
Copy Markdown
Collaborator

@duncanmcqueen @dwmcqueen Hi Duncan it seems like you have two Github accounts - you need to sign the CLA using both thanks!

@duncanmcqueen

Copy link
Copy Markdown
Author

Ahh! I lost access to the other account. Let me see if Github can fix that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants