Skip to content

fix(7606): sync theme-color meta with client-side dark-mode switch#7690

Merged
JohnMcLear merged 2 commits intodevelopfrom
fix-7606-theme-color-client-dark-mode
May 7, 2026
Merged

fix(7606): sync theme-color meta with client-side dark-mode switch#7690
JohnMcLear merged 2 commits intodevelopfrom
fix-7606-theme-color-client-dark-mode

Conversation

@JohnMcLear
Copy link
Copy Markdown
Member

Summary

  • After feat(pad): add theme-color meta to match toolbar on mobile (#7606) #7636 shipped, stffen reported on #7606 that 2.7.3 still paints a white address bar above a dark pad.
  • Root cause: the meta is computed server-side from settings.skinVariants, but the toolbar can be flipped to super-dark-toolbar client-side in three places — pad.ts:712 auto-switch (enableDarkMode + prefers-color-scheme: dark + no localStorage white-mode override), the #options-darkmode toggle in pad_editor.ts, and the #skinvariantsbuilder admin tool. All three call skinVariants.updateSkinVariantsClasses(), which until now never touched the meta.
  • Fix: push the lookup into updateSkinVariantsClasses so the meta tracks every class change. Mirrors the CSS-source-order table from src/node/utils/SkinColors.ts so the same "last matching *-toolbar token wins" rule the server uses also applies on the client. No-op when the server didn't emit a meta (non-colibris skins).

Test plan

  • New Playwright spec tests/frontend-new/specs/theme_color_dark_mode.spec.ts covers both paths:
    • colorScheme: 'light' — manual toggle via #options-darkmode flips meta #ffffff#485365#ffffff.
    • colorScheme: 'dark' — auto-switch on dark-OS clients lands on #485365 after init (the case stffen reported).
  • TDD verified: reverted the fix, both tests went red; restored, both green.

🤖 Generated with Claude Code

PR #7636 emits <meta name="theme-color"> server-side from
settings.skinVariants. That covers operators who hard-code a dark
toolbar in settings.json, but not the runtime path: pad.ts auto-flips
the toolbar to super-dark when enableDarkMode is on, the browser
reports prefers-color-scheme: dark, and no localStorage white-mode
override is set, plus the user can flip it via #options-darkmode.
Both paths run skinVariants.updateSkinVariantsClasses(), which until
now never touched the meta — so dark-mode users kept the light
#ffffff baseline and saw a white address bar above a dark toolbar
(stffen on #7606 after 2.7.3).

Push the toolbar-color lookup into updateSkinVariantsClasses so the
meta tracks every class change: the auto-switch on init, the user
toggle, and the skinVariants builder. Mirrors the CSS-source-order
table from src/node/utils/SkinColors.ts (last matching *-toolbar
token wins). When no meta is present (non-colibris skin, server
omits it) the helper is a no-op.

Adds Playwright coverage for both paths under
colorScheme: 'light' (manual toggle) and 'dark' (auto-switch on
dark-OS clients — the case stffen reported).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@qodo-code-review
Copy link
Copy Markdown

ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one.

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Sync theme-color meta with client-side dark-mode toolbar changes

🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Sync <meta name="theme-color"> with client-side dark-mode toolbar changes
• Add updateThemeColorMeta() function to track toolbar color class updates
• Mirror CSS source-order logic from SkinColors.ts for consistent color resolution
• Add Playwright tests covering manual toggle and auto-switch scenarios
Diagram
flowchart LR
  A["Dark-mode toggle<br/>or auto-switch"] -->|calls| B["updateSkinVariantsClasses()"]
  B -->|now calls| C["updateThemeColorMeta()"]
  C -->|updates| D["meta[name=theme-color]"]
  D -->|reflects| E["Toolbar color<br/>in address bar"]
Loading

Grey Divider

File Changes

1. src/static/js/skin_variants.ts 🐞 Bug fix +30/-0

Add theme-color meta sync to toolbar updates

• Added TOOLBAR_COLORS_IN_CSS_ORDER constant mapping toolbar variants to hex colors in CSS source
 order
• Implemented updateThemeColorMeta() function to sync <meta name="theme-color"> with active
 toolbar classes
• Integrated updateThemeColorMeta() call into updateSkinVariantsClasses() to track all toolbar
 changes
• Handles both auto-switch on init and user-triggered dark-mode toggles

src/static/js/skin_variants.ts


2. src/tests/frontend-new/specs/theme_color_dark_mode.spec.ts 🧪 Tests +42/-0

Add theme-color meta dark-mode toggle tests

• New Playwright test spec with two test suites covering light and dark color schemes
• Light scheme test verifies manual dark-mode toggle updates meta from #ffffff to #485365 and
 back
• Dark scheme test verifies auto-switch on dark-OS clients lands on #485365 after init
• Uses colorScheme browser context option to simulate different OS preferences

src/tests/frontend-new/specs/theme_color_dark_mode.spec.ts


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented May 7, 2026

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. 4-space indentation in test ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
New test file uses 4+ space continuation indentation (not 2 spaces), which violates the repository
coding style requirement. This can cause formatting inconsistency and potential lint failures.
Code

src/tests/frontend-new/specs/theme_color_dark_mode.spec.ts[R4-5]

+const themeColor = (page: import('@playwright/test').Page) =>
+    page.locator('meta[name="theme-color"]').getAttribute('content');
Evidence
PR Compliance ID 9 requires 2-space indentation and no tabs. The added test file introduces
continuation lines indented with 4+ spaces (e.g., the themeColor helper and the multiline
test(...) call).

src/tests/frontend-new/specs/theme_color_dark_mode.spec.ts[4-5]
src/tests/frontend-new/specs/theme_color_dark_mode.spec.ts[33-34]
Best Practice: Repository guidelines

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new Playwright spec uses 4+ space continuation indentation, but the codebase requires 2-space indentation.
## Issue Context
The `themeColor` helper is split across lines with 4 spaces, and the multiline `test(...)` call uses 6 spaces before `async`.
## Fix Focus Areas
- src/tests/frontend-new/specs/theme_color_dark_mode.spec.ts[4-5]
- src/tests/frontend-new/specs/theme_color_dark_mode.spec.ts[33-34]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Duplicated color mapping ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
The toolbar variant → hex color table is duplicated in both client code (skin_variants.ts) and
server code (SkinColors.ts), so future updates can easily change one side and silently desync the
server-rendered baseline theme-color from client-side updates. This can regress the exact “wrong
address bar color” behavior this PR is fixing.
Code

src/static/js/skin_variants.ts[R7-15]

+// Mirrors src/node/utils/SkinColors.ts: toolbar variants in CSS source order
+// from src/static/skins/colibris/src/pad-variants.css. The last matching token
+// wins, so iterate in source order.
+const TOOLBAR_COLORS_IN_CSS_ORDER: Array<[string, string]> = [
+  ['super-light-toolbar', '#ffffff'],
+  ['light-toolbar', '#f2f3f4'],
+  ['super-dark-toolbar', '#485365'],
+  ['dark-toolbar', '#576273'],
+];
Evidence
The PR adds a client-side TOOLBAR_COLORS_IN_CSS_ORDER table and uses it to compute
meta[name="theme-color"]. The server already has its own copy of the same table to compute the
initial meta value; keeping both in sync requires manual coordinated edits.

src/static/js/skin_variants.ts[7-33]
src/node/utils/SkinColors.ts[3-16]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The toolbar variant → theme color mapping is duplicated in both server and client code. This duplication can drift over time (CSS changes / palette changes), causing the server-rendered theme-color meta to disagree with the client’s dynamic updates.
## Issue Context
- Server baseline meta is computed in `configuredToolbarColor()`.
- Client updates are computed in `updateThemeColorMeta()`.
- Both hard-code the same table.
## Fix Focus Areas
- src/static/js/skin_variants.ts[7-33]
- src/node/utils/SkinColors.ts[3-34]
## Suggested approach
Create a small shared module (usable by both Node and browser builds) that exports the ordered mapping and default color, and import it from both places. If cross-bundle imports aren’t feasible, add a single-source generation step or a dedicated test that asserts the two tables are identical to prevent accidental drift.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread src/tests/frontend-new/specs/theme_color_dark_mode.spec.ts Outdated
(1) Bug — duplicated toolbar→color table: extract the CSS-source-order
mapping and the default-color constant into src/static/js/skin_toolbar_colors,
re-imported by both src/node/utils/SkinColors.ts (server, EJS template
helper) and src/static/js/skin_variants.ts (client, runtime updates). Lives
under static/js so the browser bundle can resolve it; server-side imports
of static/js modules already exist (Changeset, AttributeMap, ImportHtml,
hooks). One source of truth means a future palette change can no longer
silently desync the server-rendered baseline meta from the client updates,
which was the exact regression that brought us here.

(2) Rule violation — 4-space continuation indentation in the new
Playwright spec: re-indent the themeColor helper and the multiline
test(...) call to the repo's 2-space rule (.editorconfig).

Existing backend coverage (configuredToolbarColor unit tests + the
specialpages server-render checks for both pad and timeslider) still
passes against the refactored helper, so it's regression-locked end to
end.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JohnMcLear
Copy link
Copy Markdown
Member Author

Actioned Qodo's two findings in 1b724c5:

  1. Duplicated table (bug, drift hazard) — extracted the CSS-source-order toolbar→color map and the default-color constant into src/static/js/skin_toolbar_colors.ts. Both src/node/utils/SkinColors.ts (server/EJS helper) and src/static/js/skin_variants.ts (client runtime) now import from it. Server-side imports of static/js/ modules are already established (Changeset, AttributeMap, ImportHtml, hooks), so no new boundary. Existing configuredToolbarColor unit tests + the specialpages server-render checks all still pass against the refactored helper, locking the abstraction.
  2. 2-space indent (rule violation) — re-indented the themeColor helper and the multiline test(...) call in the Playwright spec to match .editorconfig's indent_size = 2.

@JohnMcLear JohnMcLear merged commit ab0cff4 into develop May 7, 2026
43 checks passed
@JohnMcLear JohnMcLear deleted the fix-7606-theme-color-client-dark-mode branch May 7, 2026 09:43
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.

1 participant