{% load i18n %} {% comment %} Gauge (VIZ-007 to VIZ-010): a circular progress ring reading one quantity against a fixed min/max. Consumed via the {% bw_gauge %} tag, never a plain {% include %}: min/max validation, value clamping, the percentage, the SVG dash geometry and the threshold-band colour are all computed in Python (the same "geometry in Python, template renders finished numbers" contract {% bw_ranked_list %} uses); this template never builds a dash string or a percentage itself. Required context (all supplied by the tag, never by a caller including this partial directly): label: the accessible name (aria-label), may be empty. size: one of "sm" | "md" | "lg" (VIZ-010), already validated. threshold_token: one of "accent" | "success" | "warning" | "danger" (VIZ-009), already validated and resolved against threshold_bands. circumference, dash_offset, radius: fixed-format numeric strings (f"{x:.2f}"), never a caller-influenced string: safe by construction in the style="..." attribute they render into, since no quote character can appear in a value produced this way. percent_display: the computed percentage as a plain string, e.g. "73". gauge_label: a pre-rendered safe string (VIZ-008), or "" to fall back to percent_display. A TRUSTED markup slot exactly like _stat.html's own sparkline context variable: the caller renders its own markup and marks it safe, brickwork does not sanitise it. gauge_label_has_text: the bw_gauge tag's own decision (COL-030) of whether gauge_label carries any visible text, already computed in Python by _gauge_label_has_visible_text. This template branches on THIS boolean, never on gauge_label's own truthiness: a whitespace-only string or markup with no text content (e.g. " " or mark_safe("")) is truthy in Python but has no visible text, so a truthiness test here would render an empty-looking label with no numeric fallback. attrs_html: pre-rendered consumer data-* attributes for the gauge root (bw_data_attrs, mirroring _ranked_list.html's own root data seam). States: a single populated state; a gauge always has a value once rendered (no loading/empty variant, unlike _ranked_list.html and _chart_card.html: a gauge without a value has nothing to encode, so the tag has no context in which to omit it). Accessibility: role="img" on the SVG root, named by aria-label when label is given (mirrors bw_chart_mount's CHT-012 contract for a static, already- resolved visual summary). VISIBLE text is ALWAYS rendered inside the ring (COL-030: the numeric meaning never rides on arc length or colour alone), independent of whether threshold_bands resolved a colour: either the caller's own gauge_label, when gauge_label_has_text is true, or the computed percentage otherwise, never neither. Both circles (track and arc) are aria-hidden="true" and carry no text of their own (decorative geometry). Deliberately NOT role="progressbar" (VIZ-015): that vocabulary's contract is a live quantity's progress toward completion, not a single already-resolved reading, and COL-030 is already satisfied by the visible text without it, the same reasoning bw_ranked_list documents for its own rows. This guarantee has TWO distinct failure directions, and only one of them is the accessibility guarantee itself: - Number suppressed, arc still coloured: meaning rides on colour alone. This IS the COL-030 violation, and it is what gauge_label_has_text exists to prevent. - Fallback over-fires, a legitimate caller label is discarded in favour of the number: the user still sees a number, so COL-030 holds. This is a correctness bug (a good label thrown away), not an accessibility one, even though the visible symptom (the label text changes) looks similar at a glance. When a defect is found in gauge_label_has_text or the extractor it uses (_gauge_label_has_visible_text, _GaugeLabelVisibleTextExtractor in brickwork_components.py), establish which of these two directions it fails in before assigning severity: the guarantee's name sits on the MECHANISM (which text counts as visible), not on which direction a defect in that mechanism pushes the outcome. Malformed or unusual markup (unclosed tags, void elements written without a closing slash) should degrade toward showing the number, never toward hiding it; the extractor is built so that an unclosed non-visible-text tag (title/desc/script/ style) suppresses the REST of the label, forcing the numeric fallback, which is a real, verified consequence of its depth-tracking design (it never resets on malformed input), not merely an untested coincidence, but it is not independently enforced as its own guarantee either: nothing in the code specifically detects "this markup is malformed" and forces the safe branch. If a future change to that extractor ever made malformed markup degrade the OTHER way (toward hiding a real number), that would be a genuine regression on this guarantee even though no single line of code currently promises the safe direction outright. Responsive: no breakpoint switch; size is the fixed sm/md/lg token scale (VIZ-010), never viewport-driven. The SVG viewBox and dash geometry are fixed at every size: only the rendered diameter (a CSS custom property per size modifier) changes. {% endcomment %}
{% if gauge_label_has_text %}{{ gauge_label }}{% else %}{% blocktranslate with percent=percent_display %}{{ percent }}%{% endblocktranslate %}{% endif %}