Skip to content

fix: draw the completion toast with the themed status brushes - #2118

Merged
laurentiu021 merged 1 commit into
mainfrom
fix/toast-uses-the-themed-success-brushes
Sep 4, 2026
Merged

fix: draw the completion toast with the themed status brushes#2118
laurentiu021 merged 1 commit into
mainfrom
fix/toast-uses-the-themed-success-brushes

Conversation

@laurentiu021

Copy link
Copy Markdown
Owner

Closes #2106.

What was wrong

The completion toast's container follows the theme (Background="{DynamicResource Surface2}") while three greens on top of it were fixed literals: the tick's Foreground="#22C55E", its badge Background="#2022C55E" and the outer BorderBrush="#4022C55E". #22C55E is the dark-mode value.

Measured against each preset's real Surface2, read out of ThemeService's preset table rather than approximated:

Light preset Surface2 Tick today Themed Success
clean-indigo #F1F5F9 2.08:1 6.51:1
sky-breeze #E0F2FE 1.99:1 6.21:1
mint-fresh #BBF7D0 1.88:1 5.88:1
warm-sand #FDE68A 1.83:1 5.73:1
lavender #E9D5FF 1.67:1 5.24:1
soft-blossom #FBCFE8 1.65:1 5.16:1

Six light presets, not the four #2106 listed — I had missed lavender and soft-blossom, and soft-blossom is the worst of them. On midnight-indigo the same green measures 7.66:1, which is why it reads correctly in dark mode and was never noticed. WCAG asks 4.5:1 for text and 3:1 for a meaningful graphic; it cleared neither on any light preset.

ThemeService already solved this: it seeds Success per mode — #22C55E dark, #166534 light — and the comment beside those values names this exact failure, "one crisp card beside two washed-out ones". The toast simply did not use it.

The fix

Success, SuccessBgSubtle and SuccessBorder, all three of which the theme already carries per mode.

Mockup at ui-mockups/toast-success-contrast.html, generated from ThemeService's preset table rather than hand-approximated, showing today against proposed for one dark preset and all six light ones with the ratio on each. There is no design choice in this change — the themed brush for this exact purpose already exists and the numbers are one-sided — so it is implemented rather than held for a decision.

The guard, and why not the existing one

#2106 suggested widening NoThemedFill_CarriesAHardcodedWhiteForeground. That guard reads App.xaml styles whose Background binds Accent or Danger, looking for the literal White. Different file, different fill, different literal — widening it to cover this would have meant rewriting it into something else.

NoViewPaintsItselfWithALiteralColour is the general rule instead: no colour-bearing attribute in the shell or in Views/ may take a literal hex. It is cheap because the view layer was already nearly clean — seven literals in total, three of them the toast's.

The other four are exceptions with reasons, and the reasons are different in kind:

  • ThemePopup.xaml, four 24×24 swatchesx:Named Borders whose fill the code-behind replaces with the value being edited. The literal is a designer-time default, and the element's job is to be a colour.
  • App.xaml, Stroke="#111111" and Stroke="#FFFFFF" — the FocusRing's two stacked strokes. Theme-independence is the feature: the ring has to stay visible on PrimaryButton's accent fill, DangerButton's red, a raised grey and a card surface, and the accent itself falls to 1.00:1 against one of those. Allowed by exact value, not by file, so a third literal in App.xaml still fails.

The App.xaml pair was found by the guard, not by me — my measurement had globbed the shell, Views/ and Resources/ and missed App.xaml.

Verification

Mutation proof, both files restored byte-for-byte:

Mutation Result
the tick goes back to Foreground="#22C55E" RED, naming MainWindow.xaml
the FocusRing exception is removed RED, naming App.xaml
the ThemePopup swatch exception is removed RED, naming ThemePopup.xaml

The second and third confirm both exception lists are load-bearing rather than decoration. Baseline and post-restore green.

197 cases green across ArchitectureTests, ThemeServiceTests and the theme-contrast suites. Builds 0 errors / 0 warnings, dotnet format --verify-no-changes clean on both, version consistency csproj 1.76.15 = CHANGELOG 1.76.15 = SECURITY 1.76.x.

Not verified here: how it looks on screen, which needs the app running. What is verified is that the three brushes resolve (the XAML builds), that they are the per-mode ones, and the ratios above computed from the values the theme will actually apply.

The toast's container follows the theme through Surface2 while three greens on top of
it were literals: the tick's Foreground, its badge Background and the outer
BorderBrush, all #22C55E -- the DARK-mode value.

Measured against each preset's real Surface2, the tick came out at 1.65:1 on
soft-blossom and never better than 2.08:1 on any of the six light presets, against
7.66:1 on midnight-indigo where the value was chosen. WCAG asks 4.5:1 for text and
3:1 for a meaningful graphic; it cleared neither. Six light presets, not the four the
issue listed -- lavender and soft-blossom were missing, and soft-blossom is worst.

ThemeService already seeds Success per mode (#22C55E dark, #166534 light) and the
comment beside those values names this failure. The toast now uses Success,
SuccessBgSubtle and SuccessBorder.

Guarded by NoViewPaintsItselfWithALiteralColour rather than by widening
NoThemedFill_CarriesAHardcodedWhiteForeground, which reads App.xaml styles bound to
Accent/Danger looking for literal White -- different file, fill and literal. The new
rule is cheap because the view layer had only seven literals in total. The four that
remain are exceptions with distinct reasons: ThemePopup's swatches exist to BE a
colour, and the FocusRing's two strokes must NOT follow the theme, since no single
themed colour survives every surface the ring lands on. That pair is allowed by exact
value, so a third literal in App.xaml still fails -- and the guard is what found it,
my own measurement had missed App.xaml.

Closes #2106
@laurentiu021
laurentiu021 merged commit 12ba0c5 into main Sep 4, 2026
5 checks passed
@laurentiu021
laurentiu021 deleted the fix/toast-uses-the-themed-success-brushes branch September 4, 2026 15:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: The success toast uses dark-mode green on every theme — 1.83:1 on light presets

1 participant