Skip to content

The Alert warning variant and hand-written amber-600/emerald-600 success/warning text are under WCAG 1.4.3 contrast because there is no themed --success/--warning token #1243

Description

@rawdaymx

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

  1. 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",
    
  2. 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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions