{% load brickwork_components brickwork_icons i18n %} {% comment %} Stat (VIZ-001/002/017): the KPI tile. Structural, consumed via {% include %}. Stacks an overline label, a tabular-numeral value (font-variant-numeric: tabular-nums via bw-stat__value, so a column of tiles never jitters as digits change width; AC-BW-075) and an optional trend caption, the latter delegated to _trend_indicator.html (VIZ-017; icvoss/django-brickwork#334) rather than rendered inline: one implementation of the trend contract, not two. Trend accessibility (BR-BW-TPL-007, AC-BW-073/074): whenever ``trend`` is set the included partial ALWAYS renders a directional glyph (arrow-up / arrow-down / minus, decorative) PLUS a visually hidden text fallback ("increased" / "decreased" / "unchanged"), so the direction never rides on colour alone by construction, even when the caller supplies no ``trend_label``. ``trend_label``, when given, refines the accessible text (e.g. "12% up on last month") and renders visibly beside the glyph. Trend colour (icvoss/django-brickwork#388): the direction modifier (bw-trend--up/--down/--flat) is neutral ink. ``trend`` names the way the number moved, never whether that is good news, so it carries no colour of its own; a falling cost is trend="down" whether the fall is good or bad news, and colouring --down danger-red by default painted a judgement onto every caller regardless of which. Judgement colour is opt-in via ``sentiment``, passed straight through to _trend_indicator.html (see that partial's own header comment for the full contract). Class name (#334): the trend row now renders via _trend_indicator.html rather than an inline copy of the same markup, so there is one implementation of the trend contract, not two. The element carries BOTH ``bw-trend`` (_trend_indicator.html's own root class) and ``bw-stat__trend`` (this tile's original class, passed through via _trend_indicator.html's ``css_class`` seam): ``bw-stat__trend`` was documented public surface before this extraction (docs/DESIGN.md and docs/BRANDING.md name it in prose about the X-fg token contrast pairing, and frontend/src/components.css styled it directly), so it is retained alongside the new name rather than retired. Two class names on the one element is a naming question; this extraction is only ever one implementation underneath, never two. Required context: label: the metric's name (rendered in the overline voice). value: the metric's value, pre-formatted by the caller (tabular numerals). Optional: trend ("up" | "down" | "flat"): the direction of change (VIZ-002). Any other value takes the flat treatment. trend_label (str): the visible delta text beside the glyph. sentiment ("good" | "bad", #388): opt-in judgement colour on the trend row, independent of trend, passed straight through to _trend_indicator.html. Omitted (the default) renders the direction modifier only, in neutral ink. icon (registry name): a leading flat glyph for the tile (VIZ-018). href: wraps the tile in an anchor (VIZ-024) which takes the interactive card's shadow raise. Omitted renders a plain non-interactive
, never a clickable-looking tile with no destination. loading (bool): renders skeleton rows instead of the content (STA-004). size ("sm" | "md" | "lg", default "md"): emitted as bw-stat-- only when supplied (VIZ-027). data (mapping of data-* name to value): consumer-owned test and lightweight JavaScript hooks on the tile root. Names are limited to data attributes (not Brickwork's own data-bw-* hooks), and values are escaped. sparkline (safe string, #60): pre-rendered sparkline markup (SVG/canvas), wrapped in a bw-stat__sparkline row below the value/trend. _stat.html is {% include %}d, never {% extends %}d (unlike _card.html and _modal.html, which expose consumer regions as named blocks): a plain {% include %} cannot fill a block defined in the included template, so a block here would be permanently empty. This follows _data_table.html's existing convention instead (row.cells are caller-supplied pre-rendered strings): the caller renders its own sparkline (an inline or a a script mounts into) and passes the markup already marked safe, e.g. {% include "brickwork/components/_stat.html" with label="Revenue" value="1,234" sparkline=my_rendered_svg|safe %}. The caller owns escaping: brickwork does not sanitise this string (it is trusted markup the same way row.cells and card block-filler content are), so never pass user-supplied input here unescaped. Omitted (the default) renders nothing, matching the unfilled-block convention elsewhere. Ignored while loading=True (the skeleton stands in for the whole tile's content, sparkline included). States: loading (skeleton rows stand in for the whole tile), and, when href is set, the interactive-card hover/focus-within raise (the same shadow-only treatment _card.html documents, never a transform); a trend reading carries up/down/flat sub-states. Accessibility: a trend is never colour-only: a directional glyph (decorative) always pairs with visually-hidden text naming the direction in words ("increased"/"decreased"/"unchanged"), regardless of whether the caller supplies a visible trend_label. Judgement colour, when the caller states one via sentiment, reinforces what trend_label's words already say; it is never the sole signal of it. A linked tile (href) renders as a real anchor. Covered by axe.spec.mjs against dashboard-*.html (app/dashboard.html's own KPI row, its native usage) and sections-*.html (stats/inline-band.html, via _stat_band.html); the standalone loading and href states are exercised directly by tests/test_stat.py at the Python/string level, not a dedicated browser fixture. Responsive: no breakpoint switch; no width-dependent CSS on any .bw-stat* selector. Size is the fixed sm/md/lg token scale, never viewport-driven; the surrounding grid (a dashboard's bw-stat-grid or this component's own bw-stat-band) owns whatever column-count responsiveness a page shows. {% endcomment %} {% if href %}{% else %}
{% endif %} {% if loading %} {% else %} {% if icon %}{% bw_icon icon size="md" decorative=True css_class="bw-stat__icon" %}{% endif %} {{ label }} {{ value }} {% include "brickwork/components/_trend_indicator.html" with trend=trend trend_label=trend_label sentiment=sentiment css_class="bw-stat__trend" %} {% if sparkline %}
{{ sparkline }}
{% endif %} {% endif %} {% if href %}{% else %}
{% endif %}