# Spectrum 2 theme for Gradio — Chan Compass (v2, refined) # --------------------------------------------------------------- # Single source of truth for the LIVE Gradio app's look. Mirrors the # Chan Compass · Spectrum 2 design system. Drop this next to app.py. # # from theme import THEME, CSS # with gr.Blocks(title="Chan Compass · US", theme=THEME, css=CSS) as demo: # ... # demo.launch() # Gradio 5 # # Gradio 6+: demo.launch(theme=THEME, css=CSS) # --------------------------------------------------------------- import gradio as gr # ---- Spectrum 2 palette (light) ------------------------------------------- ACCENT = "#0265dc" # blue-900 ACCENT_HOVER = "#0054b6" # blue-1000 ACCENT_DOWN = "#00418f" # blue-1100 ACCENT_SUBTLE= "#e0f2ff" # blue-100 GRAY_25 = "#ffffff" GRAY_50 = "#f8f8f8" # canvas GRAY_75 = "#f3f3f3" GRAY_100 = "#e6e6e6" # hairline border GRAY_200 = "#d5d5d5" GRAY_300 = "#b1b1b1" # field border GRAY_500 = "#6d6d6d" # muted text GRAY_700 = "#292929" # body text GRAY_800 = "#1b1b1b" POSITIVE = "#007a39" NEGATIVE = "#d7373f" NOTICE = "#b25309" THEME = gr.themes.Default( font=[gr.themes.GoogleFont("Source Sans 3"), "Adobe Clean", "system-ui", "sans-serif"], font_mono=[gr.themes.GoogleFont("Source Code Pro"), "monospace"], primary_hue=gr.themes.colors.blue, neutral_hue=gr.themes.colors.gray, radius_size=gr.themes.sizes.radius_lg, spacing_size=gr.themes.sizes.spacing_md, ).set( # surfaces body_background_fill=GRAY_50, background_fill_primary=GRAY_25, background_fill_secondary=GRAY_75, block_background_fill=GRAY_25, block_border_color=GRAY_100, block_border_width="1px", block_radius="16px", block_shadow="0 1px 3px rgba(0,0,0,.06), 0 1px 1px rgba(0,0,0,.04)", block_label_text_color=GRAY_500, block_label_text_weight="600", block_title_text_color=GRAY_800, block_title_text_weight="700", border_color_primary=GRAY_100, panel_background_fill=GRAY_25, panel_border_color=GRAY_100, # text body_text_color=GRAY_700, body_text_color_subdued=GRAY_500, body_text_size="14px", # buttons — Spectrum 2 signature pill button_large_radius="9999px", button_small_radius="9999px", button_large_padding="10px 22px", button_primary_background_fill=ACCENT, button_primary_background_fill_hover=ACCENT_HOVER, button_primary_text_color="#ffffff", button_primary_border_color=ACCENT, button_primary_shadow="none", button_secondary_background_fill=GRAY_25, button_secondary_background_fill_hover=GRAY_75, button_secondary_border_color=GRAY_300, button_secondary_text_color=GRAY_700, # inputs input_background_fill=GRAY_25, input_border_color=GRAY_300, input_border_color_focus=ACCENT, input_border_width="2px", input_radius="8px", input_shadow="none", # accents / links color_accent_soft=ACCENT_SUBTLE, link_text_color=ACCENT_HOVER, link_text_color_hover=ACCENT_DOWN, ) # ---- Fine-grained CSS the theme object can't express ---------------------- CSS = """ :root{ --s2-accent:#0265dc; --s2-accent-hover:#0054b6; --s2-accent-down:#00418f; --s2-accent-subtle:#e0f2ff; --s2-gray-25:#fff; --s2-gray-50:#f8f8f8; --s2-gray-75:#f3f3f3; --s2-gray-100:#e6e6e6; --s2-gray-200:#d5d5d5; --s2-gray-300:#b1b1b1; --s2-gray-500:#6d6d6d; --s2-gray-700:#292929; --s2-gray-800:#1b1b1b; --s2-positive:#007a39; --s2-negative:#d7373f; --s2-notice:#b25309; --s2-radius-card:16px; --s2-ease:cubic-bezier(.45,0,.4,1); } body,.gradio-container{ background:var(--s2-gray-50)!important; color:var(--s2-gray-700); font-family:'Source Sans 3','Adobe Clean',system-ui,sans-serif; } .gradio-container{ max-width:1200px!important; margin:0 auto!important; padding-top:8px!important; } .gap{ gap:14px; } /* ---------- Buttons : Spectrum 2 pill ---------- */ button.primary, button.secondary, button.lg, button.sm{ border-radius:9999px!important; font-weight:600!important; letter-spacing:0; transition:background-color .13s var(--s2-ease), transform .12s var(--s2-ease), box-shadow .13s var(--s2-ease); } button.primary{ box-shadow:0 1px 2px rgba(2,101,220,.18)!important; } button.primary:hover{ box-shadow:0 2px 8px rgba(2,101,220,.22)!important; } button.primary:active, button.secondary:active{ transform:scale(.98); } button:focus-visible{ outline:2px solid var(--s2-accent)!important; outline-offset:2px; } .table-wrap button, table button, .dataframe button, [class*="cell-menu"] button{ border-radius:6px!important; font-weight:500!important; box-shadow:none!important; } /* ---------- Hero ---------- */ #s2-hero{ display:flex; align-items:center; gap:18px; background:linear-gradient(180deg,#fff 0%,#fcfdff 100%); border:1px solid var(--s2-gray-100); border-radius:20px; padding:22px 26px; margin:6px 0 14px; box-shadow:0 1px 3px rgba(0,0,0,.05); } #s2-hero .mark{ width:50px; height:50px; flex:none; border-radius:14px; display:grid; place-items:center; background:linear-gradient(135deg,#0265dc 0%,#5258e4 58%,#7326d3 100%); color:#fff; font-size:26px; box-shadow:0 4px 14px rgba(2,101,220,.22); } #s2-hero h1{ margin:0; font-size:25px; font-weight:800; letter-spacing:-.015em; color:var(--s2-gray-800); } #s2-hero h1 span{ font-weight:300; color:var(--s2-gray-500); } #s2-hero p{ margin:4px 0 0; color:var(--s2-gray-500); font-size:13.5px; line-height:1.45; max-width:620px; } #s2-hero .chips{ margin-left:auto; display:flex; flex-wrap:wrap; gap:6px; justify-content:flex-end; max-width:330px; } #s2-hero .chips span{ background:var(--s2-gray-75); border:1px solid var(--s2-gray-100); border-radius:9999px; padding:3px 11px; font-size:11.5px; font-weight:600; color:var(--s2-gray-700); white-space:nowrap; } /* ---------- Tabs : quiet + accent underline ---------- */ .tab-nav{ border-bottom:2px solid var(--s2-gray-100)!important; gap:22px; margin-bottom:6px; } .tab-nav button{ border:none!important; background:transparent!important; border-radius:0!important; font-size:15.5px!important; font-weight:600!important; color:var(--s2-gray-500)!important; padding:11px 2px!important; margin-bottom:-2px; transition:color .13s var(--s2-ease); } .tab-nav button:hover{ color:var(--s2-gray-700)!important; } .tab-nav button.selected{ color:var(--s2-accent)!important; box-shadow:inset 0 -2px 0 var(--s2-accent)!important; } /* ---------- Cards / groups ---------- */ .block{ border-radius:var(--s2-radius-card)!important; } .gr-group, .group{ border:1px solid var(--s2-gray-100)!important; border-radius:var(--s2-radius-card)!important; background:#fff; overflow:hidden; } /* control bar — the primary input row of each tab, framed as a card */ #s2-app .s2-bar{ background:#fff!important; border:1px solid var(--s2-gray-100)!important; border-radius:14px!important; padding:14px 16px!important; box-shadow:0 1px 3px rgba(0,0,0,.05); align-items:flex-end!important; } #s2-app .s2-bar .block{ border:none!important; box-shadow:none!important; background:transparent!important; } /* eyebrow section labels */ .s2-eyebrow p{ margin:14px 0 2px!important; font-size:11px!important; font-weight:700!important; letter-spacing:.07em; text-transform:uppercase; color:var(--s2-gray-500)!important; } .s2-help p{ color:var(--s2-gray-500)!important; font-size:13px!important; line-height:1.5; margin:2px 0 6px!important; } /* ---------- Data tables ---------- */ .dataframe, table{ font-size:13.5px!important; } thead th{ background:var(--s2-gray-75)!important; font-weight:700!important; text-transform:uppercase; letter-spacing:.04em; font-size:11px!important; color:var(--s2-gray-500)!important; } tbody td{ border-color:var(--s2-gray-100)!important; } tbody tr:hover td{ background:var(--s2-gray-75)!important; } /* ---------- AI output panel — signature accent-framed surface ---------- */ .llm-out{ border:1px solid var(--s2-gray-100); border-left:5px solid var(--s2-accent); border-radius:12px; background:#fff; padding:16px 20px; min-height:140px; font-size:14.5px; line-height:1.6; overflow:auto; box-shadow:0 2px 12px rgba(2,101,220,.07); } .llm-out:empty::after{ content:"🤖 AI output appears here"; color:#9aa0a6; font-style:italic; } .llm-out h1,.llm-out h2,.llm-out h3{ font-size:16px; margin:.4em 0 .3em; color:var(--s2-gray-800); } /* raw-read (non-AI) markdown reads as a quiet well */ #detail-log textarea{ font-family:'Source Code Pro',monospace!important; font-size:12.5px!important; background:var(--s2-gray-75)!important; border-radius:10px!important; } /* ---------- Accordion (email rows, collapsible) ---------- */ .gr-accordion, .accordion{ border:1px solid var(--s2-gray-100)!important; border-radius:12px!important; background:#fff; overflow:hidden; } .gr-accordion > button, .accordion > button{ font-weight:600!important; color:var(--s2-gray-700)!important; font-size:13.5px!important; } /* ---------- Misc ---------- */ .s2-footnote p{ color:var(--s2-gray-500)!important; font-size:12.5px!important; } hr{ border:none; border-top:1px solid var(--s2-gray-100); margin:18px 0; } label span{ font-weight:600; } ::selection{ background:var(--s2-accent-subtle); } """ __all__ = ["THEME", "CSS"]