Skip to content

components

tit.reporting.html.components

Report design system: page shell, components, SVG charts and the QC-check record.

One module and one stylesheet (report.css) for every TI-Toolbox report built on it (ARCHITECTURE.md §14). A page makes no network request: CSS, the widget script below, the IBM Plex fonts (fonts/, embedded once) and every image are inline, which is what the in-app iframe CSP (tit/server/routes/files.py::REPORT_CSP) allows. No template engine, no library.

A pipeline reports QC as :class:Check rows whose role comes from tit.reporting.qc_rules: gate and internal rows block the output, advisory rows warn, report rows show a value against a reference. A blocking row's status must come from the pipeline's own failure list (:func:gate_status), so a table can never disagree with the verdict.

Cites

Cites()

Collects what a page cites, by registry key or DOI, so the reference list shows exactly those.

Source code in tit/reporting/html/components.py
def __init__(self) -> None:
    self.refs: dict[str, dict] = {}

Check dataclass

Check(id: str, label: str, description: str, shown: str, threshold: str, status: str, role: str = 'gate', value: float | None = None, rail: tuple[float, float, float, float] | None = None, cite: tuple[str, ...] | list[str] = (), note: str = '')

One QC row: what was measured, against which rule, why, and whether it passed.

role is the rule's role (gate, internal, advisory, report); description is its plain-text sentence (backticks mark code), cite its DOIs and note where the value is TI-Toolbox's own. rail is (lo, hi, ok_lo, ok_hi) for the gate rail, or None.

inline

inline(text: str) -> str

Escape plain text for HTML; backticks become <code>. For text stored in QC records.

Source code in tit/reporting/html/components.py
def inline(text: str) -> str:
    """Escape plain text for HTML; `backticks` become ``<code>``. For text stored in QC records."""
    parts = esc(text).split("`")
    return "".join(f"<code>{p}</code>" if i % 2 else p for i, p in enumerate(parts))

img

img(raw: bytes, alt: str, mime: str = 'image/webp') -> str

An inline image. alt is required: every report image says what it shows.

Source code in tit/reporting/html/components.py
def img(raw: bytes, alt: str, mime: str = "image/webp") -> str:
    """An inline image. *alt* is required: every report image says what it shows."""
    if not alt:
        raise ValueError("report images need alt text")
    return f'<img src="{data_uri(raw, mime)}" alt="{esc(alt)}" decoding="async">'

status

status(kind: str, label: str | None = None, pill: bool = False) -> str

Status as icon + word, never colour alone.

Source code in tit/reporting/html/components.py
def status(kind: str, label: str | None = None, pill: bool = False) -> str:
    """Status as icon + word, never colour alone."""
    label = STATUS_WORD[kind] if label is None else label
    return f'<span class="st {kind}{f" pill {kind}" if pill else ""}">{icon(kind)}{esc(label)}</span>'

page

page(*, title: str, kind: str, subject: str, toc: list[tuple[str, str, str | None]], body: str, footer: str = '', description: str = '', generator: str = 'TI-Toolbox', extra_css: str = '') -> str

The whole document. toc rows are (anchor, label, status kind or None).

Source code in tit/reporting/html/components.py
def page(
    *,
    title: str,
    kind: str,
    subject: str,
    toc: list[tuple[str, str, str | None]],
    body: str,
    footer: str = "",
    description: str = "",
    generator: str = "TI-Toolbox",
    extra_css: str = "",
) -> str:
    """The whole document. *toc* rows are ``(anchor, label, status kind or None)``."""
    toc_html = "".join(
        f'<li><a href="#{a}" aria-current="false">{esc(text)}'
        + (
            f'<span class="st {s}" title="{STATUS_WORD[s]}">{icon(s, 13)}<span class="sr">{STATUS_WORD[s]}</span></span>'
            if s
            else ""
        )
        + "</a></li>"
        for a, text, s in toc
    )
    return f"""<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="{esc(description)}">
<meta name="generator" content="{esc(generator)}">
<title>{esc(title)}</title>
<style>{_head_assets()}{extra_css}</style>
</head>
<body>
<div class="shell">
<nav class="rail" aria-label="Report contents">
  <a class="brand" href="#top">{_mark()}<div><b>TI-Toolbox</b><span>{esc(kind)}</span></div></a>
  <ol class="toc">{toc_html}</ol>
  <div class="rail-foot">
    <div class="meta">{esc(subject)}</div>
    <button class="tbtn" id="theme-btn" type="button" aria-label="Switch colour theme"><span>Theme: system</span></button>
  </div>
</nav>
<main class="sheet" id="top">
<div class="sheet-inner">
{body}
<footer class="foot">{footer}</footer>
</div>
</main>
</div>
<script>{_JS}</script>
</body>
</html>"""

masthead

masthead(kind: str, title: str, meta: list[tuple[str, str]]) -> str

Title block with a definition list of facts; meta values are trusted HTML.

Source code in tit/reporting/html/components.py
def masthead(kind: str, title: str, meta: list[tuple[str, str]]) -> str:
    """Title block with a definition list of facts; *meta* values are trusted HTML."""
    dl = "".join(f"<div><dt>{esc(k)}</dt><dd>{v}</dd></div>" for k, v in meta)
    return (
        f'<header class="mast"><div><div class="kind">{esc(kind)}</div><h1>{esc(title)}</h1></div>'
        f'<dl style="--n:{min(len(meta), 4)}">{dl}</dl></header>'
    )

figure

figure(num: int, title: str, content: str, caption: str, controls: str = '', cls: str = '', attrs: str = '') -> str

A numbered figure; every figure has a caption that says what good looks like.

Source code in tit/reporting/html/components.py
def figure(
    num: int,
    title: str,
    content: str,
    caption: str,
    controls: str = "",
    cls: str = "",
    attrs: str = "",
) -> str:
    """A numbered figure; every figure has a caption that says what good looks like."""
    return (
        f'<figure class="fig {cls}" {attrs}><div class="fig-bar"><span class="ttl">Figure {num}. {esc(title)}</span>'
        f'<span class="ctl">{controls}</span></div>{content}<figcaption>{caption}</figcaption></figure>'
    )

label

label(text: str, style: str) -> str

A small label positioned over a lightbox image (style is CSS for its position).

Source code in tit/reporting/html/components.py
def label(text: str, style: str) -> str:
    """A small label positioned over a lightbox image (*style* is CSS for its position)."""
    return f'<span class="lb-label" style="{style}">{esc(text)}</span>'

tile_labels

tile_labels(labels: list[str], cols: int, tile: tuple[int, int], shape: tuple[int, int], gap: int = 4) -> str

Labels at the top-left of each mosaic tile, placed in percent of the mosaic.

Source code in tit/reporting/html/components.py
def tile_labels(
    labels: list[str],
    cols: int,
    tile: tuple[int, int],
    shape: tuple[int, int],
    gap: int = 4,
) -> str:
    """Labels at the top-left of each mosaic tile, placed in percent of the mosaic."""
    th, tw = tile
    height, width = shape[:2]
    return "".join(
        label(
            text,
            f"left:calc({100 * (n % cols) * (tw + gap) / width:.3f}% + 6px);top:calc({100 * (n // cols) * (th + gap) / height:.3f}% + 5px)",
        )
        for n, text in enumerate(labels)
    )

flicker

flicker(a: bytes, b: bytes, la: str, lb: str, alt: str, labels: str = '') -> str

Two stacked images of one view on the dark imaging surround; the page flickers between them.

Source code in tit/reporting/html/components.py
def flicker(a: bytes, b: bytes, la: str, lb: str, alt: str, labels: str = "") -> str:
    """Two stacked images of one view on the dark imaging surround; the page flickers between them."""
    return (
        f'<div class="lightbox"><div class="lb-inner flick" tabindex="0" aria-label="{esc(alt)}; press space to switch">'
        f"{img(a, f'{alt} — {la}')}{img(b, f'{alt} — {lb}')}{labels}"
        f'<span class="tag" aria-live="polite"></span></div></div>'
    )

segmented

segmented(buttons: list[tuple[str, str]], group_label: str, attr: str) -> str

A row of toggle buttons: attr="data-mode" drives a flicker, "data-tab" panel tabs.

Source code in tit/reporting/html/components.py
def segmented(buttons: list[tuple[str, str]], group_label: str, attr: str) -> str:
    """A row of toggle buttons: ``attr="data-mode"`` drives a flicker, ``"data-tab"`` panel tabs."""
    inner = "".join(
        f'<button type="button" {attr}="{k}">{esc(v)}</button>' for k, v in buttons
    )
    tabs = " data-tabs" if attr == "data-tab" else ""
    return f'<span class="seg" role="group" aria-label="{esc(group_label)}"{tabs}>{inner}</span>'

scrubber

scrubber(num: int, frames: list[bytes], labels: list[str], start: int, alt: str, extra: str = '') -> str

One image and a slider; the frames live in a JSON block and are swapped by the page script.

Source code in tit/reporting/html/components.py
def scrubber(
    num: int,
    frames: list[bytes],
    labels: list[str],
    start: int,
    alt: str,
    extra: str = "",
) -> str:
    """One image and a slider; the frames live in a JSON block and are swapped by the page script."""
    uris = [data_uri(f, "image/webp") for f in frames]
    data = json.dumps({"frames": uris, "labels": labels, "alt": alt})
    return (
        f'<div class="scrub"><div class="lightbox"><div class="lb-inner"><img src="{uris[start]}" alt="{esc(alt)} {esc(labels[start])}">{extra}</div></div>'
        f'<div class="scrub-row"><label for="scrub{num}">Slice</label>'
        f'<input id="scrub{num}" type="range" min="0" max="{len(frames) - 1}" value="{start}" step="1">'
        f'<output for="scrub{num}">{esc(labels[start])}</output></div>'
        f'<script type="application/json">{data}</script></div>'
    )

table

table(head: list[str], rows: list[list[str | tuple[str, float]]], caption: str = '', right: set[int] = frozenset(), cls: str = '', sortable: bool = False) -> str

A data table; cells are trusted HTML. Columns in right are right-aligned and tabular.

sortable makes every header sort the rows (click or Enter); a cell given as (html, value) sorts by value instead of its text.

Source code in tit/reporting/html/components.py
def table(
    head: list[str],
    rows: list[list[str | tuple[str, float]]],
    caption: str = "",
    right: set[int] = frozenset(),
    cls: str = "",
    sortable: bool = False,
) -> str:
    """A data table; cells are trusted HTML. Columns in *right* are right-aligned and tabular.

    *sortable* makes every header sort the rows (click or Enter); a cell given as
    ``(html, value)`` sorts by *value* instead of its text.
    """
    th = "".join(
        f'<th scope="col" class="{"r" if i in right else ""}">{h}</th>'
        for i, h in enumerate(head)
    )

    def td(i: int, cell) -> str:
        html_, v = cell if isinstance(cell, tuple) else (cell, None)
        dv = f' data-v="{v:.6g}"' if v is not None else ""
        return f'<td class="{"r num" if i in right else ""}"{dv}>{html_}</td>'

    body = "".join(
        "<tr>" + "".join(td(i, c) for i, c in enumerate(row)) + "</tr>" for row in rows
    )
    cap = f"<caption>{caption}</caption>" if caption else ""
    cls = f"{cls} sortable" if sortable else cls
    return f'<div class="tbl-wrap"><table class="tbl {cls}">{cap}<thead><tr>{th}</tr></thead><tbody>{body}</tbody></table></div>'

stats

stats(items: list[tuple[str, str, str]]) -> str

The key-number strip: (label, value HTML, note) per tile.

Source code in tit/reporting/html/components.py
def stats(items: list[tuple[str, str, str]]) -> str:
    """The key-number strip: ``(label, value HTML, note)`` per tile."""
    tiles = "".join(
        f'<div><dt>{esc(k)}</dt><dd><b>{v}</b>{f"<span>{esc(n)}</span>" if n else ""}</dd></div>'
        for k, v, n in items
    )
    return f'<dl class="stats">{tiles}</dl>'

verdict_section

verdict_section(seal: str, headline: str, lede: str, extra: str = '') -> str

The first section: seal, headline, lede (trusted HTML) and whatever follows (extra).

Source code in tit/reporting/html/components.py
def verdict_section(seal: str, headline: str, lede: str, extra: str = "") -> str:
    """The first section: seal, headline, lede (trusted HTML) and whatever follows (*extra*)."""
    return (
        f'<section class="sec" id="verdict" style="padding-top:0" aria-labelledby="verdict-h"><div class="verdict">'
        f'<div class="verdict-head"><span class="seal {seal}" role="img" aria-label="{STATUS_WORD[seal]}">{icon(seal, 26)}</span>'
        f'<div><h2 id="verdict-h">{esc(headline)}</h2><p class="lede">{lede}</p></div></div></div>{extra}</section>'
    )

kv

kv(pairs: list[tuple[str, str]]) -> str

A definition list; values are trusted HTML.

Source code in tit/reporting/html/components.py
def kv(pairs: list[tuple[str, str]]) -> str:
    """A definition list; values are trusted HTML."""
    return (
        '<dl class="kv">'
        + "".join(f"<dt>{esc(k)}</dt><dd>{v}</dd>" for k, v in pairs)
        + "</dl>"
    )

references

references(refs: list[dict]) -> str

Reference list from tit.reporting.references entries (label, citation, doi).

Source code in tit/reporting/html/components.py
def references(refs: list[dict]) -> str:
    """Reference list from ``tit.reporting.references`` entries (label, citation, doi)."""
    items = "".join(
        f'<li id="ref-{esc(r["label"])}"><span class="k">{esc(r["label"])}</span><span>{esc(r["citation"])}'
        + (
            f' <a href="https://doi.org/{esc(r["doi"])}">doi:{esc(r["doi"])}</a>'
            if r.get("doi")
            else ""
        )
        + "</span></li>"
        for r in refs
    )
    return f'<ol class="refs">{items}</ol>'

hist_chart

hist_chart(series: list[dict], bins: list[float], *, xlabel: str, band: tuple[float, float, str] | None = None, xlim: tuple[float, float] | None = None) -> str

Density outlines (step lines + 10 % wash) with one hover band per bin.

series: {label, values (fraction per bin), var ('--s1' ...)}; band: (lo, hi, label).

Source code in tit/reporting/html/components.py
def hist_chart(
    series: list[dict],
    bins: list[float],
    *,
    xlabel: str,
    band: tuple[float, float, str] | None = None,
    xlim: tuple[float, float] | None = None,
) -> str:
    """Density outlines (step lines + 10 % wash) with one hover band per bin.

    *series*: ``{label, values (fraction per bin), var ('--s1' ...)}``; *band*: ``(lo, hi, label)``.
    """
    width, height, ml, mr, mt, mb = 490, 230, 40, 12, 22, 38
    pw, ph = width - ml - mr, height - mt - mb
    lo, hi = (bins[0], bins[-1]) if xlim is None else xlim
    ymax = max(max(s["values"]) for s in series) * 1.12 or 1.0

    def X(v: float) -> float:
        return ml + (v - lo) / (hi - lo) * pw

    def Y(v: float) -> float:
        return mt + ph - v / ymax * ph

    shown = [i for i in range(len(bins) - 1) if bins[i + 1] > lo and bins[i] < hi]
    p = [
        f'<svg class="chart" width="{width}" height="{height}" viewBox="0 0 {width} {height}" role="img" aria-label="{esc(xlabel)}">'
    ]
    for t in nice_ticks(0, ymax, 4):
        p.append(
            f'<line class="grid" x1="{ml}" x2="{ml + pw}" y1="{Y(t):.1f}" y2="{Y(t):.1f}"/>'
            f'<text x="{ml - 6}" y="{Y(t) + 4:.1f}" text-anchor="end">{t * 100:.0f}%</text>'
        )
    if band:
        b0, b1, text = band
        p.append(
            f'<rect x="{X(b0):.1f}" y="{mt}" width="{X(b1) - X(b0):.1f}" height="{ph}" fill="var(--band)" stroke="var(--band-edge)"/>'
            f'<text x="{(X(b0) + X(b1)) / 2:.1f}" y="{mt - 4}" text-anchor="middle" style="fill:var(--pass)">{esc(text)}</text>'
        )
    for t in nice_ticks(lo, hi, 6):
        p.append(
            f'<text x="{X(t):.1f}" y="{mt + ph + 16}" text-anchor="middle">{t:.1f}</text>'
        )
    p.append(
        f'<line class="axis" x1="{ml}" x2="{ml + pw}" y1="{mt + ph}" y2="{mt + ph}"/>'
        f'<text x="{ml + pw / 2:.1f}" y="{height - 4}" text-anchor="middle">{esc(xlabel)}</text>'
    )
    for s in series:
        pts = " ".join(
            f"{X(max(bins[i], lo)):.1f},{Y(s['values'][i]):.1f} {X(min(bins[i + 1], hi)):.1f},{Y(s['values'][i]):.1f}"
            for i in shown
        )
        area = f"{X(max(bins[shown[0]], lo)):.1f},{Y(0):.1f} {pts} {X(min(bins[shown[-1] + 1], hi)):.1f},{Y(0):.1f}"
        p.append(
            f'<polygon points="{area}" fill="var({s["var"]})" opacity=".10"/>'
            f'<polyline points="{pts}" fill="none" stroke="var({s["var"]})" stroke-width="2" stroke-linejoin="round"/>'
        )
    for i in shown:
        tip = f"{bins[i]:.1f}–{bins[i + 1]:.1f}" + "".join(
            f"\n{s['label']} {s['values'][i] * 100:.1f}%" for s in series
        )
        x0, x1 = X(max(bins[i], lo)), X(min(bins[i + 1], hi))
        p.append(
            f'<rect class="hit" x="{x0:.1f}" y="{mt}" width="{max(x1 - x0, 1):.1f}" height="{ph}" data-tip="{esc(tip)}"/>'
        )
    return "".join(p) + "</svg>"

legend

legend(items: list[tuple[str, str]], dot: bool = False) -> str

Inline key: (label, css var).

Source code in tit/reporting/html/components.py
def legend(items: list[tuple[str, str]], dot: bool = False) -> str:
    """Inline key: ``(label, css var)``."""
    return "".join(
        f'<span class="key"><i class="{"dot" if dot else ""}" style="background:var({v})"></i>{esc(t)}</span>'
        for t, v in items
    )

gate_scale

gate_scale(value: float, lo: float, hi: float, ok: tuple[float, float], st: str = 'pass') -> str

The gate rail: track, accepted range, threshold ticks and the value dot, clamped to the track.

Source code in tit/reporting/html/components.py
def gate_scale(
    value: float,
    lo: float,
    hi: float,
    ok: tuple[float, float],
    st: str = "pass",
) -> str:
    """The gate rail: track, accepted range, threshold ticks and the value dot, clamped to the track."""
    width, pad = 190, 6

    def X(v: float) -> float:
        return pad + (min(max(v, lo), hi) - lo) / (hi - lo) * (width - 2 * pad)

    colour = f"var(--{st}-mark)" if st in ("pass", "warn", "fail") else "var(--ink-2)"
    ticks = "".join(
        f'<line x1="{X(t):.1f}" x2="{X(t):.1f}" y1="6" y2="20" stroke="var(--ink-2)" stroke-width="1.2"/>'
        for t in ok
        if lo < t < hi
    )
    return (
        f'<svg width="{width}" height="26" viewBox="0 0 {width} 26" aria-hidden="true">'
        f'<line x1="{pad}" x2="{width - pad}" y1="13" y2="13" stroke="var(--rule-strong)" stroke-width="2" stroke-linecap="round"/>'
        f'<line x1="{X(ok[0]):.1f}" x2="{X(ok[1]):.1f}" y1="13" y2="13" stroke="var(--band-edge)" stroke-width="6"/>{ticks}'
        f'<circle cx="{X(value):.1f}" cy="13" r="5" fill="{colour}" stroke="var(--paper)" stroke-width="2"/></svg>'
    )

gate_status

gate_status(check_id: str, failures: list[str]) -> str

A blocking check fails if and only if the pipeline listed it in its failures.

Source code in tit/reporting/html/components.py
def gate_status(check_id: str, failures: list[str]) -> str:
    """A blocking check fails if and only if the pipeline listed it in its failures."""
    return "fail" if check_id in failures else "pass"

verdict

verdict(checks: list[Check], consequence: str = '') -> tuple[str, str]

(seal kind, headline) in what the page shows: the quality gate, the failed checks by name, and how many advisories need attention. Software checks are named only when one failed.

Source code in tit/reporting/html/components.py
def verdict(checks: list[Check], consequence: str = "") -> tuple[str, str]:
    """``(seal kind, headline)`` in what the page shows: the quality gate, the failed checks by name,
    and how many advisories need attention. Software checks are named only when one failed.
    """
    failed = [c for c in checks if c.blocking and c.status == "fail"]
    if failed:
        parts = []
        for role, word in (("gate", "quality gate"), ("internal", "software check")):
            names = [c.label for c in failed if c.role == role]
            if names:
                plural = "s" if len(names) > 1 else ""
                parts.append(f"{word}{plural} failed: {', '.join(names)}")
        head = "; ".join(parts)
        tail = f" — {consequence}" if consequence else ""
        return "fail", head[0].upper() + head[1:] + tail
    n = sum(c.role == "advisory" and c.status in ("warn", "fail") for c in checks)
    if not n:
        return "pass", "Quality gate passed"
    return (
        "warn",
        f"Quality gate passed · {n} {'advisory needs' if n == 1 else 'advisories need'} attention",
    )

checks_table

checks_table(checks: list[Check], caption: str, cite: Callable[[list[str]], str]) -> str

Status, name with its role badge, plain-text rule and citations, value, rule and (for gates) the rail.

cite turns a row's DOIs into citation links (the caller owns the reference list).

Source code in tit/reporting/html/components.py
def checks_table(
    checks: list[Check], caption: str, cite: Callable[[list[str]], str]
) -> str:
    """Status, name with its role badge, plain-text rule and citations, value, rule and (for gates) the rail.

    *cite* turns a row's DOIs into citation links (the caller owns the reference list).
    """
    with_rail = any(c.rail for c in checks)
    rows = []
    for c in checks:
        why = (
            inline(c.description)
            + (f" {cite(list(c.cite))}" if c.cite else "")
            + (f" <i>({esc(c.note)})</i>" if c.note else "")
        )
        row = [
            status(c.status, "Reported" if c.role == "report" else None),
            f'<span class="name">{esc(c.label)}</span> <span class="role {c.role}">{ROLE_WORD[c.role]}</span><span class="desc">{why}</span>',
            esc(c.shown),
            esc(c.threshold),
        ]
        if with_rail:
            row.append(
                f'<span class="scale">{gate_scale(c.value, c.rail[0], c.rail[1], c.rail[2:], st=c.status)}</span>'
                if c.rail and c.value is not None
                else ""
            )
        rows.append(row)
    head = ["Status", "Check", "Measured", "Rule"] + (
        ['<span class="sr">Position within the rule</span>'] if with_rail else []
    )
    return table(head, rows, caption=caption, right={2, 3}, cls="gate")