tmp colors
This commit is contained in:
parent
f48255a561
commit
d00556b5cc
6 changed files with 198 additions and 53 deletions
119
.config/gnome-settings/derive-palette.py
Normal file
119
.config/gnome-settings/derive-palette.py
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
"""Derive a full color palette from a GNOME accent.
|
||||
|
||||
The palette is a single-hue ramp of 7 stops in OKLCH:
|
||||
- All stops share the anchor's hue
|
||||
- Lightness descends in uniform steps, with the anchor centered
|
||||
=> Choosing the light end also determines the dark end by mirroring
|
||||
- Chroma follows a bell over lightness, peaking at the anchor
|
||||
|
||||
In addition, 5 accent hues with 2 shades each are derived
|
||||
for the terminals' ANSI colors.
|
||||
|
||||
The resulting values are recorded in the palette table in ./README.md
|
||||
and this script is the source of truth for all colors.
|
||||
|
||||
Usage: `python3 derive-palette.py`
|
||||
(requires `pip install coloraide` before)
|
||||
"""
|
||||
|
||||
import coloraide
|
||||
|
||||
ANCHOR = "#2190A4" # GNOME's teal (i.e., libadwaita's "--accent-teal")
|
||||
|
||||
L_LIGHT = 0.933 # The one chosen parameter
|
||||
|
||||
# Tail values of the chroma bell at the ramp's two ends
|
||||
# where the dark tail is held high enough to keep the darkest stop
|
||||
# appear in the chosen accent's hue
|
||||
C_LIGHT, C_DARK = 0.017, 0.055
|
||||
|
||||
# Canonical hue angles (in OKLCH)
|
||||
# for the terminal's semantic colors
|
||||
ACCENT_HUES = {
|
||||
"red": 25,
|
||||
"yellow": 95,
|
||||
"green": 145,
|
||||
"blue": 260,
|
||||
"magenta": 330,
|
||||
}
|
||||
|
||||
# The anchor's lightness, chroma, and hue seed everything below
|
||||
a = coloraide.Color(ANCHOR).convert("oklch")
|
||||
AL, AC, H = a["lightness"], a["chroma"], a["hue"]
|
||||
step = (L_LIGHT - AL) / 3
|
||||
|
||||
|
||||
def hexify(L, C, h=H):
|
||||
"""Convert an OKLCH color to an upper-case sRGB hex string.
|
||||
|
||||
Colors outside the sRGB gamut are fitted by reducing chroma
|
||||
while preserving lightness and hue; in this palette that only
|
||||
affects the darkest stops, where sRGB cannot hold the full chroma.
|
||||
"""
|
||||
return (
|
||||
coloraide.Color("oklch", [L, C, h])
|
||||
.convert("srgb")
|
||||
.fit(method="oklch-chroma")
|
||||
.to_string(hex=True, upper=True)
|
||||
)
|
||||
|
||||
|
||||
def bell(L):
|
||||
"""Return the ramp's chroma at lightness `L`.
|
||||
|
||||
Chroma follows a parabola over lightness: it peaks at the anchor
|
||||
(i.e., `AC`) and falls off to `C_LIGHT` at the light end and `C_DARK`
|
||||
at the dark end of the ramp. So, both ends still appear in the chosen
|
||||
accent's hue without ever exceeding the anchor's saturation.
|
||||
"""
|
||||
end = C_LIGHT if L >= AL else C_DARK
|
||||
t = (L - AL) / (3 * step)
|
||||
return AC - (AC - end) * t * t
|
||||
|
||||
|
||||
def lift(h):
|
||||
"""Return the lightness at which hue `h` reads on the dark base.
|
||||
|
||||
Starting from the anchor's lightness, raise it in steps of 0.01
|
||||
until the color reaches a WCAG 2.1 contrast of 4.5:1 against the
|
||||
ramp's darkest stop (i.e., `dark_base`, defined below but before
|
||||
the first call). The accents' lightness is thereby solved
|
||||
from the contrast requirement rather than chosen by hand.
|
||||
"""
|
||||
L = AL
|
||||
|
||||
while (
|
||||
coloraide.Color(hexify(L, AC, h)).contrast(
|
||||
dark_base,
|
||||
method="wcag21",
|
||||
)
|
||||
< 4.5
|
||||
):
|
||||
L += 0.01
|
||||
|
||||
return L
|
||||
|
||||
|
||||
ramp = [
|
||||
hexify(AL + k * step, AC if k == 0 else bell(AL + k * step))
|
||||
for k in range(3, -4, -1)
|
||||
]
|
||||
|
||||
dark_base = coloraide.Color(ramp[-1])
|
||||
|
||||
|
||||
accents = {}
|
||||
|
||||
for n, h in ACCENT_HUES.items():
|
||||
L = lift(h) # Solve only once per hue
|
||||
accents[n] = (
|
||||
hexify(L, AC, h),
|
||||
hexify(L + 0.10, AC, h),
|
||||
)
|
||||
|
||||
|
||||
print("ramp:", *ramp)
|
||||
|
||||
for n, (norm, bright) in accents.items():
|
||||
|
||||
print(f"{n}: {norm} {bright}")
|
||||
Loading…
Reference in a new issue