Closed Bug 2069383 Opened 1 month ago Closed 25 days ago

New Tab does not pick up High Contrast Mode on Linux and macOS

Categories

(Firefox :: New Tab Page, defect, P2)

defect

Tracking

()

VERIFIED FIXED
158 Branch
Accessibility Severity s2
Tracking Status
firefox-esr115 --- unaffected
firefox-esr140 --- unaffected
firefox-esr153 --- unaffected
firefox156 --- unaffected
firefox157 --- wontfix
firefox158 --- verified

People

(Reporter: dao, Assigned: dao)

References

Details

(Keywords: access, Whiteboard: [sng][mcab-hnt])

Attachments

(2 files)

Turning on the OS high-contrast setting leaves New Tab looking as it does normally while the surrounding chrome switches to high-contrast colors. Reported against the New Tab search bar, but it applies to New Tab's other surfaces too.

browser/extensions/newtab/content-src/styles/nova/_tokens.scss declares :root.nova-tokens { … } outside any cascade layer, so it outranks every @layer in tokens-shared.css and tokens-brand.css, tokens-prefers-contrast included. It carries a @media (forced-colors) counterpart but no @media (prefers-contrast) one.

The platform split follows from where each signal lands. prefers-contrast tracks the OS setting in both the chrome and the content realm everywhere. forced-colors never applies to a chrome document, and in content it follows browser.display.document_color_use, which defaults to 0 (mirror the OS setting) on Windows and 1 (off) on Linux and macOS. So on Windows the shim's forced-colors block covers most of the gap; on Linux and macOS prefers-contrast is the only signal reaching the page, and the shim eats it.

Measured on about:newtab with ui.useAccessibilityTheme=1, by removing the nova-tokens class from the page root and diffing the 47 tokens the toolkit sheets override under prefers-contrast:

  • Linux and macOS default (browser.display.document_color_use=1, no forced colors): 31 of the 47 resolve to the wrong value. A further 9 differ only in hex case and are the same color.
  • Windows default with OS HCM (document_color_use=0, forced colors active as well): 14 resolve to the wrong value, the rest being covered by the shim's forced-colors block or by Gecko's own forced-color adjustment.

A representative sample, New Tab against the address bar, under prefers-contrast:

token                             New Tab                        address bar
--border-color                    light-dark(#B7B6BA, #949297)   CanvasText
--text-color                      light-dark(#180E30, #F2F0F8)   CanvasText
--icon-color                      light-dark(#180E30, #F2F0F8)   CanvasText
--background-color-canvas         light-dark(#F7F6FB, #131215)   Canvas
--toolbar-field-background-color  light-dark(#ffffff, #252428)   Field
--toolbar-field-border-color      light-dark(#D6D5DA, #3F3E42)   CanvasText
--toolbar-field-text-color        light-dark(#3E315F, #E7E1F8)   FieldText

Under forced colors the wrong ones include --panel-background-color (Canvas where the shared sheet says Menu), --panel-text-color (a literal where it should be MenuText), --toolbarbutton-outline-color-selected (ButtonText for SelectedItem), the five --icon-color* tokens and --text-color.

The file is generated by mach buildtokens from the *.nova.tokens.json inputs, so the fix belongs in the generator: emit each block inside the cascade layer that matches it. Either way it changes how every New Tab surface renders under high contrast, not just the search bar.

Confirmed the structure described in comment 0, and it localizes one level below the SCSS: the defect is in the mach buildtokens generator, not the generated file.

Root cause. browser/extensions/newtab/content-src/styles/nova/_tokens.scss is emitted by the newtab formatter, which has two gaps the desktop formatter does not:

  1. No prefersContrast bucket at all. get-newtab-tokens-by-layer.mjs builds only { foundation, forcedColors }, and get-newtab-token-css.mjs emits a base block plus a @media (forced-colors) one, both unlayered. The desktop path in get-token-css.mjs emits @layer tokens-prefers-contrast { @media (prefers-contrast) { … } } — the pattern the design token docs specify. about:newtab does load the toolkit sheets (prerendered/activity-stream.html links tokens-brand.css, which @imports tokens-shared.css), so the 43-token tokens-prefers-contrast layer is present in the document — the unlayered :root.nova-tokens block simply outranks it, shadowing 29 of those tokens.

  2. Base accessibility values are dropped when a nova override exists. In the same file, originalValue is replaced by the nova value object, so originalValue.forcedColors is undefined and nothing falls back to the base token. panel.tokens.json declares "forcedColors": "Menu" while panel.nova.tokens.json declares only nativeTheme/default — that is the --panel-background-color case, and the other three clobbered under forced-colors. The desktop formatter survives this because its output is layered so the base layers still apply; the shim, unlayered and self-contained, loses them outright.

Proposed fix — generator only, then regenerate with mach buildtokens:

  • get-newtab-tokens-by-layer.mjs: add a prefersContrast bucket mirroring forcedColors, and fall back to the base token.original.value's prefersContrast/forcedColors when the nova override object does not define one.
  • get-newtab-token-css.mjs: emit @media (prefers-contrast) { :root.nova-tokens { … } } ahead of the forced-colors block, and wrap all three blocks in the tokens-foundation-nova / tokens-prefers-contrast-nova / tokens-forced-colors-nova layers already ordered in tokens-shared.css, so the shim can no longer outrank the toolkit's accessibility layers. Worth confirming the layer names resolve as intended given activity-stream.css loads after tokens-brand.css.

Both halves are needed: the 3 tokens-prefers-contrast-nova overrides sit behind @media -moz-pref("browser.nova.enabled"), which newtab does not set (it gates on browser.newtabpage.activity-stream.nova.enabled), so a layer wrap alone will not recover those — emitting the block is what covers them. Conversely the layer wrap is what fixes (2).

Verification anchor: tests/try-runner.js rebuilds every token target with the real config. Since this changes how all New Tab surfaces render under HCM, an a11y review of the resulting palette is advisable — see HCMMediaQueries.

This is reasoned from the source; I did not build or run it.


Suggested severity: S3
Defeats a high-contrast accommodation across every New Tab surface on Linux and macOS with no in-product workaround, but the page stays legible under the default Nova palette, and users with a hard HCM requirement generally trigger forced-colors, which the shim already covers apart from four tokens.


If you'd like to provide feedback on this comment, please use the 👍 or 👎 reaction.
If you want to categorize your feedback you can add one of the following tags: ai-triage-wrong-file, ai-triage-wrong-cause, ai-triage-hallucination, ai-triage-out-of-scope, ai-triage-wrong-fix, ai-triage-shallow-fix.

Every declaration in the generated _tokens.scss is re-indented, so the only substantive change there is the new prefers-contrast block.

Assignee: nobody → dao+bmo
Status: NEW → ASSIGNED
Accessibility Severity: --- → s2

The severity field for this bug is set to S3. However, the accessibility severity is higher, .
:dao, could you consider increasing the severity?

For more information, please visit BugBot documentation.

Flags: needinfo?(dao+bmo)
Flags: needinfo?(dao+bmo)
Pushed by dgottwald@mozilla.com: https://github.com/mozilla-firefox/firefox/commit/c239782d2cba https://hg.mozilla.org/integration/autoland/rev/a0073190ff82 Put the generated newtab Nova tokens in cascade layers and add the missing prefers-contrast overrides. r=reemhamz https://github.com/mozilla-firefox/firefox/commit/e3e964bc3939 https://hg.mozilla.org/integration/autoland/rev/4b7346c10a47 Name the real pref in the generated newtab tokens header. r=reemhamz

Not a blocker but nice to fix for the experiment in 157.

Status: ASSIGNED → RESOLVED
Closed: 25 days ago
Resolution: --- → FIXED
Target Milestone: --- → 158 Branch

The patch landed in nightly and beta is affected.
:dao, is this bug important enough to require an uplift?

For more information, please visit BugBot documentation.

Flags: needinfo?(dao+bmo)
Flags: needinfo?(dao+bmo)

Reproducible on a 2026-09-04 Firefox Nightly build on Ubuntu 22.

Verified as fixed on Firefox Nightly 158.0a1 on Ubuntu 22 and macOS 13.

Status: RESOLVED → VERIFIED
QA Whiteboard: [qa-ver-done-c158/b157]
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: