"""Generate tonal + complement scales for Lumotia v0.3 quietware tokens. Each scale has 11 steps: 50, 100, 200, ..., 900, 950. Lightness curve is hand-picked to mimic the OKLCH-style tonal scales used by Material 3 and IBM Carbon. Saturation drops at extremes to avoid neon flat tints and washed-out highs. Output is CSS variable declarations written to stdout. """ import colorsys # (name, hex, saturation_at_500) SOURCES = [ ("red", "#FF0700"), ("blue", "#000AFF"), ("green", "#00FF56"), ("yellow", "#FFCD00"), ("orange", "#F0620A"), ("red-complement", "#00F8FF"), ("blue-complement", "#FFF500"), ("green-complement", "#FF00A9"), ("yellow-complement", "#0032FF"), ("orange-complement", "#0A98F0"), ] # Lightness + saturation curves per shade step. STEPS = [ # step, L, S-factor (multiplied by source S) ( "50", 0.97, 0.30), ("100", 0.93, 0.50), ("200", 0.86, 0.70), ("300", 0.76, 0.85), ("400", 0.65, 0.95), ("500", 0.52, 1.00), ("600", 0.45, 1.00), ("700", 0.36, 0.95), ("800", 0.27, 0.90), ("900", 0.18, 0.80), ("950", 0.11, 0.70), ] def hex_to_hls(h): h = h.lstrip("#") r, g, b = tuple(int(h[i:i+2], 16) / 255 for i in (0, 2, 4)) return colorsys.rgb_to_hls(r, g, b) def hls_to_hex(h, l, s): r, g, b = colorsys.hls_to_rgb(h, l, s) return "#{:02X}{:02X}{:02X}".format(*(max(0, min(255, round(c * 255))) for c in (r, g, b))) for name, src_hex in SOURCES: h, _l_src, s_src = hex_to_hls(src_hex) print(f" /* {name} — source {src_hex} */") for step, l, s_factor in STEPS: s = max(0.0, min(1.0, s_src * s_factor)) print(f" --{name}-{step}: {hls_to_hex(h, l, s)};") print()