Dashboard KPI card: an uppercase label, one large tabular number, a delta with a direction arrow in good, bad or neutral tone, a note, an optional inline sparkline and a drill link. Surface card, hairline border, no resting shadow. Use one per metric in the row across the top of a dashboard.
size="lg" draws the value at 30px, the step above a page title, for the figures a dashboard opens with. href makes the whole card one link with the row as its label; command makes it the control with no href. suiLink fires on either before the browser follows, so a host that navigates in memory calls preventDefault.
Activate a card. The first two are links; the third has no href and asks the page to navigate.
<div style="display:grid;gap:10px">
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:14px">
<sui-stat-card size="lg" label="Open tasks" value="34" delta="+3" delta-direction="up" note="since Monday" href="#" link-label="View tasks"></sui-stat-card>
<sui-stat-card size="lg" label="Overdue" value="3" delta="+1" delta-direction="up" severity="critical" note="1 of them yours" href="#" link-label="View tasks"></sui-stat-card>
<sui-stat-card id="kpi-command" size="lg" label="Done this week" value="12" delta="+2" delta-direction="up" note="vs. last week" command></sui-stat-card>
</div>
<span id="kpi-log" style="font:460 13px/1.6 var(--font-sans);color:var(--ink-muted)">Activate a card. The first two are links; the third has no href and asks the page to navigate.</span>
</div>
// setup(root): properties and events
setup(root) {
const log = root.querySelector('#kpi-log');
root.addEventListener('suiLink', (e) => {
e.preventDefault();
const card = e.target.closest('sui-stat-card');
log.textContent = e.detail.href ? `${card.label}: the page would follow ${e.detail.href}; prevented here.` : `${card.label}: no href, the page navigates itself.`;
});
}
Good and bad deltas
severity sets the tone. Positive takes the positive ink, green under semantic status. Critical takes the danger ink, so rising receivables read as a warning. Neutral takes the muted ink at regular weight and keeps its arrow. Critical also rings its arrow and adds a hidden "unfavorable", so color is never the only cue.
Each sparkline normalizes to its own series unless scale-min and scale-max are set. Set the same pair on every card in a row, or the shapes read as comparable when they are not.
sui-sparkline is the trend part of the card, usable beside any figure; accent marks the one highlighted trend on a view. It is aria-hidden and draws no axes or tooltip, so the sentence next to it has to say what the trend is.
Cash on hand rose in five of the last seven periods.
Receivables climbed 45 percent over the same periods.
The same receivables series on a fixed 0 to 100 scale, which is how it compares with the row above.
<div style="display:grid;gap:12px;font:460 13px/1.6 var(--font-sans);color:var(--ink)">
<div style="display:flex;align-items:center;gap:12px">
<sui-sparkline id="sp-plain"></sui-sparkline>
<span>Cash on hand rose in five of the last seven periods.</span>
</div>
<div style="display:flex;align-items:center;gap:12px">
<sui-sparkline id="sp-accent" accent width="140" height="34"></sui-sparkline>
<span>Receivables climbed 45 percent over the same periods.</span>
</div>
<div style="display:flex;align-items:center;gap:12px">
<sui-sparkline id="sp-fixed" min="0" max="100"></sui-sparkline>
<span style="color:var(--ink-muted)">The same receivables series on a fixed 0 to 100 scale, which is how it compares with the row above.</span>
</div>
</div>
data is a property, and assigning a new array redraws the line; pushing into the old one does not. With fewer than two points the element draws nothing, so a feed that has just started shows no stray dot. min and max hold the scale steady as points arrive.
// setup(root): properties and events
setup(root) {
const spark = root.querySelector('#spk-live');
const count = root.querySelector('#spk-count');
const readings = [42, 48, 45, 57, 63, 60, 71, 78, 74, 86, 91, 88];
let n = 0;
const draw = () => {
spark.data = readings.slice(0, n);
count.textContent = n === 0 ? 'No readings yet.' : n === 1 ? 'One reading: nothing to draw yet.' : `${n} readings, the latest ${readings[n - 1]}.`;
};
root.querySelector('#spk-add').addEventListener('click', () => {
if (n < readings.length) { n += 1; draw(); }
});
root.querySelector('#spk-reset').addEventListener('click', () => { n = 0; draw(); });
}
Keyboard
Key
Action
Tab sui-stat-card
Lands on a card with an href or command. A plain card is not a tab stop.
EnterSpace sui-stat-card
Emits suiLink, then follows the href unless a listener called preventDefault(). Space works on a linked card too, which a plain link does not do.
API
<sui-stat-card>
Properties
Property
Attribute
Type
Default
Description
accent
accent
boolean
false
Accent sparkline: the one highlighted trend on the view. Reflected.
command
command
boolean
false
Makes the whole card the control without an href: a button announced as a link, for a host that navigates itself on suiLink. Draws no link row. With href set the card is already the control and this changes nothing. Reflected.
data
property only
number[]number[] | undefined
Series for the inline sparkline, oldest first. Property, not attribute.
delta
delta
stringstring | undefined
Change text ("+12.4%", "2 open").
deltaDirection
delta-direction
"down" | "up" | undefined
Adds an up or down arrow before the delta. Reflected.
href
href
stringstring | undefined
Drill-down href, the word every item shape in the library uses. The whole card becomes one link, with the link row as its visible label, so the card is one tab stop and a middle click opens it fresh.
label
label
string
''
Metric name, rendered as an uppercase micro-label ("Receivables").
linkLabel
link-label
stringstring | undefined
Link text; an arrow is appended.
note
note
stringstring | undefined
Faint context after the delta ("vs. July").
scaleMax
scale-max
numbernumber | undefined
Sparkline scale ceiling. See scaleMin.
scaleMin
scale-min
numbernumber | undefined
Sparkline scale floor. Set the same pair on every card in a row so the trends compare; omitted, each card's sparkline normalizes to its own series and the shapes mislead.
How the change reads, on the status ladder sui-trend shares: positive, good news, in the positive ink (the ink under brand status, green under semantic); critical, bad news (a rising receivable), in the danger ink with the direction arrow ringed and a visually hidden "unfavorable" after the figure; neutral, neither, in the muted ink at regular weight, the arrow kept and unringed. So the judgement never rests on colour alone. The danger ink is the brand's own under brand mode and rose under semantic status mode, the same ink an overdue date wears. An unknown value reads as positive. Reflected.
size
size
"lg" | "md"
'md'
Value size: md draws the figure at 23px, lg at 30px for the aggregate figures a dashboard opens with, the step above a page title. The label, delta and note keep their sizes.
value
value
string
''
Formatted figure ("$90,740.45"): two-decimal currency with separators, parentheses for report negatives. Format before passing; the card does no math.
Events
Event
Detail
React
Vue
Description
suiLink
SuiStatCardLinkDetail
onSuiLink
@sui-link
Fires when the card is activated by click, Enter or Space, before the browser follows href; detail is { href }. Cancelable: preventDefault() keeps the page where it is, for a host with in-memory navigation.
Methods
Method
Signature
Description
setFocus()
setFocus() => Promise<void>
Moves focus to the card when it is a control, a link or a command card; does nothing otherwise.
Types
SuiStatCardLinkDetailinterfaceDetail of suiLink: the card's href, or undefined on a command card that has none.
href?
string
SuiTrendSeveritytypeHow a change reads, on the library's status ladder: positive is good news, critical bad news, neutral neither.
No members.
<sui-sparkline>
Properties
Property
Attribute
Type
Default
Description
accent
accent
boolean
false
Orange line for the one highlighted trend on the view; otherwise the data-viz ramp's --viz-2.
Set --sui-sparkline-line on the element or any ancestor to paint the line and its end point
in a color of your own; it wins over accent, as a series color does on the charts. Any CSS
color or var(--token) works; use a token so the color follows the theme. Reflected.
data
property only
number[]
[]
Series values, oldest first. Property, not attribute.
height
height
number
28
SVG height in px.
max
max
numbernumber | undefined
Scale ceiling. Omitted, the series' own maximum is used. See min.
min
min
numbernumber | undefined
Scale floor. Omitted, the series' own minimum is used, so each sparkline normalizes to
itself; set the same min and max on every sparkline in a row so they compare.
width
width
number
96
SVG width in px.
Theming
Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.