""" F1 Predictor 2026 - Theme System Consolidated: colors, CSS, page config, plotly helpers, team colors """ import streamlit as st import streamlit.components.v1 as components # ── Color Palette ─────────────────────────────────────────── # STRICT: skyblue, darkblue, darkgray, black, red, green ONLY COLORS = { "skyblue": "#3B82F6", "skyblue_dark": "#2563EB", "skyblue_light": "#60A5FA", "darkblue": "#1E3A8A", "darkgray": "#475569", "black": "#0F172A", "red": "#EF4444", "green": "#10B981", "amber": "#F59E0B", "cyan": "#06B6D4", "gold": "#FBBF24", "silver": "#9CA3AF", "bronze": "#D97706", "bg": "#F8FAFC", "bg_card": "#F1F5F9", "border": "#E2E8F0", "text": "#0F172A", "text_secondary": "#475569", "text_muted": "#94A3B8", "team_red_bull": "#1E41FF", "team_ferrari": "#DC0000", "team_mercedes": "#00D2BE", "team_mclaren": "#FF8000", "team_aston_martin": "#006F62", "team_alpine": "#0093CC", "team_williams": "#005AFF", "team_rb": "#2B4562", "team_kick_sauber": "#00E701", "team_haas": "#B6BABD", } TEAM_COLOR_MAP = { "red_bull": "#1E41FF", "ferrari": "#DC0000", "mercedes": "#00D2BE", "mclaren": "#FF8000", "aston_martin": "#006F62", "alpine": "#0093CC", "williams": "#005AFF", "rb": "#2B4562", "kick_sauber": "#00E701", "haas": "#B6BABD", "red bull": "#1E41FF", "aston martin": "#006F62", "kick sauber": "#00E701", "racing bulls": "#2B4562", "sauber": "#00E701", "visa cash app rb": "#2B4562", } def bar_colors_from_teams(predictions): """Generate bar colors based on team affiliation.""" return [TEAM_COLOR_MAP.get( p.get("team", "").lower().strip().replace(" ", "_").replace("-", "_"), "#3B82F6" ) for p in predictions] def f1_plotly_layout(title="", show_legend=False): """Return a Plotly layout dict styled for the F1 light theme.""" return { "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)", "font": {"color": "#0F172A", "family": "Inter, sans-serif"}, "title": {"text": title, "font": {"color": "#0F172A", "size": 18}}, "xaxis": { "gridcolor": "rgba(71,85,105,0.15)", "linecolor": "rgba(71,85,105,0.3)", "tickfont": {"color": "#475569"}, "title": {"font": {"color": "#475569"}}, }, "yaxis": { "gridcolor": "rgba(71,85,105,0.15)", "linecolor": "rgba(71,85,105,0.3)", "tickfont": {"color": "#475569"}, "title": {"font": {"color": "#475569"}}, }, "legend": {"font": {"color": "#475569"}, "bgcolor": "rgba(0,0,0,0)"} if show_legend else {}, "margin": {"l": 60, "r": 20, "t": 60, "b": 60}, } # ── Complete CSS ────────────────────────────────────────── CSS = """ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap'); html, body, .stApp, [data-testid="stAppViewContainer"], .main, .block-container { background: #F8FAFC !important; background-color: #F8FAFC !important; } body { font-family: 'Inter', -apple-system, sans-serif; color: #0F172A; line-height: 1.6; -webkit-font-smoothing: antialiased; } #MainMenu, header, footer, .stApp > header, [data-testid="stToolbar"] { display: none !important; } /* Top Navigation Bar */ .top-nav-bar { display: flex; justify-content: center; gap: 1rem; background: #F8FAFC; border-bottom: 1px solid #E2E8F0; padding: 0.75rem 2rem; position: sticky; top: 0; z-index: 1000; backdrop-filter: blur(10px); } .nav-item { padding: 0.5rem 1rem; border-radius: 8px; cursor: pointer; font-weight: 500; transition: all 0.2s; color: #475569; border: 1px solid transparent; text-decoration: none; display: inline-block; } .nav-item.active { background: linear-gradient(135deg, #3B82F6, #2563EB); color: #F8FAFC; border: 1px solid #3B82F6; } .nav-item:hover:not(.active) { background: #F1F5F9; border: 1px solid #E2E8F0; } /* Buttons */ .stButton > button { background: linear-gradient(135deg, #3B82F6, #2563EB) !important; color: #F8FAFC !important; border: none !important; border-radius: 8px !important; padding: 0.6rem 1.5rem !important; font-weight: 600 !important; transition: all 0.2s ease !important; } .stButton > button:hover { transform: translateY(-1px) !important; box-shadow: 0 4px 12px rgba(59,130,246,0.3) !important; } /* Inputs */ .stSelectbox > div > div, .stNumberInput > div > div, .stSlider > div > div { background: #F1F5F9 !important; border: 1px solid #E2E8F0 !important; border-radius: 8px !important; } .stSelectbox label, .stNumberInput label, .stSlider label, .stCheckbox label { color: #475569 !important; font-weight: 600 !important; } /* Metrics */ [data-testid="stMetric"] { background: #F1F5F9 !important; border: 1px solid #E2E8F0 !important; border-radius: 10px !important; padding: 1rem !important; } [data-testid="stMetric"] label { color: #94A3B8 !important; font-size: 0.75rem !important; text-transform: uppercase; letter-spacing: 0.5px; } [data-testid="stMetric"] .css-1xarl3l { color: #0F172A !important; font-size: 1.5rem !important; font-weight: 800 !important; } /* Tabs */ .stTabs [data-baseweb="tab-list"] { background: #F1F5F9 !important; border-radius: 10px 10px 0 0 !important; border-bottom: 2px solid #E2E8F0 !important; } .stTabs [data-baseweb="tab"] { color: #94A3B8 !important; font-weight: 500 !important; } .stTabs [aria-selected="true"] { color: #3B82F6 !important; border-bottom: 2px solid #3B82F6 !important; } /* DataFrames */ .stDataFrame { background: #F1F5F9 !important; border-radius: 10px !important; border: 1px solid #E2E8F0 !important; } .stDataFrame th { background: #E2E8F0 !important; color: #0F172A !important; font-weight: 700 !important; } .stDataFrame td { color: #1E293B !important; } .stDataFrame tr:hover td { background: #F8FAFC !important; } /* Expanders */ .stExpander { background: #F1F5F9 !important; border: 1px solid #E2E8F0 !important; border-radius: 10px !important; } /* Sidebar */ section[data-testid="stSidebar"] { background: #F1F5F9 !important; border-right: 1px solid #E2E8F0 !important; } section[data-testid="stSidebar"] .stMarkdown, section[data-testid="stSidebar"] label { color: #0F172A !important; } /* Sidebar Toggle Button - POSITIONED & VISIBLE */ button[kind="icon"][data-testid="collapsedControl"], [data-testid="collapsedControl"], .stSidebarToggle { background-color: #EF4444 !important; border: 2px solid #DC2626 !important; border-radius: 6px !important; box-shadow: 0 2px 8px rgba(239,68,68,0.3) !important; position: fixed !important; top: 10px !important; left: 250px !important; z-index: 9999 !important; width: 40px !important; height: 40px !important; min-width: 40px !important; min-height: 40px !important; padding: 8px !important; opacity: 1 !important; visibility: visible !important; } button[kind="icon"][data-testid="collapsedControl"]:hover, [data-testid="collapsedControl"]:hover, .stSidebarToggle:hover { background-color: #DC2626 !important; transform: scale(1.05); } button[kind="icon"][data-testid="collapsedControl"] svg, [data-testid="collapsedControl"] svg, .stSidebarToggle svg { fill: #FFFFFF !important; stroke: #FFFFFF !important; } /* Radio */ .stRadio > div { background: #F1F5F9 !important; border-radius: 8px !important; padding: 0.5rem !important; } .stRadio label span { color: #475569 !important; } /* Alerts */ .stAlert { background: #F1F5F9 !important; border: 1px solid #E2E8F0 !important; border-radius: 10px !important; } .stAlert [data-testid="stAlertContent"] { color: #0F172A !important; } /* Scrollbar */ ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { background: #F8FAFC; } ::-webkit-scrollbar-thumb { background: #CBD5E0; border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: #94A3B8; } /* Plotly */ .js-plotly-plot .plotly { border-radius: 10px; } """ def apply_theme(): """Inject theme CSS into Streamlit.""" js_code = """ """ st.markdown(f"", unsafe_allow_html=True) st.components.v1.html(js_code, height=0) def apply_page_config(): """Set Streamlit page config.""" st.set_page_config( page_title="F1 Predictor 2026", page_icon="🏎️", layout="wide", initial_sidebar_state="expanded", )