File size: 5,336 Bytes
1a2024f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""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;
  }}
}}
"""