"""OnCallEnv Red Shift — Ops Room Gradio theme. Visual language: Datadog / Grafana / Linear. Cool grays, single electric-blue accent for primary actions, amber for warnings, red for critical. No neon, no scanlines, no cyberpunk flourishes. Optimized for projector readability during a 3-minute pitch. """ from __future__ import annotations import gradio as gr from gradio.themes.base import Base from gradio.themes.utils import colors, fonts, sizes class OpsRoomTheme(Base): """Production-grade observability aesthetic for OnCallEnv Red Shift.""" def __init__( self, *, primary_hue: colors.Color | str = colors.blue, secondary_hue: colors.Color | str = colors.amber, neutral_hue: colors.Color | str = colors.slate, spacing_size: sizes.Size | str = sizes.spacing_md, radius_size: sizes.Size | str = sizes.radius_md, text_size: sizes.Size | str = sizes.text_md, font: fonts.Font | str | list = ( fonts.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif", ), font_mono: fonts.Font | str | list = ( fonts.GoogleFont("JetBrains Mono"), "ui-monospace", "Consolas", "monospace", ), ): super().__init__( primary_hue=primary_hue, secondary_hue=secondary_hue, neutral_hue=neutral_hue, spacing_size=spacing_size, radius_size=radius_size, text_size=text_size, font=font, font_mono=font_mono, ) # Body / surface self.body_background_fill = "#0b1020" self.body_background_fill_dark = "#0b1020" self.body_text_color = "#d6deeb" self.body_text_color_dark = "#d6deeb" self.body_text_color_subdued = "#7d8ba1" self.body_text_color_subdued_dark = "#7d8ba1" # Blocks / panels self.block_background_fill = "#131a2e" self.block_background_fill_dark = "#131a2e" self.block_border_color = "#1f2940" self.block_border_color_dark = "#1f2940" self.block_border_width = "1px" self.block_label_background_fill = "#0f1626" self.block_label_background_fill_dark = "#0f1626" self.block_label_text_color = "#7aa7ff" self.block_label_text_color_dark = "#7aa7ff" self.block_shadow = "0 1px 3px rgba(0,0,0,0.25)" self.block_shadow_dark = "0 1px 3px rgba(0,0,0,0.25)" self.block_title_text_color = "#e6edf7" self.block_title_text_color_dark = "#e6edf7" # Borders self.border_color_accent = "#3b82f6" self.border_color_accent_dark = "#3b82f6" self.border_color_primary = "#1f2940" self.border_color_primary_dark = "#1f2940" self.panel_background_fill = "#0f1626" self.panel_background_fill_dark = "#0f1626" self.panel_border_color = "#1f2940" self.panel_border_color_dark = "#1f2940" # Primary button (electric blue) self.button_primary_background_fill = "#3b82f6" self.button_primary_background_fill_dark = "#3b82f6" self.button_primary_background_fill_hover = "#60a5fa" self.button_primary_background_fill_hover_dark = "#60a5fa" self.button_primary_text_color = "#ffffff" self.button_primary_text_color_dark = "#ffffff" self.button_primary_border_color = "#2563eb" self.button_primary_border_color_dark = "#2563eb" # Secondary button self.button_secondary_background_fill = "#1f2940" self.button_secondary_background_fill_dark = "#1f2940" self.button_secondary_background_fill_hover = "#2a3656" self.button_secondary_background_fill_hover_dark = "#2a3656" self.button_secondary_text_color = "#d6deeb" self.button_secondary_text_color_dark = "#d6deeb" self.button_secondary_border_color = "#2a3656" self.button_secondary_border_color_dark = "#2a3656" # Inputs self.input_background_fill = "#0f1626" self.input_background_fill_dark = "#0f1626" self.input_border_color = "#1f2940" self.input_border_color_dark = "#1f2940" self.input_border_color_focus = "#3b82f6" self.input_border_color_focus_dark = "#3b82f6" self.input_placeholder_color = "#5b6577" self.input_placeholder_color_dark = "#5b6577" self.input_text_color = "#d6deeb" self.input_text_color_dark = "#d6deeb" # Checkbox self.checkbox_background_color = "#0f1626" self.checkbox_background_color_dark = "#0f1626" self.checkbox_background_color_selected = "#3b82f6" self.checkbox_background_color_selected_dark = "#3b82f6" self.checkbox_border_color = "#2a3656" self.checkbox_border_color_dark = "#2a3656" self.checkbox_border_color_selected = "#3b82f6" self.checkbox_border_color_selected_dark = "#3b82f6" # Tables / code self.table_border_color = "#1f2940" self.table_border_color_dark = "#1f2940" self.table_even_background_fill = "#131a2e" self.table_even_background_fill_dark = "#131a2e" self.table_odd_background_fill = "#0f1626" self.table_odd_background_fill_dark = "#0f1626" self.code_background_fill = "#0f1626" self.code_background_fill_dark = "#0f1626" # Shadows self.shadow_drop = "0 1px 3px rgba(0,0,0,0.3)" self.shadow_drop_lg = "0 4px 12px rgba(0,0,0,0.4)" # -------------------------------------------------------------------------- # Custom CSS — overrides + bespoke components # -------------------------------------------------------------------------- CUSTOM_CSS = """ :root { --ops-bg: #0b1020; --ops-surface: #131a2e; --ops-surface-alt: #0f1626; --ops-border: #1f2940; --ops-border-strong: #2a3656; --ops-text: #d6deeb; --ops-text-dim: #7d8ba1; --ops-text-bright: #e6edf7; --ops-blue: #3b82f6; --ops-blue-bright: #60a5fa; --ops-amber: #f59e0b; --ops-red: #ef4444; --ops-green: #10b981; --ops-purple: #8b5cf6; } footer { display: none !important; } .gradio-container { background: var(--ops-bg) !important; max-width: 100% !important; padding: 0 !important; } .gradio-container > .main { max-width: 1600px; margin: 0 auto; padding: 0 24px; } /* ============== TAB HEADERS ============== */ .tab-nav button { background: transparent !important; color: var(--ops-text-dim) !important; border: none !important; border-bottom: 2px solid transparent !important; font-family: 'Inter', sans-serif !important; font-size: 0.875rem !important; font-weight: 500 !important; letter-spacing: 0.01em; padding: 12px 20px !important; transition: all 0.15s ease; } .tab-nav button:hover { color: var(--ops-text-bright) !important; } .tab-nav button.selected { color: var(--ops-blue-bright) !important; border-bottom: 2px solid var(--ops-blue) !important; background: transparent !important; } /* ============== HEADER BANNER ============== */ .ops-header { padding: 28px 24px 24px; background: linear-gradient(180deg, #0d1428 0%, var(--ops-bg) 100%); border-bottom: 1px solid var(--ops-border); margin-bottom: 16px; } .ops-header-row { display: flex; align-items: center; justify-content: space-between; gap: 24px; flex-wrap: wrap; } .ops-header h1 { font-family: 'Inter', sans-serif; font-size: 1.875rem; font-weight: 700; color: var(--ops-text-bright); margin: 0 0 4px 0; letter-spacing: -0.02em; } .ops-header .subtitle { font-family: 'Inter', sans-serif; font-size: 0.875rem; color: var(--ops-text-dim); letter-spacing: 0.01em; } .ops-header-meta { display: flex; gap: 8px; flex-wrap: wrap; } .ops-pill { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 4px; font-family: 'JetBrains Mono', monospace; font-size: 0.7rem; font-weight: 500; letter-spacing: 0.04em; border: 1px solid var(--ops-border-strong); background: var(--ops-surface-alt); color: var(--ops-text-dim); } .ops-pill-blue { color: var(--ops-blue-bright); border-color: rgba(59, 130, 246, 0.3); background: rgba(59, 130, 246, 0.08); } .ops-pill-green { color: var(--ops-green); border-color: rgba(16, 185, 129, 0.3); background: rgba(16, 185, 129, 0.08); } .ops-pill .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; } /* ============== CARDS ============== */ .ops-card { background: var(--ops-surface) !important; border: 1px solid var(--ops-border) !important; border-radius: 8px !important; padding: 16px !important; } /* ============== PRIMARY BUTTON ============== */ button.primary { font-weight: 600 !important; letter-spacing: 0.01em !important; transition: all 0.15s ease !important; box-shadow: 0 1px 2px rgba(0,0,0,0.3) !important; } button.primary:hover { transform: translateY(-1px); box-shadow: 0 4px 8px rgba(59, 130, 246, 0.25) !important; } /* ============== TYPOGRAPHY ============== */ .prose h1, .prose h2, .prose h3, .prose h4 { color: var(--ops-text-bright) !important; font-family: 'Inter', sans-serif !important; letter-spacing: -0.01em; } .prose h2 { font-size: 1.25rem !important; margin-top: 1.5rem !important; } .prose h3 { font-size: 1.05rem !important; margin-top: 1.25rem !important; } .prose p, .prose li { color: var(--ops-text) !important; line-height: 1.65; } .prose strong { color: var(--ops-text-bright) !important; font-weight: 600; } .prose a { color: var(--ops-blue-bright) !important; } .prose code { background: var(--ops-surface-alt) !important; color: var(--ops-blue-bright) !important; padding: 2px 6px; border-radius: 4px; font-family: 'JetBrains Mono', monospace !important; font-size: 0.875em; border: 1px solid var(--ops-border); } .prose pre { background: var(--ops-surface-alt) !important; border: 1px solid var(--ops-border) !important; border-radius: 6px; } .prose hr { border-color: var(--ops-border) !important; } .prose table { border-collapse: collapse; font-size: 0.875rem; } .prose th { background: var(--ops-surface-alt) !important; color: var(--ops-text-bright) !important; border: 1px solid var(--ops-border) !important; padding: 8px 12px !important; text-align: left; } .prose td { border: 1px solid var(--ops-border) !important; padding: 6px 12px !important; } /* ============== SCROLLBAR ============== */ ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { background: var(--ops-bg); } ::-webkit-scrollbar-thumb { background: var(--ops-border-strong); border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: #3a4a6a; } """ HEADER_HTML = """