"""Visual system of the doctor dashboard: status palette (ADR-006), text tokens, CSS and the plotly template. Status colours are reserved for alert state (red = acute, yellow = high/moderate, green = nothing open, grey = info only). They always ship together with an icon and a text label, never colour alone. Chart series use a separate neutral blue so that a line is never confused with a status. """ from __future__ import annotations from dataclasses import dataclass from typing import Literal Tone = Literal["red", "yellow", "green", "grey", "blue"] @dataclass(frozen=True) class ToneSpec: solid: str # marks, stripes, dots ink: str # text on light surface (WCAG AA on white / on the tint) tint: str # badge / banner background icon: str label: str TONES: dict[str, ToneSpec] = { "red": ToneSpec("#A83236", "#A83236", "#FAEEEE", "🔴", "Красная зона"), "yellow": ToneSpec("#A37524", "#805714", "#FBF3E4", "🟡", "Жёлтая зона"), "green": ToneSpec("#527F61", "#38634B", "#EEF4EE", "🟢", "Стабильно"), "grey": ToneSpec("#9AA0A6", "#565C64", "#F0F1F3", "⚪", "Нет ответа"), "blue": ToneSpec("#2A78D6", "#1F5DA8", "#E8F1FC", "🔵", ""), } INK = "#213B38" INK_2 = "#526660" INK_3 = "#63736F" SURFACE = "#FFFFFF" CANVAS = "#F6F7F3" LINE = "#E1E7E3" SERIES = TONES["blue"].solid FONT_STACK = "-apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif" CSS = f""" """ def plotly_layout(**overrides: object) -> dict[str, object]: """Shared plotly layout: recessive grid, no chart junk, the same font as the page.""" layout: dict[str, object] = { "font": {"family": FONT_STACK, "size": 12, "color": INK_2}, "paper_bgcolor": "rgba(0,0,0,0)", "plot_bgcolor": "rgba(0,0,0,0)", "margin": {"l": 8, "r": 8, "t": 36, "b": 8}, "hoverlabel": {"bgcolor": INK, "font": {"color": "#FFFFFF", "family": FONT_STACK}}, "showlegend": False, "hovermode": "x unified", } layout.update(overrides) return layout AXIS = {"showgrid": True, "gridcolor": LINE, "zeroline": False, "linecolor": LINE, "tickcolor": LINE, "ticks": ""} PLOTLY_CONFIG = {"displayModeBar": False, "responsive": True}