Spaces:
Running on Zero
Running on Zero
| """THOX brand v2 Gradio theme -- the single source for every Space in this repo. | |
| CANONICAL COPY. `ci/test_theme_parity.py` asserts every Space's copy is | |
| byte-identical to this file. Edit here, then run `python build.py` (which | |
| re-copies it) -- editing a Space's copy directly will fail CI. | |
| WHY THIS FILE EXISTS | |
| -------------------- | |
| Seven Spaces each carried their own inline theme. Two defects were present in | |
| every one of them, measured on the live fleet: | |
| 1. The stylesheet forced `.gradio-container { background: #09090B !important }` | |
| while Gradio still rendered its LIGHT palette. Loading `?__theme=light` | |
| produced black body text on the forced near-black canvas: | |
| #000000 on #09090B = 1.06:1 | |
| Invisible. Never pin one theme's colour; let the theme drive it. | |
| 2. Buttons and links were stock Gradio blue (`--color-accent` #3B82F6, | |
| `--link-text-color` #2563EB), so nothing in the fleet's UI was actually THOX. | |
| Fixing that in seven places would have drifted. It lives here instead. | |
| CONTRAST -- from ttracx/thox-design-system (standards/brand-v2.md, ADR-0001) | |
| --------------------------------------------------------------------------- | |
| The reflex pairing is wrong and must not be "fixed" back: | |
| #FFFFFF on #05A451 = 3.26:1 FAILS WCAG AA | |
| #000000 on #05A451 = 6.44:1 PASSES <- primary button, both themes | |
| Light Dark | |
| #03763B on #FFFFFF 5.74 #45e887 on #09090B 12.45 links | |
| #52525B on #FFFFFF 7.73 #A1A1AA on #09090B 7.76 subdued text | |
| #FFFFFF on #03763B 5.74 #000000 on #05A451 6.44 primary hover | |
| Plain #05A451 is only 3.26:1 on white, so light-surface text uses brand.deep | |
| (#03763B). Gradio's own `--body-text-color-subdued` default (#BBBBC2) is about | |
| 1.9:1 on white, so it is pinned in both modes here. | |
| #10B981 / #34D399 are DEPRECATED v1 emeralds. Purple #A855F7 is MagStack-only | |
| and neon #00FF88 is device-illumination-only -- neither belongs in UI chrome. | |
| """ | |
| from __future__ import annotations | |
| import gradio as gr | |
| THOX_GREEN = "#05A451" # brand.primary | |
| THOX_ACCENT = "#45e887" # accent on dark | |
| THOX_DEEP = "#03763B" # brand.deep -- accessible green on light surfaces | |
| THOX_FOCUS = "#37C77A" # brand.light -- 9.11:1 on the dark canvas | |
| THOX_HUE = gr.themes.Color( | |
| c50="#E6F7EE", c100="#C2EBD6", c200="#8FDCB6", c300="#5CCB95", | |
| c400=THOX_FOCUS, c500=THOX_GREEN, c600="#048A44", c700=THOX_DEEP, | |
| c800="#026030", c900="#064E2E", c950="#04331E", | |
| ) | |
| THOX_THEME = gr.themes.Base( | |
| primary_hue=THOX_HUE, | |
| neutral_hue=gr.themes.colors.zinc, | |
| ).set( | |
| # Primary action: brand green with BLACK ink in both themes (6.44:1). | |
| button_primary_background_fill=THOX_GREEN, | |
| button_primary_background_fill_dark=THOX_GREEN, | |
| button_primary_text_color="#000000", | |
| button_primary_text_color_dark="#000000", | |
| button_primary_background_fill_hover=THOX_DEEP, | |
| button_primary_background_fill_hover_dark=THOX_ACCENT, | |
| button_primary_text_color_hover="#FFFFFF", # on #03763B = 5.74:1 | |
| button_primary_text_color_hover_dark="#000000", | |
| # Plain green is only 3.26:1 on white, so light links use brand.deep. | |
| link_text_color=THOX_DEEP, | |
| link_text_color_dark=THOX_ACCENT, | |
| link_text_color_hover="#024F27", | |
| link_text_color_hover_dark=THOX_GREEN, | |
| # Gradio's default subdued grey fails AA on white; pin both modes. | |
| body_text_color_subdued="#52525B", # 7.73:1 on white | |
| body_text_color_subdued_dark="#A1A1AA", # 7.76:1 on #09090B | |
| background_fill_primary_dark="#09090B", | |
| background_fill_secondary_dark="#18181B", | |
| border_color_primary_dark="#3F3F46", | |
| ) | |
| # Shared chrome. Colour resolves through a Gradio variable or a per-theme | |
| # override, so it follows the active theme instead of pinning one. | |
| # `body.dark` is how Gradio 5 marks dark mode -- not html.dark, not data-theme. | |
| THOX_CSS = f""" | |
| :root {{ | |
| --thox-green: {THOX_GREEN}; --thox-accent: {THOX_ACCENT}; --thox-deep: {THOX_DEEP}; | |
| --thox-brand-ink: var(--thox-deep); | |
| }} | |
| .dark, body.dark {{ --thox-brand-ink: var(--thox-accent); }} | |
| #thox-header {{ | |
| border-left: 3px solid var(--thox-green); | |
| padding: 0.6rem 0 0.6rem 0.9rem; margin-bottom: 0.5rem; | |
| }} | |
| #thox-header h1 {{ | |
| color: var(--thox-brand-ink); | |
| font-size: 1.15rem; margin: 0; letter-spacing: .02em; | |
| }} | |
| #thox-header p {{ | |
| color: var(--body-text-color-subdued); | |
| font-size: .85rem; margin: .25rem 0 0; | |
| }} | |
| #thox-header code {{ color: var(--thox-brand-ink); }} | |
| /* Amber is semantic, so it is pinned per theme rather than taken from a Gradio | |
| variable -- the dark-mode #FBBF24 is only 1.7:1 on white. */ | |
| #thox-caveat {{ | |
| border-left: 3px solid #B45309; background: rgba(180,83,9,.07); | |
| padding: .55rem .8rem; margin: .4rem 0 .6rem; border-radius: 4px; | |
| color: #92400E; font-size: .82rem; line-height: 1.45; | |
| }} | |
| .dark #thox-caveat, body.dark #thox-caveat {{ | |
| border-left-color: #F59E0B; background: rgba(245,158,11,.08); | |
| color: #FBBF24; | |
| }} | |
| :is(button, a, input, textarea, select):focus-visible {{ | |
| outline: 2px solid var(--thox-brand-ink); outline-offset: 2px; | |
| }} | |
| ::selection {{ background: var(--thox-green); color: #000000; }} | |
| @media (prefers-reduced-motion: reduce) {{ | |
| *, *::before, *::after {{ | |
| animation-duration: .01ms !important; transition-duration: .01ms !important; | |
| }} | |
| }} | |
| """ | |