Spaces:
Running
on
Zero
Running
on
Zero
| """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() | |