Files
gradio-ui/theme.py

559 lines
16 KiB
Python

"""
Shared Gradio theme for Davies Tech Labs AI demos.
Consistent styling across all demo applications.
Cyberpunk aesthetic - dark with yellow/gold accents.
"""
import gradio as gr
# Cyberpunk color palette
CYBER_YELLOW = "#d4a700"
CYBER_GOLD = "#ffcc00"
CYBER_DARK = "#0d0d0d"
CYBER_DARKER = "#080808"
CYBER_GRAY = "#1a1a1a"
CYBER_TEXT = "#e5e5e5"
CYBER_MUTED = "#888888"
def get_lab_theme() -> gr.Theme:
"""
Create a custom Gradio theme matching cyberpunk styling.
Dark theme with yellow/gold accents.
"""
return gr.themes.Base(
primary_hue=gr.themes.colors.yellow,
secondary_hue=gr.themes.colors.amber,
neutral_hue=gr.themes.colors.zinc,
font=[gr.themes.GoogleFont("Space Grotesk"), "ui-sans-serif", "system-ui", "sans-serif"],
font_mono=[gr.themes.GoogleFont("JetBrains Mono"), "ui-monospace", "monospace"],
).set(
# Background colors
body_background_fill=CYBER_DARK,
body_background_fill_dark=CYBER_DARKER,
background_fill_primary=CYBER_GRAY,
background_fill_primary_dark=CYBER_DARK,
background_fill_secondary=CYBER_DARKER,
background_fill_secondary_dark="#050505",
# Text colors
body_text_color=CYBER_TEXT,
body_text_color_dark=CYBER_TEXT,
body_text_color_subdued=CYBER_MUTED,
body_text_color_subdued_dark=CYBER_MUTED,
# Borders
border_color_primary=CYBER_YELLOW,
border_color_primary_dark=CYBER_YELLOW,
border_color_accent=CYBER_GOLD,
border_color_accent_dark=CYBER_GOLD,
# Buttons
button_primary_background_fill=CYBER_YELLOW,
button_primary_background_fill_dark=CYBER_YELLOW,
button_primary_background_fill_hover="#b8940a",
button_primary_background_fill_hover_dark="#b8940a",
button_primary_text_color=CYBER_DARK,
button_primary_text_color_dark=CYBER_DARK,
button_primary_border_color=CYBER_GOLD,
button_primary_border_color_dark=CYBER_GOLD,
button_secondary_background_fill="transparent",
button_secondary_background_fill_dark="transparent",
button_secondary_text_color=CYBER_YELLOW,
button_secondary_text_color_dark=CYBER_YELLOW,
button_secondary_border_color=CYBER_YELLOW,
button_secondary_border_color_dark=CYBER_YELLOW,
# Inputs
input_background_fill=CYBER_DARKER,
input_background_fill_dark=CYBER_DARKER,
input_border_color="#333333",
input_border_color_dark="#333333",
input_border_color_focus=CYBER_YELLOW,
input_border_color_focus_dark=CYBER_YELLOW,
# Shadows and effects
shadow_drop="0 4px 20px rgba(212, 167, 0, 0.15)",
shadow_drop_lg="0 8px 40px rgba(212, 167, 0, 0.2)",
# Block styling
block_background_fill=CYBER_GRAY,
block_background_fill_dark=CYBER_GRAY,
block_border_color="#2a2a2a",
block_border_color_dark="#2a2a2a",
block_label_background_fill="#1a1a00",
block_label_background_fill_dark="#1a1a00",
block_label_text_color=CYBER_YELLOW,
block_label_text_color_dark=CYBER_YELLOW,
block_label_border_color=CYBER_YELLOW,
block_label_border_color_dark=CYBER_YELLOW,
block_title_text_color=CYBER_TEXT,
block_title_text_color_dark=CYBER_TEXT,
# Table / Dataframe
table_border_color="#2a2a2a",
table_even_background_fill="#111111",
table_even_background_fill_dark="#111111",
table_odd_background_fill=CYBER_GRAY,
table_odd_background_fill_dark=CYBER_GRAY,
table_row_focus="#1f1a00",
table_row_focus_dark="#1f1a00",
# Panel / accordion
panel_background_fill=CYBER_DARK,
panel_background_fill_dark=CYBER_DARK,
panel_border_color="#2a2a2a",
panel_border_color_dark="#2a2a2a",
# Checkbox / radio
checkbox_background_color=CYBER_DARKER,
checkbox_background_color_dark=CYBER_DARKER,
checkbox_label_background_fill=CYBER_GRAY,
checkbox_label_background_fill_dark=CYBER_GRAY,
checkbox_label_text_color=CYBER_TEXT,
checkbox_label_text_color_dark=CYBER_TEXT,
# Colors
color_accent=CYBER_YELLOW,
color_accent_soft="#1f1a00",
color_accent_soft_dark="#1f1a00",
)
# Common CSS for all demos - Cyberpunk theme
CUSTOM_CSS = """
/* Cyberpunk font import */
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=JetBrains+Mono:wght@400;500&display=swap');
/* Root variables */
:root {
--cyber-yellow: #d4a700;
--cyber-gold: #ffcc00;
--cyber-dark: #0d0d0d;
--cyber-gray: #1a1a1a;
--cyber-text: #e5e5e5;
}
/* Container styling */
.gradio-container {
max-width: 1400px !important;
margin: auto !important;
background: var(--cyber-dark) !important;
}
/* Header/title styling - glitch effect */
.title-row, h1 {
color: var(--cyber-text) !important;
font-family: 'Space Grotesk', sans-serif !important;
font-weight: 700 !important;
text-transform: uppercase;
letter-spacing: 0.15em;
position: relative;
}
h1::after {
content: '';
position: absolute;
bottom: -8px;
left: 0;
width: 100%;
height: 2px;
background: linear-gradient(90deg, var(--cyber-yellow), transparent);
}
/* Yellow accent lines - horizontal separator */
.cyber-line {
width: 100%;
height: 2px;
background: var(--cyber-yellow);
margin: 1.5rem 0;
box-shadow: 0 0 10px var(--cyber-yellow);
}
/* Scrolling Japanese text effect */
.cyber-marquee {
overflow: hidden;
background: linear-gradient(90deg, var(--cyber-dark), transparent 5%, transparent 95%, var(--cyber-dark));
padding: 0.5rem 0;
border-top: 1px solid var(--cyber-yellow);
border-bottom: 1px solid var(--cyber-yellow);
}
.cyber-marquee-content {
display: inline-block;
white-space: nowrap;
animation: marquee 20s linear infinite;
color: var(--cyber-yellow);
font-family: 'Space Grotesk', sans-serif;
letter-spacing: 0.5em;
}
@keyframes marquee {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
/* Status indicators */
.status-ok {
color: #00ff88 !important;
font-weight: 600;
text-shadow: 0 0 10px #00ff88;
}
.status-error {
color: #ff3366 !important;
font-weight: 600;
text-shadow: 0 0 10px #ff3366;
}
.status-pending {
color: var(--cyber-yellow) !important;
font-weight: 600;
text-shadow: 0 0 10px var(--cyber-yellow);
}
/* Metrics display - terminal style */
.metrics-box {
background: rgba(13, 13, 13, 0.9) !important;
border: 1px solid var(--cyber-yellow) !important;
border-radius: 0 !important;
padding: 16px !important;
font-family: 'JetBrains Mono', monospace !important;
color: var(--cyber-gold) !important;
box-shadow: 0 0 20px rgba(212, 167, 0, 0.1);
}
/* Code blocks */
.code-block, pre, code {
background: #0a0a0a !important;
border: 1px solid #333 !important;
border-left: 3px solid var(--cyber-yellow) !important;
font-family: 'JetBrains Mono', monospace !important;
}
/* Buttons - cyber style */
.gr-button-primary {
background: var(--cyber-yellow) !important;
color: var(--cyber-dark) !important;
border: none !important;
text-transform: uppercase !important;
letter-spacing: 0.1em !important;
font-weight: 600 !important;
transition: all 0.3s ease !important;
clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 8px 100%, 0 calc(100% - 8px));
}
.gr-button-primary:hover {
background: var(--cyber-gold) !important;
box-shadow: 0 0 30px rgba(212, 167, 0, 0.5) !important;
transform: translateY(-2px);
}
.gr-button-secondary {
background: transparent !important;
color: var(--cyber-yellow) !important;
border: 1px solid var(--cyber-yellow) !important;
text-transform: uppercase !important;
letter-spacing: 0.1em !important;
}
/* Input fields */
.gr-input, .gr-textbox, textarea, input {
background: #0a0a0a !important;
border: 1px solid #333 !important;
color: var(--cyber-text) !important;
border-radius: 0 !important;
transition: border-color 0.3s ease !important;
}
.gr-input:focus, .gr-textbox:focus, textarea:focus, input:focus {
border-color: var(--cyber-yellow) !important;
box-shadow: 0 0 10px rgba(212, 167, 0, 0.3) !important;
}
/* Tabs - angular cyber style */
.gr-tab-nav {
border-bottom: 2px solid var(--cyber-yellow) !important;
}
.gr-tab {
background: transparent !important;
color: var(--cyber-muted) !important;
border: none !important;
text-transform: uppercase !important;
letter-spacing: 0.1em !important;
}
.gr-tab.selected {
color: var(--cyber-yellow) !important;
background: rgba(212, 167, 0, 0.1) !important;
}
/* Accordion */
.gr-accordion {
border: 1px solid #333 !important;
background: var(--cyber-gray) !important;
}
/* Labels and text */
label, .gr-label {
color: var(--cyber-yellow) !important;
text-transform: uppercase !important;
font-size: 0.75rem !important;
letter-spacing: 0.1em !important;
}
/* Slider styling */
.gr-slider {
--slider-color: var(--cyber-yellow) !important;
}
/* Footer - cyber style */
.footer {
text-align: center;
color: #666;
font-size: 0.8rem;
padding: 1.5rem;
border-top: 1px solid #333;
margin-top: 2rem;
font-family: 'JetBrains Mono', monospace;
letter-spacing: 0.05em;
}
.footer a {
color: var(--cyber-yellow);
text-decoration: none;
transition: all 0.3s ease;
}
.footer a:hover {
text-shadow: 0 0 10px var(--cyber-yellow);
}
/* Cyber badge/tag */
.cyber-badge {
display: inline-block;
padding: 4px 12px;
background: transparent;
border: 1px solid var(--cyber-yellow);
color: var(--cyber-yellow);
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.1em;
font-family: 'JetBrains Mono', monospace;
}
/* Progress bars */
.progress-bar {
background: #1a1a1a !important;
border: 1px solid #333 !important;
}
.progress-bar-fill {
background: linear-gradient(90deg, var(--cyber-yellow), var(--cyber-gold)) !important;
}
/* Scrollbar styling */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--cyber-dark);
}
::-webkit-scrollbar-thumb {
background: #333;
border: 1px solid var(--cyber-yellow);
}
::-webkit-scrollbar-thumb:hover {
background: #444;
}
/* Glowing text effect utility */
.glow-text {
text-shadow: 0 0 10px var(--cyber-yellow), 0 0 20px var(--cyber-yellow);
}
/* ── Examples table / Dataframe overrides ── */
/* Gradio renders Examples as a <table> inside a Dataset component.
The default styles inject white / light-gray rows that blow out the
cyberpunk palette. Force them dark here. */
.gr-samples-table,
.gr-sample-textbox,
table.table,
.gr-examples table,
div[class*="dataset"] table {
background: var(--cyber-dark) !important;
color: var(--cyber-text) !important;
}
.gr-samples-table tr,
.gr-examples table tr,
div[class*="dataset"] table tr {
background: #111111 !important;
border-bottom: 1px solid #222 !important;
}
.gr-samples-table tr:nth-child(even),
.gr-examples table tr:nth-child(even),
div[class*="dataset"] table tr:nth-child(even) {
background: #0d0d0d !important;
}
.gr-samples-table tr:hover,
.gr-examples table tr:hover,
div[class*="dataset"] table tr:hover {
background: #1f1a00 !important;
cursor: pointer;
}
.gr-samples-table th,
.gr-examples table th,
div[class*="dataset"] table th {
background: var(--cyber-gray) !important;
color: var(--cyber-yellow) !important;
text-transform: uppercase !important;
font-size: 0.75rem !important;
letter-spacing: 0.1em !important;
border-bottom: 2px solid var(--cyber-yellow) !important;
padding: 10px 16px !important;
}
.gr-samples-table td,
.gr-examples table td,
div[class*="dataset"] table td {
color: #999 !important;
border-bottom: 1px solid #1a1a1a !important;
padding: 10px 16px !important;
font-family: 'JetBrains Mono', monospace !important;
font-size: 0.85rem !important;
}
/* ── Block label pill (e.g. "GENERATED AUDIO", "STATUS") ── */
/* These are the small floating labels above each component block */
span[class*="label-wrap"],
.gr-block-label,
.label-wrap {
background: #1a1a00 !important;
border: 1px solid var(--cyber-yellow) !important;
color: var(--cyber-yellow) !important;
}
/* ── Dropdown / select menus ── */
.gr-dropdown,
select,
ul[role="listbox"],
div[class*="dropdown"],
.secondary-wrap {
background: #0a0a0a !important;
color: var(--cyber-text) !important;
border-color: #333 !important;
}
ul[role="listbox"] li,
div[class*="dropdown"] li {
background: #0a0a0a !important;
color: var(--cyber-text) !important;
}
ul[role="listbox"] li:hover,
ul[role="listbox"] li[aria-selected="true"],
div[class*="dropdown"] li:hover {
background: #1f1a00 !important;
color: var(--cyber-yellow) !important;
}
/* ── Audio player ── */
.gr-audio,
audio {
background: var(--cyber-dark) !important;
border: 1px solid #2a2a2a !important;
}
/* Audio waveform container */
div[data-testid="waveform-container"],
div[class*="audio"] {
background: #0a0a0a !important;
}
/* ── Markdown inside blocks ── */
.gr-markdown,
.gr-markdown p,
.prose {
color: var(--cyber-text) !important;
}
.gr-markdown h3,
.gr-markdown h2 {
color: var(--cyber-yellow) !important;
letter-spacing: 0.05em !important;
}
.gr-markdown strong {
color: var(--cyber-gold) !important;
}
/* ── Examples accordion header ("Examples" label) ── */
.gr-examples .label-wrap,
div[id*="examples"] .label-wrap,
span[data-testid="block-label"] {
background: #1a1a00 !important;
color: var(--cyber-yellow) !important;
border: 1px solid var(--cyber-yellow) !important;
font-size: 0.7rem !important;
text-transform: uppercase !important;
letter-spacing: 0.1em !important;
}
/* ── Misc: tooltip, info text ── */
.gr-info,
.gr-description {
color: #666 !important;
}
/* ── Svelte internal: make sure no white backgrounds leak ── */
.contain > div,
.wrap > div {
background: inherit !important;
}
/* ── Tab content panels ── */
.tabitem {
background: var(--cyber-dark) !important;
}
"""
def create_header(title: str, description: str) -> gr.Markdown:
"""Create a cyberpunk-style header for demo apps."""
# Japanese text for marquee effect
jp_text = "サイバー · コマース · フューチャー · "
return gr.Markdown(f"""
<div style="margin-bottom: 2rem;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
<span class="cyber-badge">STORE</span>
<span class="cyber-badge">v2.0</span>
<span class="cyber-badge">ONLINE</span>
</div>
<h1 style="font-size: 3rem; margin: 0; letter-spacing: 0.2em;">{title.upper()}</h1>
<p style="color: #888; margin-top: 0.5rem; font-family: 'JetBrains Mono', monospace; font-size: 0.9rem;">{description}</p>
<div class="cyber-line"></div>
<div class="cyber-marquee">
<span class="cyber-marquee-content">{jp_text * 8}</span>
</div>
</div>
""")
def create_footer() -> gr.Markdown:
"""Create a cyberpunk-style footer for demo apps."""
return gr.Markdown("""
<div class="cyber-line"></div>
<div class="footer">
<span class="cyber-badge" style="margin-right: 1rem;">AR</span>
<span style="color: #666;">DAVIES TECH LABS</span>
<span style="color: #444; margin: 0 1rem;">·</span>
<a href="https://mlflow.lab.daviestechlabs.io" target="_blank">MLFLOW</a>
<span style="color: #444; margin: 0 1rem;">·</span>
<a href="https://kubeflow.lab.daviestechlabs.io" target="_blank">KUBEFLOW</a>
<span style="color: #444; margin: 0 1rem;">·</span>
<span style="color: #666;">[ スクロール ]</span>
</div>
""")