display: themes are made of colors; hue is a doorway
commit 8027399b529cf09e712cf6c4f68347419b98aca6 · parent a92ee56422
· browse this tree
Ximianth, 2026-10-30 17:27:03 UTC
NewThemeColors is the ground-level constructor (an explicit accent pair, one color per scheme, verbatim). AccentColors exposes the hue-to-pair tuning, so embedders accepting a chosen hue mint colors with the same AA guarantees as hashed seeds. NewTheme(seed) is now just the composition of the two. Replaces the briefly-lived NewThemeAt from a92ee56: piping a hue through theme construction was the wrong currency.
diff --git a/display/theme.go b/display/theme.go index d65cc11..4677862 100644 --- a/display/theme.go +++ b/display/theme.go @@ -19,20 +19,31 @@ type Theme struct { } func NewTheme(seed string) Theme { - return NewThemeAt(seed, ThemeHue(seed)) + light, dark := AccentColors(ThemeHue(seed)) + return NewThemeColors(seed, light, dark) } -// NewThemeAt builds a theme at an explicitly chosen hue, for embedders whose users -// overrule the seed's draw (the hash diversifies nicely, but it cannot be argued with). -// The per-scheme lightness tuning still applies, so a chosen hue keeps the same -// contrast guarantees as a hashed one. -func NewThemeAt(name string, hue int) Theme { +// AccentColors turns a hue into the accent pair this package would wear at it: +// one color per scheme, with the lightness tuning that keeps the accent AA-legible +// as text on each scheme's paper. Exported so embedders can accept a chosen hue +// (the hash diversifies nicely, but it cannot be argued with) and still mint +// colors with the same guarantees -- colors, not hues, are what themes are made of. +func AccentColors(hue int) (light, dark string) { + return fmt.Sprintf("hsl(%d 70%% %d%%)", hue, themeLightness(hue)), + fmt.Sprintf("hsl(%d 70%% %d%%)", hue, themeLightnessDark) +} + +// NewThemeColors builds a theme from an explicit accent pair, one color per scheme. +// This is the ground-level constructor: every theme is colors in the end. +// No tuning applies here -- whoever picks exact colors owns their contrast -- +// and the values land in CSS verbatim, so validating them is the caller's business. +func NewThemeColors(name, light, dark string) Theme { return Theme{ Name: name, CSS: []byte(fmt.Sprintf( - ":root { --accent: hsl(%d 70%% %d%%); }\n"+ - "@media (prefers-color-scheme: dark) { :root { --accent: hsl(%d 70%% %d%%); } }\n", - hue, themeLightness(hue), hue, themeLightnessDark)), + ":root { --accent: %s; }\n"+ + "@media (prefers-color-scheme: dark) { :root { --accent: %s; } }\n", + light, dark)), } }