Files
sure/app/components/DS/alert.rb
Guillem Arias Fauste f0e270f578 fix(design-system): restore dark-mode contrast on Toggle + destructive borders (#1932)
Two regressions from the recent token sweep, both producing low-contrast
results in dark mode.

## DS::Toggle off-track

PR #1843 (DS::Toggle a11y + token swaps) replaced the raw
`bg-gray-100 theme-dark:bg-gray-700` off-track with `bg-surface-inset`
for semantic alignment. `bg-surface-inset` resolves to gray-800 in
dark mode, but the toggle typically sits inside `bg-container`
(gray-900). The contrast ratio dropped from ~2.45:1 (gray-700 vs
gray-900) to ~1.5:1 (gray-800 vs gray-900) — visibly worse than the
pre-#1843 baseline and below WCAG 1.4.11 (3:1 for UI components).

Most visible inside the transaction-edit modal SETTINGS section
(`Exclude`, `One-time Expense`) where the off-state switches nearly
vanished into the modal chrome.

Introduce `--color-toggle-track` (light: gray-100, dark: gray-700) and
swap `bg-surface-inset` → `bg-toggle-track` in DS::Toggle. Restores the
pre-#1843 off-track contrast while keeping a semantic token (instead
of the raw palette references the migration was trying to remove).

## border-destructive subtle borders

PR #1849 (single-color tokens to @theme) flagged that
`border-destructive/N` rendered the wrong shade (the `@utility
border-destructive` block defined red-500 light, while
`--color-destructive` in `@theme` is red-600 — `/N` resolves from
@theme), and swapped a couple of callsites to solid `border-destructive`.
Solid renders red-500/red-400 at full saturation in both modes, which
reads as a loud error border on contexts that were meant to be subtle
(left-rule on the provider-sync "view error details" pane, error-message
box in SimpleFIN settings, alert-component border, provider connection
error rows).

Two callsites (`DS::Alert`, settings/providers/_connection_row) still
carried the broken `border-destructive/20` / `/25` modifier — same
off-shade footgun #1849 was meant to retire.

Introduce `--color-destructive-subtle` (light: red-200, dark: red-800)
and swap the four subtle-by-intent callsites to `border-destructive-subtle`:

- app/components/DS/alert.rb (destructive variant)
- app/views/settings/providers/_connection_row.html.erb (err status)
- app/components/provider_sync_summary.html.erb (error-details left rule)
- app/views/simplefin_items/edit.html.erb (error-message box)

The handful of intentionally-loud `border-destructive` callsites
(split-transaction over-allocation, blank-name account labels, etc.)
keep the solid token.

Regenerated `_generated.css` via `npm run tokens:build`.
2026-05-23 09:21:46 +02:00

88 lines
1.9 KiB
Ruby

class DS::Alert < DesignSystemComponent
VARIANTS = %i[info success warning error destructive].freeze
LIVE_MODES = %i[none status alert].freeze
def initialize(message: nil, title: nil, variant: :info, live: :none)
@message = message
@title = title
@variant = normalize_variant(variant)
@live = normalize_live(live)
end
private
attr_reader :message, :title, :variant, :live
def normalize_variant(raw)
sym = raw.respond_to?(:to_sym) ? raw.to_sym : nil
VARIANTS.include?(sym) ? sym : :info
end
def normalize_live(raw)
sym = raw.respond_to?(:to_sym) ? raw.to_sym : nil
case sym
when :polite then :status
when :assertive then :alert
when *LIVE_MODES then sym
else :none
end
end
def container_classes
base_classes = "p-4 rounded-lg border"
variant_classes = case variant
when :info
"bg-info/10 border-info/20"
when :success
"bg-success/10 border-success/20"
when :warning
"bg-warning/10 border-warning/20"
when :error, :destructive
"bg-destructive/10 border-destructive-subtle"
end
"#{base_classes} #{variant_classes}"
end
def icon_name
case variant
when :info
"info"
when :success
"check-circle"
when :warning
"alert-triangle"
when :error, :destructive
"x-circle"
end
end
def icon_color
case variant
when :success
"success"
when :warning
"warning"
when :error, :destructive
"destructive"
else
"info"
end
end
def aria_role
case live
when :status then "status"
when :alert then "alert"
end
end
def variant_label
I18n.t("ds.alert.variants.#{variant}")
end
def title_id
@title_id ||= "DS-alert-title-#{SecureRandom.hex(4)}"
end
end