What's wrong
ThemeProvider.Demo/Program.cs CalculateTargetLightnessForPriority (~lines 438–480) is a copy of the private SemanticColorMapper.CalculateTargetLightnessForSemantic (ThemeProvider/SemanticColorMapper.cs ~160–225). The copy predates the change that put non-neutral meanings into 20–82% of the global lightness range: it always spreads targets across the full global min–max and ignores the meaning.
So in the demo's "Lightness-Based Priority System" table, "Target L" disagrees with "Actual L" for Primary, Alternate, Success and every other non-neutral meaning, which makes the mapper look like it misses its targets. Only copy-paste keeps the two in sync, and it has already drifted.
Repro
Console app against the built library, Catppuccin Mocha, Primary. demoTarget = the demo's formula; actual = MakeCompletePalette(...) colour .ToOklab().L:
VeryLow demoTarget=0.18 actual=0.32
Low demoTarget=0.30 actual=0.39
MediumLow demoTarget=0.41 actual=0.47
Medium demoTarget=0.53 actual=0.54
MediumHigh demoTarget=0.65 actual=0.61
High demoTarget=0.76 actual=0.68
VeryHigh demoTarget=0.88 actual=0.75
For Neutral the columns agree, because Neutral really does use the full range.
Suggested fix
Stop duplicating the formula. Expose the target calculation from the library, e.g. a public SemanticColorMapper.GetTargetLightness(ISemanticTheme theme, SemanticColorRequest request) built on the existing private helpers; have the demo call it and delete CalculateTargetLightnessForPriority.
Acceptance criteria
- The demo contains no lightness-range or target formula of its own.
- A unit test asserts that, for every built-in theme, meaning and priority,
GetTargetLightness is within 1e-3 of the Oklab L of the MakeCompletePalette colour wherever that colour is in gamut.
Related: #115 covers the demo's label text ("non-neutral uses 50–90%", Program.cs:252) and stale docs, not the computed values.
What's wrong
ThemeProvider.Demo/Program.csCalculateTargetLightnessForPriority(~lines 438–480) is a copy of the privateSemanticColorMapper.CalculateTargetLightnessForSemantic(ThemeProvider/SemanticColorMapper.cs~160–225). The copy predates the change that put non-neutral meanings into 20–82% of the global lightness range: it always spreads targets across the full global min–max and ignores the meaning.So in the demo's "Lightness-Based Priority System" table, "Target L" disagrees with "Actual L" for Primary, Alternate, Success and every other non-neutral meaning, which makes the mapper look like it misses its targets. Only copy-paste keeps the two in sync, and it has already drifted.
Repro
Console app against the built library, Catppuccin Mocha, Primary.
demoTarget= the demo's formula;actual=MakeCompletePalette(...)colour.ToOklab().L:For Neutral the columns agree, because Neutral really does use the full range.
Suggested fix
Stop duplicating the formula. Expose the target calculation from the library, e.g. a public
SemanticColorMapper.GetTargetLightness(ISemanticTheme theme, SemanticColorRequest request)built on the existing private helpers; have the demo call it and deleteCalculateTargetLightnessForPriority.Acceptance criteria
GetTargetLightnessis within 1e-3 of the Oklab L of theMakeCompletePalettecolour wherever that colour is in gamut.Related: #115 covers the demo's label text ("non-neutral uses 50–90%",
Program.cs:252) and stale docs, not the computed values.