Skip to content

Single-colour semantic meanings drop the source alpha (always opaque), and the strict gamut check shifts in-gamut colours like #ff0000 → #ff0201 #111

Description

@matt-edmondson

ThemeProvider/SemanticColorMapper.cs has two related defects in the single-source-colour path (ExtrapolateColorToLightness and its chroma search).

1. Alpha is discarded

At :286 and :323 the code calls Color.FromOklab(...) without an alpha argument, so alpha defaults to 1.0 and baseColor.A is lost. The interpolation path (MixOklab) does keep alpha. The result is that every meaning with a single colour, which includes every accent, comes out opaque, while neutral ramps keep their alpha.

Repro: a custom theme with Neutral = [#10101080, #f0f0f080] and Primary = [#3366cc80]:

src primary alpha=0.50196; Neutral(Medium).A=0.50196; Primary(Medium).A=1

Expected: Primary(Medium).A is 0.502.

Fix: use Color.FromOklab(targetOklab, baseColor.A) and Color.FromOklab(testOklab, baseColor.A).

2. The gamut test has no tolerance

:289-291 and :325-327 check R >= 0.0 && R <= 1.0 exactly. An Oklab round trip leaves about 1e-7 of floating-point error. For example, #ff0000 comes back as linear (1.0000000004, -5e-9, -3e-8). Colours that are really in gamut are therefore classed as out of gamut, and the chroma binary search (tolerance 0.001) stops slightly inside the boundary.

Repro: a single-colour Primary mapped at its own lightness:

  • #ff0000 → #ff0201
  • #00ff00 → #04ff03
  • #ffff00 → #ffff08

Expected: the input colour is returned unchanged.

Fix: move the check into one helper that uses an epsilon, for example c >= -1e-6 && c <= 1 + 1e-6, keep the final clamp, and use the helper at both call sites.

Acceptance

  • Accent colours keep their source alpha.
  • Mapping a single colour at its own lightness returns that colour exactly, to 8-bit precision.
  • Tests cover both.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    readyFully specified; implement as written

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions