Skip to content

KPICard

KPICard groups multiple related metrics inside one compact card.

Quick Start

from faststrap import KPICard

KPICard(
    "Campaign Health",
    metrics=[
        ("Leads", "1,240", "+18%", "up"),
        ("CAC", "$42", "-6%", "up"),
        ("Churn", "2.1%", "+0.4%", "down"),
    ],
    columns=3,
)

Metric Tuple Format

Each metric must include at least (label, value).

("Revenue", "$82k")
("Revenue", "$82k", "+12%", "up")

The optional fourth value must be one of up, down, or neutral. Unknown values are treated as neutral.

Parameters

Parameter Type Default Description
title str required Card label.
metrics Sequence[Sequence[Any]] required Metric tuples.
columns int 2 Number of metric columns. Must be at least 1.
variant Bootstrap variant UNSET Card background variant.
inverse bool False Use inverted text colors.
**kwargs Any Extra HTML attributes.

API Reference

faststrap.components.display.stat_card.KPICard(title, metrics, columns=2, variant=UNSET, inverse=False, **kwargs)

Card that displays multiple KPIs in a compact grid.

Source code in src/faststrap/components/display/stat_card.py
@register(category="display")
@beta
def KPICard(
    title: str,
    metrics: Sequence[Sequence[Any]],
    columns: int = 2,
    variant: VariantType | None = UNSET,
    inverse: bool = False,
    **kwargs: Any,
) -> Div:
    """Card that displays multiple KPIs in a compact grid."""
    user_cls = kwargs.pop("cls", "")
    kwargs["cls"] = merge_classes("faststrap-kpi-card", user_cls)

    cfg = resolve_defaults(
        "KPICard",
        columns=columns,
        variant=variant,
        inverse=inverse,
    )
    c_columns = cfg.get("columns", columns)
    c_variant = cfg.get("variant", variant)
    c_inverse = cfg.get("inverse", inverse)

    if c_columns < 1:
        msg = f"columns must be >= 1, got {c_columns}"
        raise ValueError(msg)

    title_cls = "text-muted small text-uppercase fw-semibold"
    if c_inverse:
        title_cls = "text-white-50 small text-uppercase fw-semibold"

    title_el = P(title, cls=title_cls)

    metric_cells: list[Any] = []
    col_class = f"col-{12 // min(c_columns, 12)}"
    for metric in metrics:
        if len(metric) < 2:
            msg = "Each metric must include at least (label, value)."
            raise ValueError(msg)
        label = metric[0]
        value = metric[1]
        delta = metric[2] if len(metric) > 2 else None
        delta_type_raw = metric[3] if len(metric) > 3 else "neutral"
        if delta_type_raw not in {"up", "down", "neutral"}:
            delta_type_raw = "neutral"
        delta_type = cast(Literal["up", "down", "neutral"], delta_type_raw)
        delta_el = _trend_badge(delta, delta_type=delta_type)

        metric_cells.append(
            Div(
                P(label, cls="mb-1 text-muted small"),
                H3(value, cls="mb-0 fw-bold"),
                delta_el,
                cls=col_class,
            )
        )

    body_content = Div(
        title_el,
        Div(*metric_cells, cls="row g-3"),
    )

    return Card(body_content, variant=c_variant, inverse=c_inverse, **kwargs)