From 83fe164d989738dba1f6a86128483db397d40759 Mon Sep 17 00:00:00 2001 From: Bilal Karim <4129613+bilal-karim@users.noreply.github.com> Date: Fri, 22 May 2026 09:29:28 -0400 Subject: [PATCH 1/2] fix(a11y): high-contrast focus rings on primary and destructive buttons (WCAG 1.4.11) Two same-shape fixes for SC 1.4.11 Non-text Contrast on the Button primitive. Both variants previously composited a ring color over a same-or-similar button background, producing ratios that fail the 3:1 SC threshold: - primary: indigo ring on indigo button (~1:1, effectively invisible) -> white ring with offset against the page surface (~7:1). - destructive: red ring at 70% on red.300 button (~2:1) -> full-opacity red ring with offset against the page (~6:1). The ring-offset-2 + ring-offset-surface-primary pattern puts the ring in a 2px gap between the button and the page so it never blends with either surface. Matches the convention used by Material, Primer, and Atlassian Design System. Co-Authored-By: Claude Opus 4.6 --- src/lib/holocene/button.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/holocene/button.svelte b/src/lib/holocene/button.svelte index 35eb1cc769..c4f1b90840 100644 --- a/src/lib/holocene/button.svelte +++ b/src/lib/holocene/button.svelte @@ -25,11 +25,11 @@ variants: { variant: { primary: - 'surface-interactive border-transparent text-white focus-visible:ring-primary/70 data-[active=true]:bg-subtle data-[active=true]:text-primary', + 'surface-interactive border-transparent text-white focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-surface-primary data-[active=true]:bg-subtle data-[active=true]:text-primary', secondary: 'surface-primary border-subtle focus-visible:ring-primary/70 hover:surface-interactive-secondary focus-visible:surface-interactive-secondary data-[active=true]:bg-subtle', destructive: - 'surface-interactive-danger border-transparent focus-visible:ring-danger/70 data-[active=true]:surface-interactive-danger', + 'surface-interactive-danger border-transparent focus-visible:ring-danger focus-visible:ring-offset-2 focus-visible:ring-offset-surface-primary data-[active=true]:surface-interactive-danger', ghost: 'bg-transparent border-transparent text-primary hover:surface-interactive-ghost focus-visible:surface-interactive-ghost focus-visible:ring-primary/70 data-[active=true]:bg-subtle', 'table-header': From 821ed18b5e22b6b516bc802826cb7c4ee46b5c3d Mon Sep 17 00:00:00 2001 From: Bilal Karim <4129613+bilal-karim@users.noreply.github.com> Date: Fri, 22 May 2026 09:44:43 -0400 Subject: [PATCH 2/2] fix(a11y): apply consistent focus-ring pattern across all Button variants Follow-up to the primary + destructive focus-ring fix. - Primary ring switched from ring-white to ring-primary. The white ring blended with the white page in light mode (button -> white gap -> white ring -> white page); the new full-opacity indigo ring is visible against both light and dark page surfaces. - Secondary, ghost, and table-header variants now also use the ring-{color} + ring-offset-2 + ring-offset-surface-primary pattern for visual consistency. The offset puts the ring in a page-color gap separated from the button. Pattern is now uniform: button -> page-color gap -> ring (in button's color family) -> page. Co-Authored-By: Claude Opus 4.6 --- src/lib/holocene/button.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/holocene/button.svelte b/src/lib/holocene/button.svelte index c4f1b90840..60b05b7621 100644 --- a/src/lib/holocene/button.svelte +++ b/src/lib/holocene/button.svelte @@ -25,15 +25,15 @@ variants: { variant: { primary: - 'surface-interactive border-transparent text-white focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-surface-primary data-[active=true]:bg-subtle data-[active=true]:text-primary', + 'surface-interactive border-transparent text-white focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-surface-primary data-[active=true]:bg-subtle data-[active=true]:text-primary', secondary: - 'surface-primary border-subtle focus-visible:ring-primary/70 hover:surface-interactive-secondary focus-visible:surface-interactive-secondary data-[active=true]:bg-subtle', + 'surface-primary border-subtle focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-surface-primary hover:surface-interactive-secondary focus-visible:surface-interactive-secondary data-[active=true]:bg-subtle', destructive: 'surface-interactive-danger border-transparent focus-visible:ring-danger focus-visible:ring-offset-2 focus-visible:ring-offset-surface-primary data-[active=true]:surface-interactive-danger', ghost: - 'bg-transparent border-transparent text-primary hover:surface-interactive-ghost focus-visible:surface-interactive-ghost focus-visible:ring-primary/70 data-[active=true]:bg-subtle', + 'bg-transparent border-transparent text-primary hover:surface-interactive-ghost focus-visible:surface-interactive-ghost focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-surface-primary data-[active=true]:bg-subtle', 'table-header': - 'bg-transparent border-transparent focus-visible:ring-primary/70 focus-visible:border-transparent', + 'bg-transparent border-transparent focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-surface-primary focus-visible:border-transparent', }, size: { xs: 'h-8 text-xs px-2 py-1',