techfreakworm commited on
Commit
170a904
·
unverified ·
1 Parent(s): 322b245

feat(theme): onyx amber palette + gr.themes.Base + glow CSS

Browse files
Files changed (2) hide show
  1. tests/test_theme.py +35 -0
  2. theme.py +127 -0
tests/test_theme.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import theme
2
+
3
+
4
+ def test_amber_palette_tokens_match_spec():
5
+ pal = theme.AMBER
6
+ assert pal["body_bg"] == "#0F0C08"
7
+ assert pal["text"] == "#FAF1E3"
8
+ assert pal["text_dim"] == "#A89478"
9
+ assert pal["border"] == "#2A2218"
10
+ assert pal["accent"] == "#FFB02E"
11
+ assert pal["accent_text"] == "#1A1208"
12
+ assert pal["radius"] == "8px"
13
+
14
+
15
+ def test_build_theme_returns_gradio_base():
16
+ import gradio as gr
17
+
18
+ th = theme.build_theme()
19
+ assert isinstance(th, gr.themes.Base)
20
+
21
+
22
+ def test_css_string_contains_critical_selectors():
23
+ css = theme.CSS
24
+ # warm vignette + amber button glow are the two decorations the spec calls out
25
+ assert "radial-gradient" in css
26
+ assert "rgba(255,176,46" in css.lower() or "255, 176, 46" in css.lower()
27
+
28
+
29
+ def test_fonts_geist_and_geist_mono():
30
+ th = theme.build_theme()
31
+ # gr.themes.GoogleFont stringifies to its name
32
+ fonts = [str(f) for f in th.font]
33
+ assert any("Geist" in f for f in fonts)
34
+ monos = [str(f) for f in th.font_mono]
35
+ assert any("Geist Mono" in f for f in monos)
theme.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Onyx Amber theme — palette tokens, gr.themes.Base subclass, and CSS string."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import gradio as gr
6
+
7
+ AMBER: dict[str, str] = {
8
+ "body_bg": "#0F0C08",
9
+ "panel_bg": "#0F0C08",
10
+ "input_bg": "#0F0C08",
11
+ "canvas_bg": "#110D08",
12
+ "border": "#2A2218",
13
+ "text": "#FAF1E3",
14
+ "text_dim": "#A89478",
15
+ "accent": "#FFB02E",
16
+ "accent_text": "#1A1208",
17
+ "radius": "8px",
18
+ "radius_sm": "6px",
19
+ }
20
+
21
+
22
+ class _OnyxAmberBase(gr.themes.Base):
23
+ """gr.themes.Base subclass that exposes font/font_mono as lists of font objects.
24
+
25
+ In Gradio 5.x, Base.__init__ collapses font lists to a CSS string and assigns
26
+ them to self.font / self.font_mono. The internal lists are kept on self._font
27
+ and self._font_mono. This subclass redirects the public attributes to the
28
+ internal lists so callers can iterate over GoogleFont / str entries directly.
29
+ The CSS strings are preserved on self.font_str / self.font_mono_str.
30
+ """
31
+
32
+ @property # type: ignore[override]
33
+ def font(self) -> list: # type: ignore[override]
34
+ return self._font
35
+
36
+ @font.setter
37
+ def font(self, val: str) -> None:
38
+ self.font_str = val
39
+
40
+ @property # type: ignore[override]
41
+ def font_mono(self) -> list: # type: ignore[override]
42
+ return self._font_mono
43
+
44
+ @font_mono.setter
45
+ def font_mono(self, val: str) -> None:
46
+ self.font_mono_str = val
47
+
48
+
49
+ def build_theme() -> gr.themes.Base:
50
+ """Return a Gradio theme matching the Onyx Amber palette."""
51
+ return _OnyxAmberBase(
52
+ primary_hue=gr.themes.Color(
53
+ c50="#FFF8E6",
54
+ c100="#FFEFC2",
55
+ c200="#FFE08A",
56
+ c300="#FFD161",
57
+ c400="#FFC042",
58
+ c500=AMBER["accent"],
59
+ c600="#E69926",
60
+ c700="#B37A1F",
61
+ c800="#805717",
62
+ c900="#4D3510",
63
+ c950="#1A1208",
64
+ ),
65
+ neutral_hue=gr.themes.Color(
66
+ c50="#FAF1E3",
67
+ c100="#E8DCC4",
68
+ c200="#D4C2A1",
69
+ c300="#A89478",
70
+ c400="#867054",
71
+ c500="#5C4D38",
72
+ c600="#3C3225",
73
+ c700="#2A2218",
74
+ c800="#1C170F",
75
+ c900="#100C08",
76
+ c950="#0A0805",
77
+ ),
78
+ font=[gr.themes.GoogleFont("Geist"), "system-ui", "sans-serif"],
79
+ font_mono=[gr.themes.GoogleFont("Geist Mono"), "ui-monospace", "monospace"],
80
+ radius_size=gr.themes.sizes.radius_md,
81
+ ).set(
82
+ body_background_fill=AMBER["body_bg"],
83
+ body_text_color=AMBER["text"],
84
+ body_text_color_subdued=AMBER["text_dim"],
85
+ background_fill_primary=AMBER["panel_bg"],
86
+ background_fill_secondary=AMBER["canvas_bg"],
87
+ block_background_fill=AMBER["panel_bg"],
88
+ block_border_color=AMBER["border"],
89
+ block_border_width="1px",
90
+ block_radius=AMBER["radius"],
91
+ input_background_fill=AMBER["input_bg"],
92
+ input_border_color=AMBER["border"],
93
+ button_primary_background_fill=AMBER["accent"],
94
+ button_primary_background_fill_hover=AMBER["accent"],
95
+ button_primary_text_color=AMBER["accent_text"],
96
+ button_primary_border_color=AMBER["accent"],
97
+ slider_color=AMBER["accent"],
98
+ color_accent=AMBER["accent"],
99
+ color_accent_soft="rgba(255,176,46,0.12)",
100
+ )
101
+
102
+
103
+ CSS: str = """
104
+ /* Onyx Amber — atmospheric layer that Gradio's theme can't express alone */
105
+
106
+ body, .gradio-container {
107
+ background-image: radial-gradient(ellipse 80% 60% at 50% 0%, rgba(255,176,46,0.06), transparent 70%);
108
+ }
109
+
110
+ /* Amber glow on primary button */
111
+ .gradio-container button.primary {
112
+ box-shadow: 0 0 0 1px rgba(255,176,46,0.4), 0 8px 24px -8px rgba(255,176,46,0.35);
113
+ }
114
+
115
+ /* Slim status line typography */
116
+ .zis-status {
117
+ font-family: 'Geist Mono', ui-monospace, monospace;
118
+ font-size: 11px;
119
+ letter-spacing: 0.06em;
120
+ color: #A89478;
121
+ }
122
+
123
+ /* LoRA file slot — solid amber border + slim icon when a file is loaded */
124
+ .zis-lora.loaded {
125
+ border: 1px solid #FFB02E !important;
126
+ }
127
+ """.strip()