maui-theming: recommend a single-source palette instead of inline literals

maui-theming was the last skill not passing the gate: 1W/3T/0L, +10%
[-21.8%, +41.8%]. No losses - three ties. I had put that down to a ceiling
effect (its baseline Quality is 5.0/5) and suggested accepting it. That was
wrong; the artifacts show a real, fixable gap.

What the eval artifacts actually show:

- Skilled answers were already LONGER than baseline on all three tied
  scenarios (2466 vs 2100, 2195 vs 1972, 1797 vs 1590 chars), so this was not
  the terseness problem fixed in the previous commit.
- On "Detect and respond to system theme changes" the skilled answer covered
  strictly more than baseline (adding UiMode and Preferences) and still tied.
  More words were not converting into wins.
- On "Add light/dark mode support using AppThemeBinding" BOTH answers scattered
  hardcoded hex literals across every element - while that scenario's rubric
  explicitly says "Does not suggest hardcoded color values as a final
  solution". Baseline at least mentioned moving them to App.xaml; the skill
  never demonstrated it.

So the differentiator was structural, not volume. Added a "Define the palette
once - don't scatter literals" section that makes the recommended final shape a
single App.xaml source of truth: a raw Color palette plus implicit Styles
(TargetType="ContentPage" ApplyToDerivedTypes="True", TargetType="Label") whose
setters carry the AppThemeBinding. Pages then need no theming markup at all.
Inline AppThemeBinding is reframed as the one-off case, and even then pointed at
StaticResource keys rather than literal hex.

The pattern is compile-verified in a net11.0-maccatalyst app (implicit styles
with nested AppThemeBinding + StaticResource in a merged ResourceDictionary).

Measured (n=4/scenario, 16 trials, executor claude-opus-4.6, judge gpt-5.5):

  before this commit   10W/5T/1L   +0.22 [+0.09, +0.36]   63% win
  after                14W/1T/1L   +0.33 [+0.21, +0.44]   88% win

Ties collapse from 5 to 1, and all four scenarios now win predominantly -
including "Add light/dark mode support using AppThemeBinding", which was the
tie that motivated the change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 58513e0c-4086-4112-9ea1-95dbd8e8e469
This commit is contained in:
Jakub Florkowski
2026-07-28 22:55:37 +02:00
parent 5812d5af17
commit 1db8796dc4
@@ -86,14 +86,53 @@ more than two themes, or a user-selectable theme.
`AppThemeBinding` selects a value based on the current system theme. It supports `Light`, `Dark`, and an optional `Default` fallback.
### XAML
### Define the palette once — don't scatter literals
Putting `{AppThemeBinding Light=#333333, Dark=#FFFFFF}` on every element is the
single most common theming mistake: the palette ends up duplicated across dozens of
files and cannot be changed in one place. **Recommend this shape as the final
answer**, not inline literals:
```xml
<!-- App.xaml — one source of truth for the whole app -->
<Application.Resources>
<ResourceDictionary>
<!-- 1. Raw palette -->
<Color x:Key="LightPageBackground">#FFFFFF</Color>
<Color x:Key="DarkPageBackground">#1E1E1E</Color>
<Color x:Key="LightPrimaryText">#333333</Color>
<Color x:Key="DarkPrimaryText">#E0E0E0</Color>
<!-- 2. Implicit styles bind the pair once; every page inherits them -->
<Style TargetType="ContentPage" ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor"
Value="{AppThemeBinding Light={StaticResource LightPageBackground},
Dark={StaticResource DarkPageBackground}}" />
</Style>
<Style TargetType="Label">
<Setter Property="TextColor"
Value="{AppThemeBinding Light={StaticResource LightPrimaryText},
Dark={StaticResource DarkPrimaryText}}" />
</Style>
</ResourceDictionary>
</Application.Resources>
```
Pages then need **no theming markup at all** — they pick the styles up implicitly.
Use an inline `AppThemeBinding` only for genuine one-offs, and even then reference
`{StaticResource}` keys rather than literal hex.
### XAML (inline form, for one-offs)
```xml
<Label Text="Themed text"
TextColor="{AppThemeBinding Light=Green, Dark=Red}"
BackgroundColor="{AppThemeBinding Light=White, Dark=Black}" />
<!-- With resource references -->
<!-- With resource references — preferred over literals -->
<Label TextColor="{AppThemeBinding Light={StaticResource LightPrimary},
Dark={StaticResource DarkPrimary}}" />
```