feat(pad): add theme-color meta to match toolbar on mobile (#7606) (#7636)

* feat(pad): add <meta name="theme-color"> matching toolbar (#7606)

Mobile browsers paint the address-bar / status-bar area above the
viewport. Without theme-color this is a system color that does not
match the Etherpad toolbar, leaving a visible gap above the pad.

Render <meta name="theme-color"> server-side so the bar matches the
configured toolbar on first paint. Light + dark variants are emitted
with prefers-color-scheme media queries when dark mode is enabled.
Colors are derived from settings.skinVariants via a new SkinColors
helper (mirrors --bg-color in the colibris pad-variants.css).

Closes #7606

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(timeslider): emit single theme-color matching configured toolbar

Qodo flagged a mismatch: timeslider does not switch skin variants on
prefers-color-scheme, so emitting a dark theme-color via media query
would leave dark-mode devices with a dark address bar over a light
toolbar. Drop the media-query metas on timeslider and emit one
unconditional theme-color resolved from settings.skinVariants.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(pad): emit unconditional theme-color so dark-OS users still match

Qodo flagged that gating the light theme-color on
prefers-color-scheme: light leaves no applicable meta on dark-OS
devices when enableDarkMode is false — the address bar then uses a
system color while the toolbar stays light.

Drop the light media query so the light theme-color is the baseline,
and let the prefers-color-scheme: dark meta override it when dark
mode is enabled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(theme-color): align dark meta with client-side super-dark override

Two related Qodo findings on the SkinColors helper:

- The pad client's dark-mode auto-switch (pad.ts L650) forces
  super-dark-toolbar regardless of the configured skinVariants, so
  the prefers-color-scheme: dark meta must always be #485365 — not
  whichever dark variant the operator configured.
- When skinVariants only carries a dark token (e.g. dark-toolbar),
  the previous helper left the baseline meta at #ffffff, so light-OS
  users would see white above a dark toolbar.

Replace toolbarThemeColors() with configuredToolbarColor() (used as
the unconditional baseline) and a fixed DARK_MODE_TOOLBAR_COLOR
constant (used in the prefers-color-scheme: dark meta).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(theme-color): server-side only, drop fragile dark media query

Address remaining Qodo findings on the theme-color rollout:

- (#1) Skip emitting the meta entirely when settings.skinName is not
  colibris — the helper only knows colibris's --bg-color values, so
  on no-skin or third-party skins the previous code would emit a
  white meta over a non-white toolbar.
- (#4) Drop the prefers-color-scheme: dark variant. The pad's
  client-side dark mode is also gated on a localStorage white-mode
  override that no media query can express, so the dark meta could
  paint a dark address bar over a still-light toolbar. The single
  baseline meta always matches what the user sees on first paint.
- (#8) Remove the redundant module.exports assignment; rely on the
  ES named export only (tsx handles the require() interop).
- (#9) Iterate the toolbar variants in CSS source order and let the
  last match win, matching the cascade in pad-variants.css when
  multiple *-toolbar tokens are present.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
John McLear 2026-05-01 17:25:24 +08:00 committed by GitHub
parent 4704d80e82
commit 63cae17720
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 134 additions and 0 deletions

View file

@ -0,0 +1,34 @@
'use strict';
// Toolbar background colors that the colibris skin variants resolve to.
// Mirrors --bg-color in src/static/skins/colibris/src/pad-variants.css. Only
// the colibris skin has a known mapping; for any other skin we cannot derive
// the toolbar color server-side and emit no theme-color meta.
//
// Order matters: when skinVariants contains multiple *-toolbar tokens the
// CSS cascade picks the rule defined last in pad-variants.css, so iterate in
// source order and let the last matching token win.
const TOOLBAR_COLORS_IN_CSS_ORDER: Array<[string, string]> = [
['super-light-toolbar', '#ffffff'],
['light-toolbar', '#f2f3f4'],
['super-dark-toolbar', '#485365'],
['dark-toolbar', '#576273'],
];
const COLIBRIS_DEFAULT_TOOLBAR_COLOR = '#ffffff';
// The toolbar color the user actually sees on first paint, derived from the
// configured skin and skinVariants. Returns null when the skin is unknown so
// callers can omit the meta rather than emit a misleading value.
export const configuredToolbarColor = (
skinName: string | undefined | null,
skinVariants: string | undefined | null,
): string | null => {
if (skinName !== 'colibris') return null;
const tokens = new Set((skinVariants || '').split(/\s+/).filter(Boolean));
let color: string | null = null;
for (const [variant, c] of TOOLBAR_COLORS_IN_CSS_ORDER) {
if (tokens.has(variant)) color = c;
}
return color || COLIBRIS_DEFAULT_TOOLBAR_COLOR;
};