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
+16 -12
View File
@@ -120,12 +120,13 @@ silently stay blank instead of erroring.
- Per-course study guide export (markdown, organized by section) - Per-course study guide export (markdown, organized by section)
**Personalization** (Settings) **Personalization** (Settings)
- 54 built-in themes (grouped in the dropdown: Base, Editor Themes, and 5 - 46 built-in themes (grouped in the dropdown: Base, Editor Themes, and 5
categories of website-inspired schemes sourced from Figma's "53 Unique categories of website-inspired schemes sourced from Figma's "53 Unique
Website Color Schemes" - 42 of the 53 kept, after cutting a couple of Website Color Schemes" - 34 of the 53 kept, all as dark/colored
neon-bright ones and several that ended up near-duplicates of another backgrounds rather than the source page's mostly-white mockups, after
scheme once mapped into actual UI colors), or a fully custom accent cutting a couple of neon-bright accents and pruning near-duplicates by
color, corner radius, and card style actual color distance), or a fully custom accent color, corner radius,
and card style
- Font, text size, page width, and spacing density - Font, text size, page width, and spacing density
- Default library path override + manual "Refresh Library" (bypasses the - Default library path override + manual "Refresh Library" (bypasses the
5-minute filesystem-scan cache) 5-minute filesystem-scan cache)
@@ -199,10 +200,13 @@ folder and file names.
## Tools ## Tools
`tools/generate_website_scheme_themes.py` is the one-off script that `tools/generate_website_scheme_themes.py` is the one-off script that
produced the 42 "website scheme" theme entries in `THEME_PALETTES` (of produced the 34 "website scheme" theme entries in `THEME_PALETTES` (of
53 candidates - 11 were cut after visual review, 2 for a neon-bright 53 candidates - all 24 light-mode ones were converted to dark/colored
accent and 9 for landing too close to another kept scheme once mapped backgrounds per request, then 19 were cut across two review passes: 2
into actual UI colors) - not part of the running app, kept for reference for a neon-bright accent, 17 for landing too close to another kept
if those palettes ever need regenerating or extending. See its module scheme once mapped into actual UI colors, since forcing everything dark
docstring for how it turned Figma's color-scheme screenshots into actual made several collide that hadn't before) - not part of the running app,
UI palettes. kept for reference if those palettes ever need regenerating or
extending. `build_theme()` takes a `force_mood='dark'|'light'` override
for exactly this kind of bulk remapping. See its module docstring for
how it turned Figma's color-scheme screenshots into actual UI palettes.
+82 -133
View File
@@ -100,28 +100,25 @@ SETTINGS_CHOICES = {
'theme': { 'theme': {
'dark', 'light', 'dracula', 'tokyo_night', 'catppuccin_mocha', 'ayu_dark', 'dark', 'light', 'dracula', 'tokyo_night', 'catppuccin_mocha', 'ayu_dark',
'github_dark', 'atom_one_dark', 'houston', 'night_owl', 'nord', 'matcha', 'github_dark', 'atom_one_dark', 'houston', 'night_owl', 'nord', 'matcha',
# 42 palettes derived from Figma's "53 Unique Website Color Schemes" # 34 palettes derived from Figma's "53 Unique Website Color Schemes"
# resource page (11 of the 53 dropped: 2 for a neon-bright accent # resource page, all converted to dark/colored backgrounds (the
# that read badly as a button color, the rest for being too close # source page's own schemes were mostly light-mode marketing
# to another scheme already kept - same accent hue/lightness and # mockups) - see WEBSITE_SCHEME_CATEGORIES below for the dropdown
# background, just a different name) - see WEBSITE_SCHEME_CATEGORIES # grouping, and tools/generate_website_scheme_themes.py (a one-off
# below for the dropdown grouping, and # content-generation script, not part of the running app) for how
# tools/generate_website_scheme_themes.py (a one-off content- # the kept ones were built: dominant colors extracted per scheme,
# generation script, not part of the running app) for how the kept # mapped into this app's bg/text/accent shape with automatic
# ones were built from that page's screenshots: dominant colors # contrast adjustment against both the background and the white
# extracted per scheme, then mapped into this app's bg/text/accent # button text, then deduplicated against each other by actual
# shape with automatic contrast adjustment against both the # color distance (not eyeballed) once everything converged to dark.
# background and the white button text. 'ink_wash', 'jade_pebble_morning', 'woodland', 'graphite', 'pearl', 'yacht_club',
'ink_wash', 'jade_pebble_morning', 'woodland', 'graphite', 'urban_slate', 'pearl', 'amber_walnut_morning', 'copper_aquamarine_dream', 'sandstone_aquamarine_serenity',
'vichy', 'sorbet', 'yacht_club', 'amber_walnut_morning', 'copper_aquamarine_dream', 'honey_opal_sunset', 'seashell_garnet_afternoon', 'rose_quartz_evening', 'calcite',
'cocoa_topaz_noonday', 'sandstone_aquamarine_serenity', 'honey_opal_sunset', 'fireside', 'terrazzo', 'sapphire_nightfall_whisper', 'marina',
'seashell_garnet_afternoon', 'rose_quartz_evening', 'calcite', 'fireside', 'terrazzo', 'emerald_lavender_lake', 'sage_peridot_morning', 'amethyst_dawn_haze', 'royal_glimmer',
'sapphire_nightfall_whisper', 'marina', 'emerald_lavender_lake', 'neptune', 'tropical_heat', 'celestial', 'festive_eve', 'freshly_squeezed',
'sage_peridot_morning', 'amethyst_dawn_haze', 'turquoise_amber_autumn', 'jelly_shoes', 'lemon_granite_morning', 'arctic_reflection', 'slate', 'autumn_luxe',
'sapphire_ash_morning', 'royal_glimmer', 'neptune', 'tropical_heat', 'celestial', 'inked', 'wraith', 'urban_nocturne',
'festive_eve', 'freshly_squeezed', 'jelly_shoes', 'gossamer', 'clockwork',
'lemon_granite_morning', 'arctic_reflection', 'slate', 'autumn_luxe', 'inked',
'wraith', 'urban_nocturne',
}, },
'font_family': {'system', 'sans', 'serif', 'monospace', 'monaspace'}, 'font_family': {'system', 'sans', 'serif', 'monospace', 'monaspace'},
'font_size': {'small', 'medium', 'large', 'xlarge'}, 'font_size': {'small', 'medium', 'large', 'xlarge'},
@@ -144,14 +141,10 @@ THEME_DISPLAY_NAMES = {
'jade_pebble_morning': 'Jade Pebble Morning', 'jade_pebble_morning': 'Jade Pebble Morning',
'woodland': 'Woodland', 'woodland': 'Woodland',
'graphite': 'Graphite', 'graphite': 'Graphite',
'urban_slate': 'Urban Slate',
'pearl': 'Pearl', 'pearl': 'Pearl',
'vichy': 'Vichy',
'sorbet': 'Sorbet',
'yacht_club': 'Yacht Club', 'yacht_club': 'Yacht Club',
'amber_walnut_morning': 'Amber Walnut Morning', 'amber_walnut_morning': 'Amber Walnut Morning',
'copper_aquamarine_dream': 'Copper Aquamarine Dream', 'copper_aquamarine_dream': 'Copper Aquamarine Dream',
'cocoa_topaz_noonday': 'Cocoa Topaz Noonday',
'sandstone_aquamarine_serenity': 'Sandstone Aquamarine Serenity', 'sandstone_aquamarine_serenity': 'Sandstone Aquamarine Serenity',
'honey_opal_sunset': 'Honey Opal Sunset', 'honey_opal_sunset': 'Honey Opal Sunset',
'seashell_garnet_afternoon': 'Seashell Garnet Afternoon', 'seashell_garnet_afternoon': 'Seashell Garnet Afternoon',
@@ -164,8 +157,6 @@ THEME_DISPLAY_NAMES = {
'emerald_lavender_lake': 'Emerald Lavender Lake', 'emerald_lavender_lake': 'Emerald Lavender Lake',
'sage_peridot_morning': 'Sage Peridot Morning', 'sage_peridot_morning': 'Sage Peridot Morning',
'amethyst_dawn_haze': 'Amethyst Dawn Haze', 'amethyst_dawn_haze': 'Amethyst Dawn Haze',
'turquoise_amber_autumn': 'Turquoise Amber Autumn',
'sapphire_ash_morning': 'Sapphire Ash Morning',
'royal_glimmer': 'Royal Glimmer', 'royal_glimmer': 'Royal Glimmer',
'neptune': 'Neptune', 'neptune': 'Neptune',
'tropical_heat': 'Tropical Heat', 'tropical_heat': 'Tropical Heat',
@@ -173,8 +164,6 @@ THEME_DISPLAY_NAMES = {
'festive_eve': 'Festive Eve', 'festive_eve': 'Festive Eve',
'freshly_squeezed': 'Freshly Squeezed', 'freshly_squeezed': 'Freshly Squeezed',
'jelly_shoes': 'Jelly Shoes', 'jelly_shoes': 'Jelly Shoes',
'gossamer': 'Gossamer',
'clockwork': 'Clockwork',
'lemon_granite_morning': 'Lemon Granite Morning', 'lemon_granite_morning': 'Lemon Granite Morning',
'arctic_reflection': 'Arctic Reflection', 'arctic_reflection': 'Arctic Reflection',
'slate': 'Slate', 'slate': 'Slate',
@@ -260,16 +249,16 @@ THEME_PALETTES = {
'bg-tertiary-hover': '#3c4850', 'text-primary': '#d1ded3', 'text-muted': '#7c8885', 'bg-tertiary-hover': '#3c4850', 'text-primary': '#d1ded3', 'text-muted': '#7c8885',
'border-color': '#707c4f', 'accent': '#a4b07e', 'accent-hover': '#8b966b', 'border-color': '#707c4f', 'accent': '#a4b07e', 'accent-hover': '#8b966b',
}, },
# --- Minimal & Neutral (from Figma's "53 Unique Website Color Schemes") --- # --- Minimal & Neutral (from Figma's "53 Unique Website Color Schemes", converted to dark backgrounds) ---
'ink_wash': { 'ink_wash': {
'bg-primary': '#202020', 'bg-secondary': '#323232', 'bg-tertiary': '#444444', 'bg-primary': '#202020', 'bg-secondary': '#323232', 'bg-tertiary': '#444444',
'bg-tertiary-hover': '#4b4b4b', 'text-primary': '#e6e6e6', 'text-muted': '#9e9e9e', 'bg-tertiary-hover': '#4b4b4b', 'text-primary': '#e6e6e6', 'text-muted': '#9e9e9e',
'border-color': '#494949', 'accent': '#2990d6', 'accent-hover': '#2173ab', 'border-color': '#494949', 'accent': '#2990d6', 'accent-hover': '#2173ab',
}, },
'jade_pebble_morning': { 'jade_pebble_morning': {
'bg-primary': '#f2f4f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#edefeb', 'bg-primary': '#242d24', 'bg-secondary': '#344134', 'bg-tertiary': '#445544',
'bg-tertiary-hover': '#e0e5dc', 'text-primary': '#242522', 'text-muted': '#6b7066', 'bg-tertiary-hover': '#4b5e4b', 'text-primary': '#e5e6e5', 'text-muted': '#9ba19b',
'border-color': '#d1d6cd', 'accent': '#659539', 'accent-hover': '#4c702b', 'border-color': '#495b49', 'accent': '#9dca73', 'accent-hover': '#83bc4e',
}, },
'woodland': { 'woodland': {
'bg-primary': '#323220', 'bg-secondary': '#48482e', 'bg-tertiary': '#5d5d3c', 'bg-primary': '#323220', 'bg-secondary': '#48482e', 'bg-tertiary': '#5d5d3c',
@@ -277,50 +266,30 @@ THEME_PALETTES = {
'border-color': '#646440', 'accent': '#acc927', 'accent-hover': '#879e1f', 'border-color': '#646440', 'accent': '#acc927', 'accent-hover': '#879e1f',
}, },
'graphite': { 'graphite': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb', 'bg-primary': '#2c2c26', 'bg-secondary': '#3f3f37', 'bg-tertiary': '#525247',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666', 'bg-tertiary-hover': '#5a5a4f', 'text-primary': '#e6e6e5', 'text-muted': '#a0a09c',
'border-color': '#d6cdcd', 'accent': '#d34646', 'accent-hover': '#ba2c2c', 'border-color': '#57574c', 'accent': '#e07b7b', 'accent-hover': '#d65252',
},
'urban_slate': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666',
'border-color': '#d6cdcd', 'accent': '#2174ab', 'accent-hover': '#195780',
}, },
'pearl': { 'pearl': {
'bg-primary': '#f2f1f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ecebef', 'bg-primary': '#292929', 'bg-secondary': '#3b3b3b', 'bg-tertiary': '#4d4d4d',
'bg-tertiary-hover': '#dfdce5', 'text-primary': '#232225', 'text-muted': '#696670', 'bg-tertiary-hover': '#545454', 'text-primary': '#e6e6e6', 'text-muted': '#9e9e9e',
'border-color': '#d0cdd6', 'accent': '#bd7c3d', 'accent-hover': '#966331', 'border-color': '#525252', 'accent': '#d7ad84', 'accent-hover': '#ca935e',
},
'vichy': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666',
'border-color': '#d6cdcd', 'accent': '#1d968b', 'accent-hover': '#156b63',
},
'sorbet': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666',
'border-color': '#d6cdcd', 'accent': '#c07b59', 'accent-hover': '#a7613f',
}, },
'yacht_club': { 'yacht_club': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb', 'bg-primary': '#322720', 'bg-secondary': '#48382e', 'bg-tertiary': '#5d493c',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666', 'bg-tertiary-hover': '#675042', 'text-primary': '#e7e5e4', 'text-muted': '#a49d98',
'border-color': '#d6cdcd', 'accent': '#97562b', 'accent-hover': '#6f3f20', 'border-color': '#644e40', 'accent': '#c67139', 'accent-hover': '#9e5a2e',
}, },
# --- Warm (from Figma's "53 Unique Website Color Schemes") --- # --- Warm (from Figma's "53 Unique Website Color Schemes", converted to dark backgrounds) ---
'amber_walnut_morning': { 'amber_walnut_morning': {
'bg-primary': '#292929', 'bg-secondary': '#3b3b3b', 'bg-tertiary': '#4d4d4d', 'bg-primary': '#292929', 'bg-secondary': '#3b3b3b', 'bg-tertiary': '#4d4d4d',
'bg-tertiary-hover': '#545454', 'text-primary': '#e6e6e6', 'text-muted': '#9e9e9e', 'bg-tertiary-hover': '#545454', 'text-primary': '#e6e6e6', 'text-muted': '#9e9e9e',
'border-color': '#525252', 'accent': '#d09070', 'accent-hover': '#c3724a', 'border-color': '#525252', 'accent': '#d09070', 'accent-hover': '#c3724a',
}, },
'copper_aquamarine_dream': { 'copper_aquamarine_dream': {
'bg-primary': '#f4f4f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efefeb', 'bg-primary': '#202c32', 'bg-secondary': '#2e3f48', 'bg-tertiary': '#3c525d',
'bg-tertiary-hover': '#e5e5dc', 'text-primary': '#252522', 'text-muted': '#707066', 'bg-tertiary-hover': '#425a67', 'text-primary': '#e4e6e7', 'text-muted': '#98a0a4',
'border-color': '#d6d6cd', 'accent': '#bc5224', 'accent-hover': '#913f1c', 'border-color': '#405864', 'accent': '#d65d29', 'accent-hover': '#ab4a21',
},
'cocoa_topaz_noonday': {
'bg-primary': '#322620', 'bg-secondary': '#48362e', 'bg-tertiary': '#5d473c',
'bg-tertiary-hover': '#674e42', 'text-primary': '#e7e5e4', 'text-muted': '#a49c98',
'border-color': '#644c40', 'accent': '#f1a04e', 'accent-hover': '#ed871f',
}, },
'sandstone_aquamarine_serenity': { 'sandstone_aquamarine_serenity': {
'bg-primary': '#322420', 'bg-secondary': '#48342e', 'bg-tertiary': '#5d443c', 'bg-primary': '#322420', 'bg-secondary': '#48342e', 'bg-tertiary': '#5d443c',
@@ -333,9 +302,9 @@ THEME_PALETTES = {
'border-color': '#645240', 'accent': '#d6b12a', 'accent-hover': '#ac8e21', 'border-color': '#645240', 'accent': '#d6b12a', 'accent-hover': '#ac8e21',
}, },
'seashell_garnet_afternoon': { 'seashell_garnet_afternoon': {
'bg-primary': '#f1f2f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ebedef', 'bg-primary': '#203232', 'bg-secondary': '#2e4848', 'bg-tertiary': '#3c5d5d',
'bg-tertiary-hover': '#dce0e5', 'text-primary': '#222425', 'text-muted': '#666b70', 'bg-tertiary-hover': '#426767', 'text-primary': '#e4e7e7', 'text-muted': '#98a4a4',
'border-color': '#cdd1d6', 'accent': '#da3e50', 'accent-hover': '#c02537', 'border-color': '#406464', 'accent': '#f36878', 'accent-hover': '#ef394e',
}, },
'rose_quartz_evening': { 'rose_quartz_evening': {
'bg-primary': '#322024', 'bg-secondary': '#482e34', 'bg-tertiary': '#5d3c44', 'bg-primary': '#322024', 'bg-secondary': '#482e34', 'bg-tertiary': '#5d3c44',
@@ -348,102 +317,82 @@ THEME_PALETTES = {
'border-color': '#525252', 'accent': '#f1854e', 'accent-hover': '#ed641f', 'border-color': '#525252', 'accent': '#f1854e', 'accent-hover': '#ed641f',
}, },
'fireside': { 'fireside': {
'bg-primary': '#f4f2f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efedeb', 'bg-primary': '#322420', 'bg-secondary': '#48342e', 'bg-tertiary': '#5d443c',
'bg-tertiary-hover': '#e5e0dc', 'text-primary': '#252422', 'text-muted': '#706b66', 'bg-tertiary-hover': '#674b42', 'text-primary': '#e7e5e4', 'text-muted': '#a49b98',
'border-color': '#d6d1cd', 'accent': '#ca7226', 'accent-hover': '#9f5a1e', 'border-color': '#644940', 'accent': '#d67929', 'accent-hover': '#ab6121',
}, },
'terrazzo': { 'terrazzo': {
'bg-primary': '#203232', 'bg-secondary': '#2e4848', 'bg-tertiary': '#3c5d5d', 'bg-primary': '#203232', 'bg-secondary': '#2e4848', 'bg-tertiary': '#3c5d5d',
'bg-tertiary-hover': '#426767', 'text-primary': '#e4e7e7', 'text-muted': '#98a4a4', 'bg-tertiary-hover': '#426767', 'text-primary': '#e4e7e7', 'text-muted': '#98a4a4',
'border-color': '#406464', 'accent': '#ecad6f', 'accent-hover': '#e69342', 'border-color': '#406464', 'accent': '#ecad6f', 'accent-hover': '#e69342',
}, },
# --- Cool (from Figma's "53 Unique Website Color Schemes") --- # --- Cool (from Figma's "53 Unique Website Color Schemes", converted to dark backgrounds) ---
'sapphire_nightfall_whisper': { 'sapphire_nightfall_whisper': {
'bg-primary': '#202932', 'bg-secondary': '#2e3b48', 'bg-tertiary': '#3c4c5d', 'bg-primary': '#202932', 'bg-secondary': '#2e3b48', 'bg-tertiary': '#3c4c5d',
'bg-tertiary-hover': '#425467', 'text-primary': '#e4e6e7', 'text-muted': '#989ea4', 'bg-tertiary-hover': '#425467', 'text-primary': '#e4e6e7', 'text-muted': '#989ea4',
'border-color': '#405264', 'accent': '#297fd6', 'accent-hover': '#2166ab', 'border-color': '#405264', 'accent': '#297fd6', 'accent-hover': '#2166ab',
}, },
'marina': { 'marina': {
'bg-primary': '#f1f3f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ebeeef', 'bg-primary': '#322720', 'bg-secondary': '#48382e', 'bg-tertiary': '#5d493c',
'bg-tertiary-hover': '#dce2e5', 'text-primary': '#222425', 'text-muted': '#666d70', 'bg-tertiary-hover': '#675042', 'text-primary': '#e7e5e4', 'text-muted': '#a49d98',
'border-color': '#cdd3d6', 'accent': '#3e8cda', 'accent-hover': '#2572c0', 'border-color': '#644e40', 'accent': '#76ade5', 'accent-hover': '#4b93dd',
}, },
'emerald_lavender_lake': { 'emerald_lavender_lake': {
'bg-primary': '#f1f4f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ebefef', 'bg-primary': '#203228', 'bg-secondary': '#2e4839', 'bg-tertiary': '#3c5d4a',
'bg-tertiary-hover': '#dce5e5', 'text-primary': '#222525', 'text-muted': '#667070', 'bg-tertiary-hover': '#426752', 'text-primary': '#e4e7e5', 'text-muted': '#98a49d',
'border-color': '#cdd6d6', 'accent': '#239f58', 'accent-hover': '#1a7541', 'border-color': '#40644f', 'accent': '#2ed174', 'accent-hover': '#25a75d',
}, },
'sage_peridot_morning': { 'sage_peridot_morning': {
'bg-primary': '#f1f4f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ebefeb', 'bg-primary': '#203220', 'bg-secondary': '#2e482e', 'bg-tertiary': '#3c5d3c',
'bg-tertiary-hover': '#dce5dc', 'text-primary': '#222522', 'text-muted': '#667066', 'bg-tertiary-hover': '#426742', 'text-primary': '#e4e7e4', 'text-muted': '#98a498',
'border-color': '#cdd6cd', 'accent': '#1f9e83', 'accent-hover': '#177360', 'border-color': '#406440', 'accent': '#20d1af', 'accent-hover': '#19a58a',
}, },
'amethyst_dawn_haze': { 'amethyst_dawn_haze': {
'bg-primary': '#242032', 'bg-secondary': '#342e48', 'bg-tertiary': '#443c5d', 'bg-primary': '#242032', 'bg-secondary': '#342e48', 'bg-tertiary': '#443c5d',
'bg-tertiary-hover': '#4b4267', 'text-primary': '#e5e4e7', 'text-muted': '#9b98a4', 'bg-tertiary-hover': '#4b4267', 'text-primary': '#e5e4e7', 'text-muted': '#9b98a4',
'border-color': '#494064', 'accent': '#ccba27', 'accent-hover': '#a1931f', 'border-color': '#494064', 'accent': '#ccba27', 'accent-hover': '#a1931f',
}, },
'turquoise_amber_autumn': {
'bg-primary': '#322620', 'bg-secondary': '#48362e', 'bg-tertiary': '#5d473c',
'bg-tertiary-hover': '#674e42', 'text-primary': '#e7e5e4', 'text-muted': '#a49c98',
'border-color': '#644c40', 'accent': '#d66329', 'accent-hover': '#ab4f21',
},
'sapphire_ash_morning': {
'bg-primary': '#f1f2f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ebecef',
'bg-tertiary-hover': '#dcdfe5', 'text-primary': '#222325', 'text-muted': '#666970',
'border-color': '#cdd0d6', 'accent': '#356a8d', 'accent-hover': '#274e68',
},
'royal_glimmer': { 'royal_glimmer': {
'bg-primary': '#1f312d', 'bg-secondary': '#2d4741', 'bg-tertiary': '#3b5c56', 'bg-primary': '#1f312d', 'bg-secondary': '#2d4741', 'bg-tertiary': '#3b5c56',
'bg-tertiary-hover': '#41665e', 'text-primary': '#e4e7e7', 'text-muted': '#98a4a2', 'bg-tertiary-hover': '#41665e', 'text-primary': '#e4e7e7', 'text-muted': '#98a4a2',
'border-color': '#3f635b', 'accent': '#29d6b4', 'accent-hover': '#21ab90', 'border-color': '#3f635b', 'accent': '#29d6b4', 'accent-hover': '#21ab90',
}, },
'neptune': { 'neptune': {
'bg-primary': '#f1f3f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ebeeef', 'bg-primary': '#203232', 'bg-secondary': '#2e4848', 'bg-tertiary': '#3c5d5d',
'bg-tertiary-hover': '#dce2e5', 'text-primary': '#222425', 'text-muted': '#666d70', 'bg-tertiary-hover': '#426767', 'text-primary': '#e4e7e7', 'text-muted': '#98a4a4',
'border-color': '#cdd3d6', 'accent': '#248fb8', 'accent-hover': '#1c6e8d', 'border-color': '#406464', 'accent': '#68ccf3', 'accent-hover': '#39bcef',
}, },
# --- Vibrant & Bold (from Figma's "53 Unique Website Color Schemes") --- # --- Vibrant & Bold (from Figma's "53 Unique Website Color Schemes", converted to dark backgrounds) ---
'tropical_heat': { 'tropical_heat': {
'bg-primary': '#f4f3f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efeeeb', 'bg-primary': '#203232', 'bg-secondary': '#2e4848', 'bg-tertiary': '#3c5d5d',
'bg-tertiary-hover': '#e5e3dc', 'text-primary': '#252522', 'text-muted': '#706e66', 'bg-tertiary-hover': '#426767', 'text-primary': '#e4e7e7', 'text-muted': '#98a4a4',
'border-color': '#d6d4cd', 'accent': '#ca5226', 'accent-hover': '#9f411e', 'border-color': '#406464', 'accent': '#d65729', 'accent-hover': '#ab4621',
}, },
'celestial': { 'celestial': {
'bg-primary': '#f4f4f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efefeb', 'bg-primary': '#323220', 'bg-secondary': '#48482e', 'bg-tertiary': '#5d5d3c',
'bg-tertiary-hover': '#e5e4dc', 'text-primary': '#252522', 'text-muted': '#707066', 'bg-tertiary-hover': '#676742', 'text-primary': '#e7e7e4', 'text-muted': '#a4a498',
'border-color': '#d6d5cd', 'accent': '#918b1c', 'accent-hover': '#666214', 'border-color': '#646440', 'accent': '#cbc026', 'accent-hover': '#a0971e',
}, },
'festive_eve': { 'festive_eve': {
'bg-primary': '#f2f1f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ecebef', 'bg-primary': '#202b32', 'bg-secondary': '#2e3e48', 'bg-tertiary': '#3c515d',
'bg-tertiary-hover': '#dfdce5', 'text-primary': '#232225', 'text-muted': '#696670', 'bg-tertiary-hover': '#425a67', 'text-primary': '#e4e6e7', 'text-muted': '#98a0a4',
'border-color': '#d0cdd6', 'accent': '#733eda', 'accent-hover': '#5a25c0', 'border-color': '#405764', 'accent': '#8f5cf2', 'accent-hover': '#6f2dee',
}, },
'freshly_squeezed': { 'freshly_squeezed': {
'bg-primary': '#f4f3f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efeeeb', 'bg-primary': '#322d20', 'bg-secondary': '#48412e', 'bg-tertiary': '#5d553c',
'bg-tertiary-hover': '#e5e2dc', 'text-primary': '#252422', 'text-muted': '#706d66', 'bg-tertiary-hover': '#675e42', 'text-primary': '#e7e6e4', 'text-muted': '#a4a198',
'border-color': '#d6d3cd', 'accent': '#a28320', 'accent-hover': '#776118', 'border-color': '#645b40', 'accent': '#d6ab29', 'accent-hover': '#ab8921',
}, },
'jelly_shoes': { 'jelly_shoes': {
'bg-primary': '#f3f1f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#eeebef', 'bg-primary': '#242032', 'bg-secondary': '#342e48', 'bg-tertiary': '#443c5d',
'bg-tertiary-hover': '#e1dce5', 'text-primary': '#242225', 'text-muted': '#6c6670', 'bg-tertiary-hover': '#4b4267', 'text-primary': '#e5e4e7', 'text-muted': '#9b98a4',
'border-color': '#d2cdd6', 'accent': '#9d3eda', 'accent-hover': '#8325c0', 'border-color': '#494064', 'accent': '#bc68f3', 'accent-hover': '#a739ef',
},
# --- Modern (from Figma's "53 Unique Website Color Schemes") ---
'gossamer': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666',
'border-color': '#d6cdcd', 'accent': '#d95c37', 'accent-hover': '#ba4623',
},
'clockwork': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666',
'border-color': '#d6cdcd', 'accent': '#d26c28', 'accent-hover': '#a75620',
}, },
# --- Modern (from Figma's "53 Unique Website Color Schemes", converted to dark backgrounds) ---
'lemon_granite_morning': { 'lemon_granite_morning': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb', 'bg-primary': '#202c32', 'bg-secondary': '#2e3f48', 'bg-tertiary': '#3c525d',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666', 'bg-tertiary-hover': '#425a67', 'text-primary': '#e4e6e7', 'text-muted': '#98a0a4',
'border-color': '#d6cdcd', 'accent': '#89831b', 'accent-hover': '#5e5a13', 'border-color': '#405864', 'accent': '#c9bf27', 'accent-hover': '#9e961f',
}, },
'arctic_reflection': { 'arctic_reflection': {
'bg-primary': '#202c32', 'bg-secondary': '#2e3f48', 'bg-tertiary': '#3c525d', 'bg-primary': '#202c32', 'bg-secondary': '#2e3f48', 'bg-tertiary': '#3c525d',
@@ -451,9 +400,9 @@ THEME_PALETTES = {
'border-color': '#405864', 'accent': '#4693b9', 'accent-hover': '#387694', 'border-color': '#405864', 'accent': '#4693b9', 'accent-hover': '#387694',
}, },
'slate': { 'slate': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb', 'bg-primary': '#262c26', 'bg-secondary': '#373f37', 'bg-tertiary': '#475247',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666', 'bg-tertiary-hover': '#4f5a4f', 'text-primary': '#e5e6e5', 'text-muted': '#9ca09c',
'border-color': '#d6cdcd', 'accent': '#1f9e31', 'accent-hover': '#177324', 'border-color': '#4c574c', 'accent': '#1ad236', 'accent-hover': '#14a52a',
}, },
'autumn_luxe': { 'autumn_luxe': {
'bg-primary': '#303020', 'bg-secondary': '#45452e', 'bg-tertiary': '#5b5b3d', 'bg-primary': '#303020', 'bg-secondary': '#45452e', 'bg-tertiary': '#5b5b3d',
@@ -482,11 +431,11 @@ THEME_PALETTES = {
# neutral / Warm / Cool / Vibrant and bold / Modern" section headings, so # neutral / Warm / Cool / Vibrant and bold / Modern" section headings, so
# browsing the dropdown still mirrors how the original schemes were curated. # browsing the dropdown still mirrors how the original schemes were curated.
WEBSITE_SCHEME_CATEGORIES = { WEBSITE_SCHEME_CATEGORIES = {
'Minimal & Neutral': ['ink_wash', 'jade_pebble_morning', 'woodland', 'graphite', 'urban_slate', 'pearl', 'vichy', 'sorbet', 'yacht_club'], 'Minimal & Neutral': ['ink_wash', 'jade_pebble_morning', 'woodland', 'graphite', 'pearl', 'yacht_club'],
'Warm': ['amber_walnut_morning', 'copper_aquamarine_dream', 'cocoa_topaz_noonday', 'sandstone_aquamarine_serenity', 'honey_opal_sunset', 'seashell_garnet_afternoon', 'rose_quartz_evening', 'calcite', 'fireside', 'terrazzo'], 'Warm': ['amber_walnut_morning', 'copper_aquamarine_dream', 'sandstone_aquamarine_serenity', 'honey_opal_sunset', 'seashell_garnet_afternoon', 'rose_quartz_evening', 'calcite', 'fireside', 'terrazzo'],
'Cool': ['sapphire_nightfall_whisper', 'marina', 'emerald_lavender_lake', 'sage_peridot_morning', 'amethyst_dawn_haze', 'turquoise_amber_autumn', 'sapphire_ash_morning', 'royal_glimmer', 'neptune'], 'Cool': ['sapphire_nightfall_whisper', 'marina', 'emerald_lavender_lake', 'sage_peridot_morning', 'amethyst_dawn_haze', 'royal_glimmer', 'neptune'],
'Vibrant & Bold': ['tropical_heat', 'celestial', 'festive_eve', 'freshly_squeezed', 'jelly_shoes'], 'Vibrant & Bold': ['tropical_heat', 'celestial', 'festive_eve', 'freshly_squeezed', 'jelly_shoes'],
'Modern': ['gossamer', 'clockwork', 'lemon_granite_morning', 'arctic_reflection', 'slate', 'autumn_luxe', 'inked', 'wraith', 'urban_nocturne'], 'Modern': ['lemon_granite_morning', 'arctic_reflection', 'slate', 'autumn_luxe', 'inked', 'wraith', 'urban_nocturne'],
} }
# How each choice resolves to an actual CSS value. Keeping this server-side # How each choice resolves to an actual CSS value. Keeping this server-side
+33 -14
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 THEME_PALETTES/THEME_DISPLAY_NAMES (offlineu_core.py), sourced from Figma's
"53 Unique Website Color Schemes" resource page: "53 Unique Website Color Schemes" resource page:
https://www.figma.com/resource-library/website-color-schemes/ 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 screenshot, not a swatch grid - so website_scheme_swatches.json (checked in
alongside this script) holds dominant colors already extracted from those alongside this script) holds dominant colors already extracted from those
53 images via canvas pixel-histogram sampling in a browser, one entry per 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 material into an actual UI palette per scheme (bg/text/accent, matching
THEME_PALETTES' shape), then nudges lightness as needed so every result THEME_PALETTES' shape), then nudges lightness/saturation as needed so every
clears the same contrast bars this file's checks apply: text-vs-background, result clears the same contrast bars this file's checks apply:
accent-vs-background, and accent-vs-white (buttons always use white text - text-vs-background, accent-vs-background, and accent-vs-white (buttons
see .btn in course_dashboard.html - so a too-bright accent needs catching always use white text - see .btn in course_dashboard.html - so a too-bright
even when it reads fine against the background alone). 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 Running this file directly (see __main__ below) auto-detects each scheme's
from the same frozen swatch data. To add more schemes, extract their own mood from its swatches and writes all 53 as candidates - that's NOT
dominant colors the same way (see the canvas-sampling approach used what's actually shipped. The source material is mostly light-mode
in-session; not scripted here) and append to the JSON, then rerun and marketing mockups, so the first pass over-represented near-white
splice the output into offlineu_core.py by hand. 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 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 colorsys
import json import json
@@ -93,7 +107,7 @@ def tame_accent_saturation(s, l):
return min(s, 0.85) return min(s, 0.85)
def build_theme(entry): def build_theme(entry, force_mood=None):
name = entry['name'] name = entry['name']
swatches = entry['colors'] swatches = entry['colors']
parsed = [] parsed = []
@@ -107,6 +121,11 @@ def build_theme(entry):
if not content: if not content:
content = parsed content = parsed
if force_mood == 'dark':
is_dark = True
elif force_mood == 'light':
is_dark = False
else:
darkest_l = min(p['l'] for p in content) darkest_l = min(p['l'] for p in content)
is_dark = darkest_l < 0.28 is_dark = darkest_l < 0.28