feat(utils): add shared styling and color palette

This commit is contained in:
Orchestrator
2026-06-04 17:08:24 -05:00
parent d87246658f
commit 75faf181a8

127
src/utils/styling.py Normal file
View File

@@ -0,0 +1,127 @@
"""Shared styling utilities and color palette for all charts."""
from typing import Optional
import matplotlib.figure
import matplotlib.axes
import matplotlib.pyplot as plt
# ---------------------------------------------------------------------------
# Export & sizing constants
# ---------------------------------------------------------------------------
EXPORT_DPI = 300
FIGURE_SIZE_DEFAULT = (12, 7) # width, height in inches
FIGURE_SIZE_SQUARE = (8, 8)
FIGURE_SIZE_WIDE = (16, 10)
# ---------------------------------------------------------------------------
# Zone colors (risk / status shading)
# ---------------------------------------------------------------------------
BUBBLE_ZONE = "#e74c3c" # Red — danger/bubble
WARNING_ZONE = "#f39c12" # Orange — warning
NORMAL_ZONE = "#27ae60" # Green — normal/healthy
# ---------------------------------------------------------------------------
# Data series colors
# ---------------------------------------------------------------------------
AI_SPEND = "#2980b9" # Blue — AI spending
REVENUE = "#27ae60" # Green — revenue
AGENT_GROWTH = "#8e44ad" # Purple — agent adoption
DEBT = "#c0392b" # Dark red — debt
PRODUCTIVITY = "#16a085" # Teal — productivity metrics
# ---------------------------------------------------------------------------
# Neutral colors
# ---------------------------------------------------------------------------
GRAY_LIGHT = "#ecf0f1"
GRAY_MEDIUM = "#95a5a6"
GRAY_DARK = "#2c3e50"
BLACK = "#1a1a2e"
WHITE = "#ffffff"
# ---------------------------------------------------------------------------
# Theme
# ---------------------------------------------------------------------------
def get_theme() -> dict:
"""Return a matplotlib rcParams dict with a clean, professional look."""
return {
"font.family": "DejaVu Sans",
"font.size": 12,
"figure.facecolor": WHITE,
"figure.dpi": EXPORT_DPI,
"axes.facecolor": "#fafafa",
"axes.edgecolor": "#dddddd",
"axes.grid": True,
"axes.axisbelow": True,
"grid.color": "#e0e0e0",
"grid.linestyle": "-",
"grid.linewidth": 0.5,
"grid.alpha": 0.7,
"xtick.labelsize": 10,
"ytick.labelsize": 10,
"axes.titlesize": 16,
"axes.titleweight": "bold",
"axes.labelsize": 12,
"legend.fontsize": 9,
"figure.titlesize": 16,
"figure.titleweight": "bold",
"savefig.dpi": EXPORT_DPI,
"savefig.facecolor": WHITE,
"savefig.bbox": "tight",
}
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def apply_theme(fig: matplotlib.figure.Figure, ax: Optional[matplotlib.axes.Axes] = None) -> None:
"""Apply the professional theme to a figure and optionally its axes.
Parameters
----------
fig : matplotlib Figure
The figure to theme.
ax : matplotlib Axes, optional
If provided, spine cleanup is applied to this specific axes.
If *None*, every axes in the figure is cleaned.
"""
plt.rcParams.update(get_theme())
fig.set_facecolor(WHITE)
targets = [ax] if ax is not None else fig.axes
for axes in targets:
axes.set_facecolor("#fafafa")
axes.spines["top"].set_visible(False)
axes.spines["right"].set_visible(False)
axes.spines["left"].set_color("#cccccc")
axes.spines["bottom"].set_color("#cccccc")
def get_bubble_zone_colors() -> dict:
"""Return zone colours suitable for shaded risk regions."""
return {
"bubble": BUBBLE_ZONE,
"warning": WARNING_ZONE,
"normal": NORMAL_ZONE,
}
def get_company_colors() -> dict:
"""Return consistent brand-colour mapping for hyperscalers."""
return {
"Microsoft": "#00a4ef",
"Alphabet": "#4285f4",
"Meta": "#1877f2",
"Amazon": "#ff9900",
}