BrianIsaac's picture
fix: add CSS for secondary large buttons to match primary button size
0a6e04f
raw
history blame
9.56 kB
"""Custom Gradio theme for Portfolio Intelligence Platform.
Professional financial application theme with:
- Trust-inspiring blue/grey colour palette
- Clean typography with Inter font
- Subtle shadows and borders
- Optimised for data-heavy interfaces
"""
import gradio as gr
from gradio.themes.base import Base
from gradio.themes.utils import colors, fonts, sizes
class FinancialTheme(Base):
"""Professional financial application theme for Gradio."""
def __init__(self, **kwargs):
"""Initialise financial theme with professional colours and typography."""
super().__init__(
# Trust & stability colours
primary_hue=colors.blue, # Professional blue
secondary_hue=colors.slate, # Neutral greys
neutral_hue=colors.gray,
# Professional sizing
spacing_size=sizes.spacing_md,
radius_size=sizes.radius_sm, # Subtle rounded corners
text_size=sizes.text_lg, # Readable text
# Professional fonts
font=fonts.GoogleFont("Inter"), # Clean sans-serif
font_mono=fonts.GoogleFont("IBM Plex Mono")
)
# Custom colour overrides
super().set(
# Background
body_background_fill="#f8f9fa",
body_background_fill_dark="#1a1a1a",
# Primary buttons
button_primary_background_fill="#288cfa",
button_primary_background_fill_hover="#1a7de8",
button_primary_text_color="white",
button_primary_shadow="0 2px 4px rgba(40, 140, 250, 0.2)",
button_primary_border_color="#288cfa",
# Secondary buttons
button_secondary_background_fill="white",
button_secondary_background_fill_hover="#f8f9fa",
button_secondary_text_color="#288cfa",
button_secondary_border_color="#dee2e6",
# Card/block styling
block_background_fill="white",
block_border_width="1px",
block_border_color="#e5e7eb",
block_shadow="0 1px 3px 0 rgba(0, 0, 0, 0.1)",
block_title_text_weight="600",
block_title_text_color="#242c34",
block_label_text_weight="500",
block_label_text_color="#6b7280",
# Input styling
input_background_fill="white",
input_border_color="#d1d5db",
input_shadow="0 1px 2px 0 rgba(0, 0, 0, 0.05)",
input_border_width="1px",
# Checkbox/radio
checkbox_border_color="#d1d5db",
checkbox_background_color="white",
# Status colours
color_accent_soft="#10b981", # Green for positive
stat_background_fill="#fef3c7", # Subtle yellow for highlights
# Progress bar
loader_color="#288cfa",
slider_color="#288cfa",
# Table styling
table_border_color="#e5e7eb",
table_odd_background_fill="#f9fafb",
table_even_background_fill="white",
# Panel styling
panel_background_fill="white",
panel_border_color="#e5e7eb",
panel_border_width="1px",
)
# Custom CSS for additional styling (theme-adaptive with dark mode improvements)
FINANCIAL_CSS = """
/* Global container styling */
.gradio-container {
font-family: 'Inter', 'Arial', sans-serif;
max-width: 1400px;
margin: auto;
}
/* Adaptive container background - follows HuggingFace Spaces theme */
.gradio-container {
background: var(--body-background-fill);
}
/* Clean input styling - theme adaptive */
input,
textarea {
background-color: var(--input-background-fill);
border: 1px solid var(--input-border-color);
border-radius: 8px;
}
input:focus-visible,
textarea:focus-visible {
outline: 3px solid var(--button-primary-background-fill);
outline-offset: 2px;
border-radius: 2px;
opacity: 1 !important;
}
input:focus,
textarea:focus {
opacity: 1 !important;
border-color: var(--button-primary-background-fill);
box-shadow: 0 0 0 3px var(--button-primary-background-fill-hover);
}
/* Group/card styling with subtle shadows - theme adaptive */
.gr-group,
.gr-box {
background-color: var(--block-background-fill);
border: 1px solid var(--block-border-color);
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
/* Button styling - theme adaptive */
.gr-button {
border-radius: 8px;
}
.gr-button-primary {
background: var(--button-primary-background-fill);
border: none;
color: var(--button-primary-text-color);
font-weight: 600;
}
.gr-button-primary:hover {
background: var(--button-primary-background-fill-hover);
}
/* Example portfolio cards - theme adaptive */
.gr-examples {
background-color: var(--block-background-fill);
border: 1px solid var(--block-border-color);
border-radius: 8px;
}
.gr-examples button {
background-color: var(--button-secondary-background-fill);
border: 1px solid var(--button-secondary-border-color);
}
.gr-examples button:hover {
background-color: var(--button-secondary-background-fill-hover);
border-color: var(--button-primary-background-fill);
}
/* Header styling - theme adaptive */
.header h1 {
color: var(--body-text-color);
font-weight: 700;
letter-spacing: -0.025em;
margin-bottom: 0.5rem;
}
.header h3 {
color: var(--body-text-color);
opacity: 0.7;
font-weight: 400;
margin-top: 0;
}
.markdown-text h1 {
font-weight: 600;
border-bottom: 2px solid var(--color-accent);
padding-bottom: 0.5rem;
margin-bottom: 1rem;
}
.markdown-text h2 {
font-weight: 600;
margin-top: 1.5rem;
margin-bottom: 0.75rem;
}
.markdown-text h3 {
font-weight: 500;
margin-top: 1rem;
margin-bottom: 0.5rem;
opacity: 0.8;
}
/* Metric cards */
.metrics-row {
gap: 1rem;
}
.metric-card {
padding: 1.5rem;
border-radius: 8px;
}
.metric-card .label {
font-size: 0.875rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.5rem;
opacity: 0.7;
}
.metric-card .value {
font-size: 2rem;
font-weight: 600;
}
/* Status colours */
.positive {
color: #10b981;
font-weight: 600;
}
.negative {
color: #ef4444;
font-weight: 600;
}
.neutral {
font-weight: 500;
opacity: 0.7;
}
/* Data tables */
.dataframe {
border-radius: 6px;
overflow: visible;
}
.dataframe th {
font-weight: 600;
text-transform: uppercase;
font-size: 0.75rem;
letter-spacing: 0.05em;
}
/* Button enhancements */
.primary.lg {
padding: 0.75rem 2rem;
font-weight: 600;
font-size: 1.125rem;
}
.secondary.lg {
padding: 0.75rem 2rem;
font-weight: 600;
font-size: 1.125rem;
}
/* Progress bar */
.progress-bar {
background: linear-gradient(90deg, var(--color-accent), var(--color-accent-soft));
}
/* Accordion styling for ChatMessage metadata */
.message-metadata {
border-left: 3px solid var(--color-accent);
padding-left: 1rem;
margin: 0.5rem 0;
border-radius: 4px;
padding: 0.75rem 1rem;
}
.message-metadata .title {
font-weight: 600;
margin-bottom: 0.25rem;
}
.message-metadata .log {
font-size: 0.875rem;
opacity: 0.7;
}
/* Tool call status indicators */
.status-pending {
color: #f59e0b;
}
.status-done {
color: #10b981;
}
.status-error {
color: #ef4444;
}
/* Loading spinner */
.loading {
border: 3px solid var(--border-color-primary);
border-top-color: var(--color-accent);
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Responsive design */
@media (max-width: 768px) {
.gradio-container {
padding: 1rem;
}
.metric-card .value {
font-size: 1.5rem;
}
}
/* Chart containers */
.plotly-chart {
border-radius: 8px;
padding: 1rem;
}
/* Tab styling */
.tab-nav button.selected {
border-bottom: 2px solid var(--color-accent);
color: var(--color-accent);
font-weight: 600;
}
/* Error messages - theme adaptive */
.error-message {
background: var(--error-background-fill, #fee2e2);
border-left: 4px solid var(--error-border-color, #ef4444);
padding: 1rem;
border-radius: 4px;
color: var(--error-text-color, #991b1b);
}
.dark .error-message {
background: var(--error-background-fill-dark, #450a0a);
color: var(--error-text-color-dark, #fca5a5);
}
/* Success messages - theme adaptive */
.success-message {
background: var(--color-accent-soft-background, #d1fae5);
border-left: 4px solid var(--color-accent-soft, #10b981);
padding: 1rem;
border-radius: 4px;
color: var(--color-accent-soft-text, #065f46);
}
.dark .success-message {
background: var(--color-accent-soft-background-dark, #064e3b);
color: var(--color-accent-soft-text-dark, #6ee7b7);
}
/* Info messages - theme adaptive */
.info-message {
background: var(--info-background-fill, #dbeafe);
border-left: 4px solid var(--button-primary-background-fill, #3b82f6);
padding: 1rem;
border-radius: 4px;
color: var(--info-text-color, #1e40af);
}
.dark .info-message {
background: var(--info-background-fill-dark, #1e3a8a);
color: var(--info-text-color-dark, #93c5fd);
}
"""
def get_financial_theme() -> gr.Theme:
"""Get the financial theme instance.
Returns:
Configured FinancialTheme instance
"""
return FinancialTheme()