Add 42 curated themes from Figma's website color schemes

Sourced from Figma's "53 Unique Website Color Schemes" resource page,
which only shows rendered mockup screenshots (no raw hex data) - extracted
dominant colors via canvas pixel sampling, then a small pipeline
(tools/generate_website_scheme_themes.py) maps each scheme's real colors
into this app's palette shape, with contrast enforced against both the
background and the white text every .btn uses.

Cut 11 of the original 53 after review: 2 for a neon-bright accent (the
saturation cap only bounded lightness at first, so ~28 of 53 accents
landed at pure 100% saturation - fixed at the pipeline level, calibrated
against what the existing hand-picked themes actually use), and 9 for
being near-duplicates of another kept scheme once mapped into actual UI
colors (measured by real color distance, not eyeballed) - including one
pixel-identical pair and a 4-way cluster trimmed to its two most distinct
members.

Settings dropdown is grouped into <optgroup>s (Base, Editor Themes, and
5 categories matching the source page's own curation) rather than one
flat 54-option list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-24 08:08:56 -04:00
co-authored by Claude Sonnet 5
parent acabb89981
commit 722b381913
5 changed files with 563 additions and 6 deletions
+302 -3
View File
@@ -100,6 +100,28 @@ SETTINGS_CHOICES = {
'theme': {
'dark', 'light', 'dracula', 'tokyo_night', 'catppuccin_mocha', 'ayu_dark',
'github_dark', 'atom_one_dark', 'houston', 'night_owl', 'nord', 'matcha',
# 42 palettes derived from Figma's "53 Unique Website Color Schemes"
# resource page (11 of the 53 dropped: 2 for a neon-bright accent
# that read badly as a button color, the rest for being too close
# to another scheme already kept - same accent hue/lightness and
# background, just a different name) - see WEBSITE_SCHEME_CATEGORIES
# below for the dropdown grouping, and
# tools/generate_website_scheme_themes.py (a one-off content-
# generation script, not part of the running app) for how the kept
# ones were built from that page's screenshots: dominant colors
# extracted per scheme, then mapped into this app's bg/text/accent
# shape with automatic contrast adjustment against both the
# background and the white button text.
'ink_wash', 'jade_pebble_morning', 'woodland', 'graphite', 'urban_slate', 'pearl',
'vichy', 'sorbet', 'yacht_club', 'amber_walnut_morning', 'copper_aquamarine_dream',
'cocoa_topaz_noonday', 'sandstone_aquamarine_serenity', 'honey_opal_sunset',
'seashell_garnet_afternoon', 'rose_quartz_evening', 'calcite', 'fireside', 'terrazzo',
'sapphire_nightfall_whisper', 'marina', 'emerald_lavender_lake',
'sage_peridot_morning', 'amethyst_dawn_haze', 'turquoise_amber_autumn',
'sapphire_ash_morning', 'royal_glimmer', 'neptune', 'tropical_heat', 'celestial',
'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_size': {'small', 'medium', 'large', 'xlarge'},
@@ -118,6 +140,48 @@ THEME_DISPLAY_NAMES = {
'ayu_dark': 'Ayu Dark', 'github_dark': 'GitHub Dark',
'atom_one_dark': 'Atom One Dark', 'houston': 'Houston',
'night_owl': 'Night Owl', 'nord': 'Nord', 'matcha': 'Matcha',
'ink_wash': 'Ink Wash',
'jade_pebble_morning': 'Jade Pebble Morning',
'woodland': 'Woodland',
'graphite': 'Graphite',
'urban_slate': 'Urban Slate',
'pearl': 'Pearl',
'vichy': 'Vichy',
'sorbet': 'Sorbet',
'yacht_club': 'Yacht Club',
'amber_walnut_morning': 'Amber Walnut Morning',
'copper_aquamarine_dream': 'Copper Aquamarine Dream',
'cocoa_topaz_noonday': 'Cocoa Topaz Noonday',
'sandstone_aquamarine_serenity': 'Sandstone Aquamarine Serenity',
'honey_opal_sunset': 'Honey Opal Sunset',
'seashell_garnet_afternoon': 'Seashell Garnet Afternoon',
'rose_quartz_evening': 'Rose Quartz Evening',
'calcite': 'Calcite',
'fireside': 'Fireside',
'terrazzo': 'Terrazzo',
'sapphire_nightfall_whisper': 'Sapphire Nightfall Whisper',
'marina': 'Marina',
'emerald_lavender_lake': 'Emerald Lavender Lake',
'sage_peridot_morning': 'Sage Peridot Morning',
'amethyst_dawn_haze': 'Amethyst Dawn Haze',
'turquoise_amber_autumn': 'Turquoise Amber Autumn',
'sapphire_ash_morning': 'Sapphire Ash Morning',
'royal_glimmer': 'Royal Glimmer',
'neptune': 'Neptune',
'tropical_heat': 'Tropical Heat',
'celestial': 'Celestial',
'festive_eve': 'Festive Eve',
'freshly_squeezed': 'Freshly Squeezed',
'jelly_shoes': 'Jelly Shoes',
'gossamer': 'Gossamer',
'clockwork': 'Clockwork',
'lemon_granite_morning': 'Lemon Granite Morning',
'arctic_reflection': 'Arctic Reflection',
'slate': 'Slate',
'autumn_luxe': 'Autumn Luxe',
'inked': 'Inked',
'wraith': 'Wraith',
'urban_nocturne': 'Urban Nocturne',
}
# Full color palette per theme. 'accent' here is only the *default* accent
@@ -196,6 +260,233 @@ THEME_PALETTES = {
'bg-tertiary-hover': '#3c4850', 'text-primary': '#d1ded3', 'text-muted': '#7c8885',
'border-color': '#707c4f', 'accent': '#a4b07e', 'accent-hover': '#8b966b',
},
# --- Minimal & Neutral (from Figma's "53 Unique Website Color Schemes") ---
'ink_wash': {
'bg-primary': '#202020', 'bg-secondary': '#323232', 'bg-tertiary': '#444444',
'bg-tertiary-hover': '#4b4b4b', 'text-primary': '#e6e6e6', 'text-muted': '#9e9e9e',
'border-color': '#494949', 'accent': '#2990d6', 'accent-hover': '#2173ab',
},
'jade_pebble_morning': {
'bg-primary': '#f2f4f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#edefeb',
'bg-tertiary-hover': '#e0e5dc', 'text-primary': '#242522', 'text-muted': '#6b7066',
'border-color': '#d1d6cd', 'accent': '#659539', 'accent-hover': '#4c702b',
},
'woodland': {
'bg-primary': '#323220', 'bg-secondary': '#48482e', 'bg-tertiary': '#5d5d3c',
'bg-tertiary-hover': '#676742', 'text-primary': '#e7e7e4', 'text-muted': '#a4a498',
'border-color': '#646440', 'accent': '#acc927', 'accent-hover': '#879e1f',
},
'graphite': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666',
'border-color': '#d6cdcd', 'accent': '#d34646', 'accent-hover': '#ba2c2c',
},
'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': {
'bg-primary': '#f2f1f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ecebef',
'bg-tertiary-hover': '#dfdce5', 'text-primary': '#232225', 'text-muted': '#696670',
'border-color': '#d0cdd6', 'accent': '#bd7c3d', 'accent-hover': '#966331',
},
'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': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666',
'border-color': '#d6cdcd', 'accent': '#97562b', 'accent-hover': '#6f3f20',
},
# --- Warm (from Figma's "53 Unique Website Color Schemes") ---
'amber_walnut_morning': {
'bg-primary': '#292929', 'bg-secondary': '#3b3b3b', 'bg-tertiary': '#4d4d4d',
'bg-tertiary-hover': '#545454', 'text-primary': '#e6e6e6', 'text-muted': '#9e9e9e',
'border-color': '#525252', 'accent': '#d09070', 'accent-hover': '#c3724a',
},
'copper_aquamarine_dream': {
'bg-primary': '#f4f4f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efefeb',
'bg-tertiary-hover': '#e5e5dc', 'text-primary': '#252522', 'text-muted': '#707066',
'border-color': '#d6d6cd', 'accent': '#bc5224', 'accent-hover': '#913f1c',
},
'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': {
'bg-primary': '#322420', 'bg-secondary': '#48342e', 'bg-tertiary': '#5d443c',
'bg-tertiary-hover': '#674b42', 'text-primary': '#e7e5e4', 'text-muted': '#a49b98',
'border-color': '#644940', 'accent': '#76c9e5', 'accent-hover': '#4bb8dd',
},
'honey_opal_sunset': {
'bg-primary': '#322920', 'bg-secondary': '#483b2e', 'bg-tertiary': '#5d4d3c',
'bg-tertiary-hover': '#675442', 'text-primary': '#e7e6e4', 'text-muted': '#a49e98',
'border-color': '#645240', 'accent': '#d6b12a', 'accent-hover': '#ac8e21',
},
'seashell_garnet_afternoon': {
'bg-primary': '#f1f2f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ebedef',
'bg-tertiary-hover': '#dce0e5', 'text-primary': '#222425', 'text-muted': '#666b70',
'border-color': '#cdd1d6', 'accent': '#da3e50', 'accent-hover': '#c02537',
},
'rose_quartz_evening': {
'bg-primary': '#322024', 'bg-secondary': '#482e34', 'bg-tertiary': '#5d3c44',
'bg-tertiary-hover': '#67424b', 'text-primary': '#e7e4e5', 'text-muted': '#a4989b',
'border-color': '#644049', 'accent': '#f36868', 'accent-hover': '#ef3939',
},
'calcite': {
'bg-primary': '#292929', 'bg-secondary': '#3b3b3b', 'bg-tertiary': '#4d4d4d',
'bg-tertiary-hover': '#545454', 'text-primary': '#e6e6e6', 'text-muted': '#9e9e9e',
'border-color': '#525252', 'accent': '#f1854e', 'accent-hover': '#ed641f',
},
'fireside': {
'bg-primary': '#f4f2f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efedeb',
'bg-tertiary-hover': '#e5e0dc', 'text-primary': '#252422', 'text-muted': '#706b66',
'border-color': '#d6d1cd', 'accent': '#ca7226', 'accent-hover': '#9f5a1e',
},
'terrazzo': {
'bg-primary': '#203232', 'bg-secondary': '#2e4848', 'bg-tertiary': '#3c5d5d',
'bg-tertiary-hover': '#426767', 'text-primary': '#e4e7e7', 'text-muted': '#98a4a4',
'border-color': '#406464', 'accent': '#ecad6f', 'accent-hover': '#e69342',
},
# --- Cool (from Figma's "53 Unique Website Color Schemes") ---
'sapphire_nightfall_whisper': {
'bg-primary': '#202932', 'bg-secondary': '#2e3b48', 'bg-tertiary': '#3c4c5d',
'bg-tertiary-hover': '#425467', 'text-primary': '#e4e6e7', 'text-muted': '#989ea4',
'border-color': '#405264', 'accent': '#297fd6', 'accent-hover': '#2166ab',
},
'marina': {
'bg-primary': '#f1f3f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ebeeef',
'bg-tertiary-hover': '#dce2e5', 'text-primary': '#222425', 'text-muted': '#666d70',
'border-color': '#cdd3d6', 'accent': '#3e8cda', 'accent-hover': '#2572c0',
},
'emerald_lavender_lake': {
'bg-primary': '#f1f4f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ebefef',
'bg-tertiary-hover': '#dce5e5', 'text-primary': '#222525', 'text-muted': '#667070',
'border-color': '#cdd6d6', 'accent': '#239f58', 'accent-hover': '#1a7541',
},
'sage_peridot_morning': {
'bg-primary': '#f1f4f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ebefeb',
'bg-tertiary-hover': '#dce5dc', 'text-primary': '#222522', 'text-muted': '#667066',
'border-color': '#cdd6cd', 'accent': '#1f9e83', 'accent-hover': '#177360',
},
'amethyst_dawn_haze': {
'bg-primary': '#242032', 'bg-secondary': '#342e48', 'bg-tertiary': '#443c5d',
'bg-tertiary-hover': '#4b4267', 'text-primary': '#e5e4e7', 'text-muted': '#9b98a4',
'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': {
'bg-primary': '#1f312d', 'bg-secondary': '#2d4741', 'bg-tertiary': '#3b5c56',
'bg-tertiary-hover': '#41665e', 'text-primary': '#e4e7e7', 'text-muted': '#98a4a2',
'border-color': '#3f635b', 'accent': '#29d6b4', 'accent-hover': '#21ab90',
},
'neptune': {
'bg-primary': '#f1f3f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ebeeef',
'bg-tertiary-hover': '#dce2e5', 'text-primary': '#222425', 'text-muted': '#666d70',
'border-color': '#cdd3d6', 'accent': '#248fb8', 'accent-hover': '#1c6e8d',
},
# --- Vibrant & Bold (from Figma's "53 Unique Website Color Schemes") ---
'tropical_heat': {
'bg-primary': '#f4f3f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efeeeb',
'bg-tertiary-hover': '#e5e3dc', 'text-primary': '#252522', 'text-muted': '#706e66',
'border-color': '#d6d4cd', 'accent': '#ca5226', 'accent-hover': '#9f411e',
},
'celestial': {
'bg-primary': '#f4f4f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efefeb',
'bg-tertiary-hover': '#e5e4dc', 'text-primary': '#252522', 'text-muted': '#707066',
'border-color': '#d6d5cd', 'accent': '#918b1c', 'accent-hover': '#666214',
},
'festive_eve': {
'bg-primary': '#f2f1f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#ecebef',
'bg-tertiary-hover': '#dfdce5', 'text-primary': '#232225', 'text-muted': '#696670',
'border-color': '#d0cdd6', 'accent': '#733eda', 'accent-hover': '#5a25c0',
},
'freshly_squeezed': {
'bg-primary': '#f4f3f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efeeeb',
'bg-tertiary-hover': '#e5e2dc', 'text-primary': '#252422', 'text-muted': '#706d66',
'border-color': '#d6d3cd', 'accent': '#a28320', 'accent-hover': '#776118',
},
'jelly_shoes': {
'bg-primary': '#f3f1f4', 'bg-secondary': '#ffffff', 'bg-tertiary': '#eeebef',
'bg-tertiary-hover': '#e1dce5', 'text-primary': '#242225', 'text-muted': '#6c6670',
'border-color': '#d2cdd6', 'accent': '#9d3eda', 'accent-hover': '#8325c0',
},
# --- 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',
},
'lemon_granite_morning': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666',
'border-color': '#d6cdcd', 'accent': '#89831b', 'accent-hover': '#5e5a13',
},
'arctic_reflection': {
'bg-primary': '#202c32', 'bg-secondary': '#2e3f48', 'bg-tertiary': '#3c525d',
'bg-tertiary-hover': '#425a67', 'text-primary': '#e4e6e7', 'text-muted': '#98a0a4',
'border-color': '#405864', 'accent': '#4693b9', 'accent-hover': '#387694',
},
'slate': {
'bg-primary': '#f4f1f1', 'bg-secondary': '#ffffff', 'bg-tertiary': '#efebeb',
'bg-tertiary-hover': '#e5dcdc', 'text-primary': '#252222', 'text-muted': '#706666',
'border-color': '#d6cdcd', 'accent': '#1f9e31', 'accent-hover': '#177324',
},
'autumn_luxe': {
'bg-primary': '#303020', 'bg-secondary': '#45452e', 'bg-tertiary': '#5b5b3d',
'bg-tertiary-hover': '#646443', 'text-primary': '#e7e7e4', 'text-muted': '#a4a498',
'border-color': '#616141', 'accent': '#c08040', 'accent-hover': '#9a6633',
},
'inked': {
'bg-primary': '#171717', 'bg-secondary': '#292929', 'bg-tertiary': '#3b3b3b',
'bg-tertiary-hover': '#424242', 'text-primary': '#e6e6e6', 'text-muted': '#9e9e9e',
'border-color': '#404040', 'accent': '#27c9c9', 'accent-hover': '#1f9e9e',
},
'wraith': {
'bg-primary': '#1c1c12', 'bg-secondary': '#323220', 'bg-tertiary': '#48482e',
'bg-tertiary-hover': '#515134', 'text-primary': '#e7e7e4', 'text-muted': '#a4a498',
'border-color': '#4e4e32', 'accent': '#d69c29', 'accent-hover': '#ab7d21',
},
'urban_nocturne': {
'bg-primary': '#171717', 'bg-secondary': '#292929', 'bg-tertiary': '#3b3b3b',
'bg-tertiary-hover': '#424242', 'text-primary': '#e6e6e6', 'text-muted': '#9e9e9e',
'border-color': '#404040', 'accent': '#bfc927', 'accent-hover': '#969e1f',
},
}
# Groups the 53 Figma-derived theme keys above for the Settings dropdown
# (rendered as <optgroup>s) - matches that source page's own "Minimal and
# neutral / Warm / Cool / Vibrant and bold / Modern" section headings, so
# browsing the dropdown still mirrors how the original schemes were curated.
WEBSITE_SCHEME_CATEGORIES = {
'Minimal & Neutral': ['ink_wash', 'jade_pebble_morning', 'woodland', 'graphite', 'urban_slate', 'pearl', 'vichy', 'sorbet', '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'],
'Cool': ['sapphire_nightfall_whisper', 'marina', 'emerald_lavender_lake', 'sage_peridot_morning', 'amethyst_dawn_haze', 'turquoise_amber_autumn', 'sapphire_ash_morning', 'royal_glimmer', 'neptune'],
'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'],
}
# How each choice resolves to an actual CSS value. Keeping this server-side
@@ -2514,15 +2805,23 @@ def bulk_rename_apply_api():
def settings_page():
"""Render the display-settings page."""
current = load_settings()
named_presets = sorted(
(t for t in SETTINGS_CHOICES['theme'] if t not in ('dark', 'light')),
# Grouped for the dropdown as <optgroup>s: the two base themes, the
# original hand-picked editor-inspired presets, then the Figma-derived
# website-scheme themes under their own source-page categories (see
# WEBSITE_SCHEME_CATEGORIES) - a flat 65-option list would be unusable.
website_scheme_keys = {k for keys in WEBSITE_SCHEME_CATEGORIES.values() for k in keys}
editor_presets = sorted(
SETTINGS_CHOICES['theme'] - {'dark', 'light'} - website_scheme_keys,
key=lambda t: THEME_DISPLAY_NAMES[t]
)
theme_groups = [('Base', ['dark', 'light']), ('Editor Themes', editor_presets)]
for category, keys in WEBSITE_SCHEME_CATEGORIES.items():
theme_groups.append((category, sorted(keys, key=lambda t: THEME_DISPLAY_NAMES[t])))
return render_template(
'settings.html',
settings=current,
default_library_path=LIBRARY_PATH,
theme_choices=['dark', 'light'] + named_presets,
theme_groups=theme_groups,
theme_display_names=THEME_DISPLAY_NAMES,
font_family_choices=['system', 'sans', 'serif', 'monospace', 'monaspace'],
font_size_choices=['small', 'medium', 'large', 'xlarge'],