feat(utils): add shared styling and color palette
This commit is contained in:
127
src/utils/styling.py
Normal file
127
src/utils/styling.py
Normal 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",
|
||||
}
|
||||
Reference in New Issue
Block a user