Convert all Figma theme backgrounds to dark/colored, dedupe again

The 34 kept themes were split 24 light-mode / 18 dark-mode - the light
ones all shared the same washed-out near-white background (L=0.95),
too bright per feedback. Added a force_mood override to build_theme()
and regenerated all 24 as dark instead, same accent character, colored-
dark background rather than near-white.

That conversion collapsed a lot of previously-distinct light backgrounds
into similar dark neutrals, so re-ran the same color-distance dedup pass
across the full set: 8 more turned out to be near-twins once everything
converged to dark (including a 3-way orange-on-dark cluster trimmed to
one). Net: 53 candidates -> 34 kept, all dark/colored, zero white
backgrounds - down from the 42 (24 light/18 dark) shipped last commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-24 08:33:34 -04:00
co-authored by Claude Sonnet 5
parent 722b381913
commit 52371dd02f
3 changed files with 133 additions and 161 deletions
+35 -16
View File
@@ -1,5 +1,5 @@
"""
One-off tool that generated the 53 "website scheme" theme entries in
One-off tool behind the 34 "website scheme" theme entries in
THEME_PALETTES/THEME_DISPLAY_NAMES (offlineu_core.py), sourced from Figma's
"53 Unique Website Color Schemes" resource page:
https://www.figma.com/resource-library/website-color-schemes/
@@ -8,22 +8,36 @@ That page has no raw hex data - each scheme is a rendered mockup
screenshot, not a swatch grid - so website_scheme_swatches.json (checked in
alongside this script) holds dominant colors already extracted from those
53 images via canvas pixel-histogram sampling in a browser, one entry per
scheme: {n, name, colors: [{hex, pct}, ...]}. This script turns that raw
scheme: {n, name, colors: [{hex, pct}, ...]}. build_theme() turns that raw
material into an actual UI palette per scheme (bg/text/accent, matching
THEME_PALETTES' shape), then nudges lightness as needed so every result
clears the same contrast bars this file's checks apply: text-vs-background,
accent-vs-background, and accent-vs-white (buttons always use white text -
see .btn in course_dashboard.html - so a too-bright accent needs catching
even when it reads fine against the background alone).
THEME_PALETTES' shape), then nudges lightness/saturation as needed so every
result clears the same contrast bars this file's checks apply:
text-vs-background, accent-vs-background, and accent-vs-white (buttons
always use white text - see .btn in course_dashboard.html - so a too-bright
accent needs catching even when it reads fine against the background
alone), and so accents don't land at the full saturation that reads as
neon in the mid-lightness band.
Not a live pipeline - re-running it regenerates the exact same 53 themes
from the same frozen swatch data. To add more schemes, extract their
dominant colors the same way (see the canvas-sampling approach used
in-session; not scripted here) and append to the JSON, then rerun and
splice the output into offlineu_core.py by hand.
Running this file directly (see __main__ below) auto-detects each scheme's
own mood from its swatches and writes all 53 as candidates - that's NOT
what's actually shipped. The source material is mostly light-mode
marketing mockups, so the first pass over-represented near-white
backgrounds; the shipped 34 all use build_theme(entry, force_mood='dark')
instead (every one of the 53 converted to a dark/colored background), then
had duplicates pruned by measuring real color distance (hue + lightness +
saturation, background and accent both) between every pair, tight enough
to only catch genuine near-twins - not something this script's __main__
does for you. That curation was one-off analysis, not captured as a single
rerunnable command; to redo it, call build_theme(entry, force_mood='dark')
per scheme, then de-duplicate the results the same way before splicing
into offlineu_core.py by hand.
To add more schemes: extract their dominant colors the same way (see the
canvas-sampling approach used originally; not scripted here) and append to
the JSON, then rerun.
Usage: python3 generate_website_scheme_themes.py
Writes generated_themes.json with the full computed palette per scheme.
Writes generated_themes.json with all 53 candidates, auto-detected mood.
"""
import colorsys
import json
@@ -93,7 +107,7 @@ def tame_accent_saturation(s, l):
return min(s, 0.85)
def build_theme(entry):
def build_theme(entry, force_mood=None):
name = entry['name']
swatches = entry['colors']
parsed = []
@@ -107,8 +121,13 @@ def build_theme(entry):
if not content:
content = parsed
darkest_l = min(p['l'] for p in content)
is_dark = darkest_l < 0.28
if force_mood == 'dark':
is_dark = True
elif force_mood == 'light':
is_dark = False
else:
darkest_l = min(p['l'] for p in content)
is_dark = darkest_l < 0.28
accent_candidates = [p for p in content if p['s'] > 0.25]
if accent_candidates: