Repository navigation
fix(theme): raise text contrast to 4.5:1 across the console - #6146
Merged
norman-abramovitz merged 7 commits intoNov 7, 2026
Merged
norman-abramovitz merged 7 commits into
norman-abramovitz merged 7 commits into
Conversation
Placeholder text in inputs, textareas and the dropdown trigger was a 71 % gray (#9ca3af) on white, 2.5:1, and 55 % gray on the dark input background, 3.1:1; WCAG 1.4.3 needs 4.5:1. The token now uses the theme's muted text color: #64748b in light mode (4.8:1) and #94a3b8 in dark mode (5.7:1). The branding service re-sets the token at runtime, so its values change too, and the docs table follows. The .form-control placeholder used the muted color at 70 % opacity, which undid the muted color's contrast; it now uses the same token at full opacity. The stepper's skipped-step label was the same 71 % gray and now uses the inactive-step text color. AI disclosure (RFC-0047): prepared with AI coding assistance; reviewed by the author.
The default light-mode primary (#3b82f6) measured 3.7:1 on white, and the accent (#60a5fa) that colors every table link measured 2.5:1; WCAG 1.4.3 needs 4.5:1 for text. Both, with secondary and info, are now #2563eb: 5.2:1 on white, 5.0:1 on card headers, 4.6:1 on the active tab tint, and 5.2:1 under white button text. Operators who configure their own brand colors are unaffected. The dark-mode primary in theme.config.ts said #3b82f6 (4.0:1 on the dark content background) while the stylesheet already used #60a5fa (5.8:1); the config now matches the stylesheet. Each default lives in the Tailwind theme, the SCSS dark block, theme.config.ts, the branding service fallback and the sample config files, and the docs table lists them; all now agree. AI disclosure (RFC-0047): prepared with AI coding assistance; reviewed by the author.
Primary buttons, the selected segment of a button toggle, the active step indicator and the "bg-primary text-white" controls in templates draw white text on the primary color. In dark mode the primary is the lighter #60a5fa so that it reads as link text, and white on it is 2.5:1; WCAG 1.4.3 needs 4.5:1. No single blue passes as both text and a background for white text in dark mode. A new --color-on-primary carries the text color for primary surfaces: white in light mode (5.2:1 on #2563eb) and the dark ink #0f172a in dark mode (7.0:1 on #60a5fa). The button, toggle and stepper styles use it, and the templates use the matching text-on-primary utility. AI disclosure (RFC-0047): prepared with AI coding assistance; reviewed by the author.
The text-success, text-warning, text-danger and text-info utilities used the brand status colors, which are tuned for badges and buttons: as text on white they measure 2.3:1 (green, the running-app state), 2.2:1 (amber) and 3.8:1 (red); WCAG 1.4.3 needs 4.5:1. The theme already has per-mode status text tokens (--success-text and friends, 7:1 or better on the content background in both modes), so the four text utilities now use them. Backgrounds and borders keep the brand values. The About page's Admin badge and row used the orange 700 shade on an orange tint (2.1:1 and 2.5:1); they now use the warning background and text tokens (6.4:1 light, 6.3:1 dark). AI disclosure (RFC-0047): prepared with AI coding assistance; reviewed by the author.
The quota and space quota pages, the apply-quota dialog and the entity summary title drew their labels and subtitles at 60 % opacity, about 3:1 on white; WCAG 1.4.3 needs 4.5:1. They now use the theme's muted text color, as the faded-text fix did elsewhere. The dialog's load error used a raw red-600 and now uses text-danger. AI disclosure (RFC-0047): prepared with AI coding assistance; reviewed by the author.
AI disclosure (RFC-0047): prepared with AI coding assistance; reviewed by the author.
The after-measure on the lab left two cases. The active second-level menu item drew the primary text on a 10 % primary tint, which with the darker default primary measures exactly 4.5:1; the tint is now 6 % (4.8:1 light, 5.2:1 dark). The home page's endpoint filter set white text on the selected segment directly, missing the on-primary token; it now uses text-on-primary (2.5:1 → 7.0:1 in dark mode). AI disclosure (RFC-0047): prepared with AI coding assistance; reviewed by the author.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Towards #4166. Text in the default theme fell short of the 4.5:1 minimum of WCAG 1.4.3 in several places: placeholders, every table link, primary text and buttons, status-colored text, the About admin badge and some dimmed labels. An axe-core
color-contrastsweep of 20 pages in light and dark mode found the sites; the theme tokens explain them.Changes, one commit each
--input-placeholder(stylesheet and the branding service, which re-sets it at runtime);.form-controlplaceholder opacity dropped; stepper skip label#3b82f6/#60a5fa→#2563ebin the Tailwind theme,theme.config.ts, the service fallback, the sample configs and the docs table; dark primary/info intheme.config.ts→#60a5fa, matching the stylesheettext-primary3.7 → 5.2; active tab 3.3 → 4.6; dark primary text 4.0 → 5.8--color-on-primary#0f172ain dark mode; used by.btn-primary, the outline hover, the button toggle, the active step and 12bg-primarytemplate sites (text-on-primary)text-success/warning/danger/infoutilities → the existing per-mode--*-texttokens;bg-*/border-*keep the brand values; About admin badge → warning tokensopacity-60→text-content-mutedtext-white→text-on-primaryOperators who configure brand colors are unaffected: the defaults changed, not the configuration path.
--color-on-primaryis not configurable yet; an operator setting a light primary for dark mode gets dark text on it, which is the common case.Not in this PR
.btn-danger3.8:1) and the stepper's completed-step check mark on green — separate decision on the status palette.Tests
make check gategreen (729 spec files, 5294 tests).color-contrastover 20 pages (home, lists, Create App, Marketplace, services, endpoints, profile, About, an app's summary/routes/variables/events, a space's summary/apps/services/routes/events, org quota, quota definitions):The batch sweep also lists some dark-mode rows at 1.4:1 on freshly loaded pages; they are the light→dark switch still in progress at the 2 s mark (the same page re-checked after 6 s reports none, and the live computed colors are
#f1f5f9on#334155).AI disclosure (RFC-0047): prepared with AI coding assistance; reviewed by the author.