Spaces:
Running on Zero
Running on Zero
feat(theme): onyx amber palette + gr.themes.Base + glow CSS
Browse files- tests/test_theme.py +35 -0
- 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()
|