Skip to content

fix(a11y): render a warning icon for Chip warning intent (WCAG 1.4.1)#3450

Merged
bilal-karim merged 1 commit into
mainfrom
a11y/1.4.1-chip-warning-icon
Jun 4, 2026
Merged

fix(a11y): render a warning icon for Chip warning intent (WCAG 1.4.1)#3450
bilal-karim merged 1 commit into
mainfrom
a11y/1.4.1-chip-warning-icon

Conversation

@bilal-karim

@bilal-karim bilal-karim commented May 25, 2026

Copy link
Copy Markdown
Member

Summary

The Chip primitive's warning intent previously applied bg-danger as the only severity signal. Sighted users with color-vision differences (deuteranopia, protanopia, achromatopsia) could not identify which chip in a list was the rejected one — all chips looked similar in shape.

This PR adds a leading warning icon when intent === 'warning'. Background color is unchanged. The icon is decorative (renders with aria-hidden="true" via svg.svelte when no title is passed); the existing aria-live="assertive" hint on the parent <ChipInput> continues to carry the AT-readable signal for validation failures.

 <span class={merge('chip', intent)}>
+  {#if intent === 'warning'}
+    <Icon name="warning" class="shrink-0" />
+  {/if}
   {#if button}

Before / After

Before — warning is color-only After — warning has icon + color
image image

Invalid chips were previously distinguished only by background color. Now they also carry a warning triangle (⚠) — visible to colorblind users.

Audit context

  • WCAG 2.2 SC 1.4.1 Use of Color (Level A) — Medium remediation priority.
  • Issue file: `audit-output/issues/1.4.1-chip-warning-icon.md` (part of the Wave 2 Holocene severity-primitive bundle — Chip is the third of four; Toast shipped in fix(a11y): pair Toast variant background with a severity icon (WCAG 1.4.1) #3449, Badge / Banner ship as separate PRs).
  • Affects ChipInput and Combobox where validity drives the intent per chip. Single primitive change resolves all consumers.

Affected callsites

A single-primitive fix at Chip resolves all severity callsites:

  • src/lib/holocene/input/chip-input.svelte:128, 189 (`intent={valid ? 'default' : 'warning'}`)
  • src/lib/holocene/combobox/combobox.svelte:518

Other Chip consumers use intent=\"default\" and are unaffected.

Functional impact: zero

Pure visual decoration. Verified:

  • Props API unchanged (intent, button, removeButtonLabel, disabled, onclick, onremove, children)
  • handleRemove callback flow unchanged
  • aria-label, data-track-* attributes on close button unchanged
  • .warning { @apply bg-danger } style unchanged
  • Existing aria-live hint on parent unchanged
  • New icon has no event handlers, no ARIA exposure (decorative)

Test plan

  • Storybook: chip.stories.sveltedefault and warning intents both render correctly
  • Render <ChipInput> with a mix of valid and invalid values; only invalid chips show the warning icon
  • axe-core run on a page consuming ChipInput (search-attribute filter): zero new violations
  • Color-blind simulator (deuteranopia, protanopia): the warning icon shape is perceptible against the chip background
  • Screen reader smoke test: the existing aria-live hint continues to announce on validation failure; the decorative icon does not double-announce

Out of scope

  • Adding success / info / error intents (intent set stays warning + default)
  • Restructuring ChipInput to tie a specific chip to a specific error message (that's 1.3.1 / 4.1.2 enhancement)
  • Adding aria-invalid to the chip wrapper (would require giving the chip a role — separate decision)

🤖 Generated with Claude Code

A11y-Audit-Ref: 1.4.1-holocene-severity-primitives-wave2

The Chip primitive's 'warning' intent previously applied bg-danger
as the only severity signal. Sighted users with color-vision
differences (deuteranopia, protanopia, achromatopsia) could not
identify which chip in a list was the rejected one -- all chips
looked similar in shape.

Render a leading 'warning' Icon when intent === 'warning'. The
icon is decorative (aria-hidden via svg.svelte's default when no
title is passed); the existing aria-live="assertive" hint on the
parent ChipInput continues to carry the AT-readable signal for
validation failures.

Affects ChipInput (chip-input.svelte:128, 189) and Combobox
(combobox.svelte:518) where intent={valid ? 'default' : 'warning'}
is set per chip. Search-attribute filtering, email/ID list inputs,
and similar form surfaces inherit the fix from the primitive.

Mirrors the pattern established by PR #3449 (Toast severity icon)
and the existing Alert primitive (icon defaults to intent).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@bilal-karim
bilal-karim requested a review from a team as a code owner May 25, 2026 16:56
@vercel

vercel Bot commented May 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
holocene Ready Ready Preview, Comment May 25, 2026 4:57pm

Request Review

@github-actions github-actions Bot added a11y Accessibility audit PR a11y:no-fix-doc No A11y-Audit-Ref line; audit team triage a11y:bucket-3 Bucket 3: engineer required a11y:sc-1.4.1 and removed a11y:no-fix-doc No A11y-Audit-Ref line; audit team triage labels May 28, 2026
@bilal-karim
bilal-karim merged commit c7206af into main Jun 4, 2026
19 checks passed
@bilal-karim
bilal-karim deleted the a11y/1.4.1-chip-warning-icon branch June 4, 2026 15:41
rossedfort added a commit that referenced this pull request Jun 4, 2026
Auto-generated version bump from 2.50.0 to 2.51.0

Bump type: minor

Changes included:
- [`495e27f7`](495e27f) Use pageSize instead of maximumPageSize (#3422)
- [`42c58c45`](42c58c4) fix: use rem-based width for expanded side-nav (#3426)
- [`ce928ed5`](ce928ed) fix(a11y): meet 4.5:1 contrast for text-subtle and text-warning (WCAG 1.4.3) (#3437)
- [`e8ed8a49`](e8ed8a4) fix(a11y): consistent high-contrast focus rings across all Button variants (WCAG 1.4.11) (#3438)
- [`03918fb5`](03918fb) fix(a11y): prevent horizontal scroll on login page at 320px (WCAG 1.4.10) (#3440)
- [`a11555fb`](a11555f) fix(a11y): use rem-based font-size and unitless line-height in markdown reset (#3430)
- [`057ff323`](057ff32) fix: use empty alt on decorative SDK logo image (#3424)
- [`e435ffd7`](e435ffd) Standalone Activity details page UI updates (#3427)
- [`e09028f3`](e09028f) Make bottom-nav accept linksSnippet instead of sections (#3445)
- [`a22a1e21`](a22a1e2) Add tooltip for SDK version (#3462)
- [`c3b7820e`](c3b7820) [DT-4039] Workflow query builder doesn't work with numeric search attributes (#3435)
- [`5e53881f`](5e53881) Make Common Errors dismissable (#3471)
- [`2665e2f1`](2665e2f) [DT-4048] Add accessibility PR triage and notification helpers (#3465)
- [`8083cf4e`](8083cf4) feat(DT-4017): Schedules List UI Update (#3467)
- [`b5de30e3`](b5de30e) fix(markdown): allow nested lists to render as block (DT-4047) (#3463)
- [`8a6fd2de`](8a6fd2d) Add a prop to hide select/deselect controls (#3474)
- [`33ec1b3e`](33ec1b3) DT-4051: pre-populate input when starting standalone activity like this one (#3469)
- [`0d64fc20`](0d64fc2) fix(a11y): reveal Copyable's CopyButton on focus-within (WCAG 2.1.1) (#3452)
- [`321651c0`](321651c) fix: accept autocomplete prop on NumberInput, ChipInput, and Combobox (#3425)
- [`6567c222`](6567c22) fix: cap ZoomSvg container height to viewport (#3428)
- [`afd3a8ed`](afd3a8e) fix(a11y): accommodate text-spacing overrides on Badge and Chip (WCAG 1.4.12) (#3433)
- [`d264b876`](d264b87) fix(a11y): non-color signal for Label required indicator (WCAG 1.4.1) (#3439)
- [`73c61ea3`](73c61ea) fix(a11y): tabindex on <main> so the skip link moves focus reliably (WCAG 2.4.1) (#3451)
- [`34e582a8`](34e582a) fix(a11y): info-and-relationships compliance (WCAG 1.3.1) (#3432)
- [`f7be7b6c`](f7be7b6) fix(query): quote ExecutionDuration Go duration strings in visibility SQL (#3482)
- [`9f0c2631`](9f0c263) Passthrough goto params to link component (#3483)
- [`2f8c037b`](2f8c037) feat: add centerButton, menuButton, and linksContent snippets to BottomNavigation (#3485)
- [`7f258bac`](7f258ba) fix: add "for" to validate connection modal title (#3488)
- [`350397ab`](350397a) feat(DT-4069): Update modal backdrop to 50% opaque (#3486)
- [`f01baa49`](f01baa4) refactor: Input & DatePicker - Svelte 5 & afterLabel snippet (#3479)
- [`587c892f`](587c892) Fix decoded object payload summaries (#3491)
- [`2b85ef06`](2b85ef0) fix(DT-4044): only use browser codec endpoint when override option is selected (#3490)
- [`468893ba`](468893b) fix(a11y): improve 1.1.1 non-text content compliance (#3431)
- [`3e8c996d`](3e8c996) fix(a11y): make Tooltip keyboard-accessible, dismissible, and hoverable (#3429)
- [`47552538`](4755253) fix(a11y): name-role-value compliance, partial (WCAG 4.1.2) (#3434)
- [`e2c95d54`](e2c95d5) fix(a11y): align DOM order with visual order in mobile bottom-nav (WCAG 1.3.2) (#3441)
- [`634b08e1`](634b08e) a11y(1.4.11): focus rings — lighten dark-mode --color-border-focus-info to indigo.400 so ring-primary/70 meets 3:1 against surface-primary (#3478)
- [`039a555a`](039a555) a11y(1.4.11): Checkbox — add ring-offset so the checked-state focus ring contrasts against the indigo checked background (#3477)
- [`01161e2e`](01161e2) a11y(1.4.10): activity-options drawer — make width responsive so it reflows at 320 px (#3476)
- [`99f0a0a4`](99f0a0a) fix(a11y): filter hidden nav items in mobile bottom-nav (WCAG 4.1.2) (#3494)
- [`d12ebacc`](d12ebac) fix(a11y): add focusin/focusout to saved-query nav hover tooltip (WCAG 2.1.1) (#3453)
- [`c7206af9`](c7206af) fix(a11y): render a warning icon for Chip warning intent (WCAG 1.4.1) (#3450)
- [`b64ed713`](b64ed71) fix(a11y): pair Toast variant background with a severity icon (WCAG 1.4.1) (#3449)
- [`8e2ef708`](8e2ef70) upgrade temporal api version to latest (v1.62.13) (#3502)

Co-authored-by: rossedfort <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y:bucket-3 Bucket 3: engineer required a11y:sc-1.4.1 a11y Accessibility audit PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants