StatGroup
Several numbers that matter in one bordered row, each with its change coloured by whether that is good.
Demo
Revenue
$48,210
up+12.5%vs last month
Orders
1,284
down-3.2%vs last month
Refunds
$1,020
up+8%lower is better
Open tickets
37
unchanged0same as yesterday
Visitors
92.4k
Bounce rate
41%
down-2 pts
Avg. session
3m 12s
Get it
It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.
$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/stat_group.qwemain.qwe
import ui/theme.qwe use theme_cssuse ui/stat_group.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>Example
The code of the preview above.
demos/stat_group.qwe
view StatGroupDemo() = <div.demo-stack> <StatGroup id="stat-group-demo-sales" label="This month" items={[ { label: "Revenue", value: "$48,210", delta: 12.5, delta_suffix: "%", hint: "vs last month" }, { label: "Orders", value: "1,284", delta: -3.2, delta_suffix: "%", hint: "vs last month" }, { label: "Refunds", value: "$1,020", delta: 8, delta_suffix: "%", good: "down", hint: "lower is better" }, { label: "Open tickets", value: "37", delta: 0, hint: "same as yesterday" }, ]}/> <StatGroup id="stat-group-demo-site" label="Site" columns={3} items={[ { label: "Visitors", value: "92.4k" }, { label: "Bounce rate", value: "41%", delta: -2, delta_suffix: " pts", good: "down" }, { label: "Avg. session", value: "3m 12s" }, ]}/> </div>Source
ui/stat_group.qwe
// StatGroup: several numbers that matter in one bordered row, divided by lines:// the summary strip at the top of a dashboard or a report.//// <StatGroup items={[// { label: "Revenue", value: "$48,210", delta: 12.5, delta_suffix: "%", hint: "vs last month" },// { label: "Orders", value: "1,284", delta: -3.2, delta_suffix: "%" },// { label: "Refunds", value: "$1,020", delta: 8, delta_suffix: "%", good: "down" },// { label: "Open tickets", value: "37" },// ]}/>//// items: maps of { label, value, delta?, delta_suffix?, good?, hint? }: value is// the number as it should read (format it first); delta is the change, a number,// shown with ↑ in green above 0, ↓ in red below, → grey at 0; good: "down" when// a fall is good (costs, churn), which swaps the colours; delta_suffix follows// the change ("%", " pts"); hint is a line under it ("vs last week").// columns: how many across (as many as fit, at least 160px each, by default);// below 560px they go two across, then one. label: the group's name for screen// readers. id: the element's id.import ui/theme.qwe use ui_clslet stat_group_css = css`.ui-statgroup { display: grid; grid-template-columns: repeat(var(--ui-statgroup-n, auto-fit), minmax(160px, 1fr)); gap: 1px; overflow: hidden; border: 1px solid var(--ui-border); border-radius: var(--ui-radius); background: var(--ui-border); font: 14px/1.4 var(--ui-font); color: var(--ui-fg); container-type: inline-size; }.ui-statgroup-item { display: grid; gap: 4px; align-content: start; min-width: 0; padding: 16px 18px; background: var(--ui-surface); --t: var(--ui-muted); }.ui-statgroup-label { margin: 0; color: var(--ui-muted); font-size: 13px; font-weight: 500; }.ui-statgroup-value { margin: 0; font-size: 26px; font-weight: 650; line-height: 1.15; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }.ui-statgroup-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font-size: 13px; color: var(--ui-muted); }.ui-statgroup-delta { display: inline-flex; align-items: center; gap: 2px; padding: 1px 7px 1px 5px; border-radius: 999px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--t); background: color-mix(in srgb, var(--t) 13%, transparent); }.ui-statgroup-good { --t: var(--ui-success); }.ui-statgroup-bad { --t: var(--ui-danger); }@container (max-width: 560px) { .ui-statgroup { grid-template-columns: repeat(2, minmax(0, 1fr)); } }@container (max-width: 300px) { .ui-statgroup { grid-template-columns: minmax(0, 1fr); } }`view StatGroup(items: list, columns: int? = null, label: str? = null, id: str? = null, class: str? = null) { style global stat_group_css <div class={ui_cls(["ui-statgroup", class])} id={id} role="group" aria-label={label} style={columns != null ? `--ui-statgroup-n: ${columns}` : null}> {for it in items => { let delta = it.delta let mood = delta == null ? null : delta == 0 ? "flat" : ((delta > 0) == (it.good != "down") ? "good" : "bad") <div class={ui_cls(["ui-statgroup-item", mood != null && `ui-statgroup-${mood}`])}> <p.ui-statgroup-label>{it.label}</p> <p.ui-statgroup-value>{it.value}</p> {(delta != null || it.hint != null) && <div.ui-statgroup-foot> {delta != null && <span.ui-statgroup-delta> <span aria-hidden="true">{delta > 0 ? "↑" : (delta < 0 ? "↓" : "→")}</span> <span.ui-sr>{delta > 0 ? "up" : (delta < 0 ? "down" : "unchanged")}</span> {delta > 0 ? "+" : ""}{delta}{it.delta_suffix ?? ""} </span>} {it.hint != null && <span>{it.hint}</span>} </div>} </div> }} </div>}Related
KpiCardA number with its label, the change since last period coloured by whether that is good, and a small trend as a line or bars.7 views · 7 downloadsMetricTrendA value with an up or down arrow and its change, coloured by whether the move is good; it can work the percentage out.7 views · 6 downloadsProgressListItems each with a label, a bar and its value or percentage, marked in red when one goes over its limit.6 views · 4 downloadsScorecardA score out of a maximum as a filled ring, with a letter grade, a verdict and the parts it is made of.8 views · 5 downloadsStatA KPI tile: a label, a value, how it changed (coloured by whether that is good), and a tiny trend line.6 views · 5 downloadsActivityFeedA stream of what people did, with their pictures, an icon for each kind of action, when, quotes and day headings.5 views · 7 downloads
Thread
Thread 0 posts
On StatGroup: questions, tips, corrections, and code that uses it.
No posts yet. The first one opens the thread.