Summary
packages/ui/src/components/ui/alert.tsx has three variants: default, warning, and
destructive. destructive uses the themed --destructive token (a real CSS custom property,
contrast-checked — modulo draft 03's missing -foreground). warning instead hardcodes a raw
Tailwind palette class, text-amber-600 (line 13), because there is no --warning token in
default.css to use. Measured against --card (the surface Alert renders on,
alert.tsx:12: bg-card), amber-600 reads 3.19:1 as body text — under the 4.5:1 WCAG 1.4.3
floor for normal text.
This isn't limited to the one component. Because upstream's theme has no --success/--warning
tokens at all, every call site in the app that needs to communicate success or a non-destructive
warning as text reaches for the same family of raw, hardcoded classes. The two most common,
counted directly (apps/**/*.tsx, packages/**/*.tsx):
$ git grep -n -E "text-amber-600|text-emerald-600" upstream/main -- 'apps/*.tsx' 'packages/*.tsx' | wc -l
15
text-emerald-600 (the success equivalent) reads 3.77:1 on --card — also under 4.5:1.
Control: the destructive variant in the same component, on the same surface, at the same font
size, passes (--destructive is a real theme token with a contrast-checked value against
--card) — so this isn't Alert failing generally, it's specifically the two variants that
don't have a themed color to draw from.
Environment
|
|
| Defect type |
Static (code) |
| Commit |
975c7a02a963d80bf140bb269d773b4912b8e7b6 (upstream/main) |
| How measured |
git fetch upstream && git rev-parse upstream/main, then reading packages/ui/src/components/ui/alert.tsx and packages/ui/src/styles/default.css at that commit, and running the WCAG 2.x contrast math against the literal hex/oklch values those Tailwind classes resolve to |
How to see it
packages/ui/src/components/ui/alert.tsx, lines 12-13:
warning:
"text-amber-600 dark:text-amber-500 bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-amber-600/90 dark:*:data-[slot=alert-description]:text-amber-500/90",
- Compute the WCAG 2.x contrast of Tailwind's
amber-600 (oklch(0.666 0.179 58.318)) and
emerald-600 (oklch(0.596 0.145 163.225)) against --card in light mode
(oklch(1 0 0) — plain white).
Evidence
$ pnpm exec tsx medir-amber700-detalle.ts
amber-600/emerald-600 (actual) [
'estado-texto-success=3.77',
'estado-texto-warning=3.19',
'estado-icono-warning=2.80'
]
(estado-icono-warning is the same amber-600 value used as an icon color, e.g. a
TriangleAlertIcon, checked against the 3:1 non-text floor — it also fails, at 2.80.)
Usage count for the two hand-written text classes:
$ git grep -n -E "text-amber-600|text-emerald-600" upstream/main -- 'apps/*.tsx' 'packages/*.tsx' | wc -l
15
(Witness: git grep -l -E "text-amber-600|text-emerald-600" upstream/main -- 'apps/*.tsx' 'packages/*.tsx' lists real files, e.g.
apps/builder/src/features/ads/components/ads-analytics-view.tsx.)
Control (the destructive variant of the same component passes):
--destructive (oklch(0.577 0.245 27.325) light) against --card is not in the failing-rows
list produced by the same script run against unmodified default.css — only the success/warning
text and icon criteria are.
Where it comes from
packages/ui/src/components/ui/alert.tsx lines 12-13 (the warning variant); the equivalent
hardcoded text-emerald-600 usages are scattered across the 15 call sites counted above, none of
which import a --success/--warning custom property because default.css doesn't define one.
Why we think this is a defect rather than the intended design
This is a proposal grounded in a measured contrast defect, not a feature request for new
capability the app doesn't already need — the app already communicates success and warning
states as colored text in at least 15 places; those two colors both fail the same WCAG 1.4.3
floor the destructive variant already meets. We are not asking upstream to invent a new visual
language, only to make the two states it already has as contrast-safe as the third one
(destructive) it already ships correctly.
Suggested fix
Add --success/--warning tokens (with their -foreground counterparts, mirroring how
--destructive/--destructive-foreground — modulo draft 03 — are structured) to :root/.dark
and register them in @theme inline, then point alert.tsx's warning variant (and, over time,
the other 14 call sites) at the token instead of the raw class:
/* :root */
--success: oklch(0.527 0.154 150.069); /* Tailwind green-700 */
--warning: oklch(0.555 0.163 48.998); /* Tailwind amber-700 */
Verified with the same script, substituting these values for the current hand-written
amber-600/emerald-600 pair:
$ pnpm exec tsx medir-amber700-detalle.ts
amber-700/green-700 (candidato) [
'estado-texto-success=5.00',
'estado-texto-warning=5.04',
'estado-icono-warning=4.27'
]
All three clear their respective floors (4.5:1 for text, 3:1 for the icon) with margin. We are
not sending a PR for this one: introducing new theme tokens and threading them through the
Alert variant map (and, ideally, the other 14 call sites) is a bigger, more opinionated change
than a two-line value swap, and it's the kind of API surface addition a maintainer should weigh
in on before code is written for it.
What we did not verify
- Dark-mode values for
--success/--warning beyond the existing hand-written
emerald-400/amber-400 pair, which already pass in dark mode today (not in the failing-rows
list) — we did not re-derive dark-mode-specific replacements since the light-mode ones are the
ones that fail.
- The other 13 call sites beyond
alert.tsx and the two files sampled in Evidence — we counted
them, we did not open and re-verify contrast for each individually.
- APCA / WCAG 3 numbers — only WCAG 2.x is measured here.
Related issues — none of these describes it
Searched gh search issues --repo ChatbotXIO/ChatbotX "<term>" for alert warning contrast and
warning text contrast (no --state filter); both returned zero results. Witness that the
mechanism works: the same command with "bug" returns real issues (e.g. #1226, #1228).
Summary
packages/ui/src/components/ui/alert.tsxhas three variants:default,warning, anddestructive.destructiveuses the themed--destructivetoken (a real CSS custom property,contrast-checked — modulo draft 03's missing
-foreground).warninginstead hardcodes a rawTailwind palette class,
text-amber-600(line 13), because there is no--warningtoken indefault.cssto use. Measured against--card(the surfaceAlertrenders on,alert.tsx:12:bg-card),amber-600reads 3.19:1 as body text — under the 4.5:1 WCAG 1.4.3floor for normal text.
This isn't limited to the one component. Because upstream's theme has no
--success/--warningtokens at all, every call site in the app that needs to communicate success or a non-destructive
warning as text reaches for the same family of raw, hardcoded classes. The two most common,
counted directly (
apps/**/*.tsx,packages/**/*.tsx):text-emerald-600(the success equivalent) reads 3.77:1 on--card— also under 4.5:1.Control: the
destructivevariant in the same component, on the same surface, at the same fontsize, passes (
--destructiveis a real theme token with a contrast-checked value against--card) — so this isn'tAlertfailing generally, it's specifically the two variants thatdon't have a themed color to draw from.
Environment
975c7a02a963d80bf140bb269d773b4912b8e7b6(upstream/main)git fetch upstream && git rev-parse upstream/main, then readingpackages/ui/src/components/ui/alert.tsxandpackages/ui/src/styles/default.cssat that commit, and running the WCAG 2.x contrast math against the literal hex/oklch values those Tailwind classes resolve toHow to see it
packages/ui/src/components/ui/alert.tsx, lines 12-13:amber-600(oklch(0.666 0.179 58.318)) andemerald-600(oklch(0.596 0.145 163.225)) against--cardin light mode(
oklch(1 0 0)— plain white).Evidence
(
estado-icono-warningis the sameamber-600value used as an icon color, e.g. aTriangleAlertIcon, checked against the 3:1 non-text floor — it also fails, at 2.80.)Usage count for the two hand-written text classes:
(Witness:
git grep -l -E "text-amber-600|text-emerald-600" upstream/main -- 'apps/*.tsx' 'packages/*.tsx'lists real files, e.g.apps/builder/src/features/ads/components/ads-analytics-view.tsx.)Control (the
destructivevariant of the same component passes):--destructive(oklch(0.577 0.245 27.325)light) against--cardis not in the failing-rowslist produced by the same script run against unmodified
default.css— only the success/warningtext and icon criteria are.
Where it comes from
packages/ui/src/components/ui/alert.tsxlines 12-13 (thewarningvariant); the equivalenthardcoded
text-emerald-600usages are scattered across the 15 call sites counted above, none ofwhich import a
--success/--warningcustom property becausedefault.cssdoesn't define one.Why we think this is a defect rather than the intended design
This is a proposal grounded in a measured contrast defect, not a feature request for new
capability the app doesn't already need — the app already communicates success and warning
states as colored text in at least 15 places; those two colors both fail the same WCAG 1.4.3
floor the
destructivevariant already meets. We are not asking upstream to invent a new visuallanguage, only to make the two states it already has as contrast-safe as the third one
(
destructive) it already ships correctly.Suggested fix
Add
--success/--warningtokens (with their-foregroundcounterparts, mirroring how--destructive/--destructive-foreground— modulo draft 03 — are structured) to:root/.darkand register them in
@theme inline, then pointalert.tsx'swarningvariant (and, over time,the other 14 call sites) at the token instead of the raw class:
Verified with the same script, substituting these values for the current hand-written
amber-600/emerald-600 pair:
All three clear their respective floors (4.5:1 for text, 3:1 for the icon) with margin. We are
not sending a PR for this one: introducing new theme tokens and threading them through the
Alertvariant map (and, ideally, the other 14 call sites) is a bigger, more opinionated changethan a two-line value swap, and it's the kind of API surface addition a maintainer should weigh
in on before code is written for it.
What we did not verify
--success/--warningbeyond the existing hand-writtenemerald-400/amber-400pair, which already pass in dark mode today (not in the failing-rowslist) — we did not re-derive dark-mode-specific replacements since the light-mode ones are the
ones that fail.
alert.tsxand the two files sampled in Evidence — we countedthem, we did not open and re-verify contrast for each individually.
Related issues — none of these describes it
Searched
gh search issues --repo ChatbotXIO/ChatbotX "<term>"foralert warning contrastandwarning text contrast(no--statefilter); both returned zero results. Witness that themechanism works: the same command with
"bug"returns real issues (e.g. #1226, #1228).