Code Block
Code to read and copy: file name, line numbers, highlighted lines and a monochrome syntax in weight and light. Copy rolls to Copied; a terminal copies its commands only.
View codeA key figure, its change and the curve behind it: the figure rolls, the curve draws itself and a guide follows your pointer with the value of the day. Trends told by arrows.
With your AI
Say it to your AI. It gets the exact code below, not its best guess, and knows where it goes.
Show my dashboard numbers in Kodza Stat Cards.
claude mcp add -t http kodza https://kodza.dev/api/mcp
<article class="kodza-stat" data-points="1420,1610,1580,1890,2050,1730,2200" data-labels="Mon,Tue,Wed,Thu,Fri,Sat,Sun">
<p class="kodza-stat__label">Copies this week</p>
<p class="kodza-stat__value">12,480</p>
<p class="kodza-stat__delta" data-trend="up">12.4% <span>vs last week</span></p>
<svg class="kodza-stat__chart" role="img" aria-label="Copies per day, from 1,420 on Monday to 2,200 on Sunday"></svg>
</article>
<article class="kodza-stat" data-points="0,0,0,0,0,0,0" data-labels="Mon,Tue,Wed,Thu,Fri,Sat,Sun">
<p class="kodza-stat__label">Dependencies</p>
<p class="kodza-stat__value">0 KB</p>
<p class="kodza-stat__delta" data-trend="flat">0% <span>since forever</span></p>
<svg class="kodza-stat__chart" role="img" aria-label="No dependencies on any day of the week"></svg>
</article>
<article class="kodza-stat" data-points="14,12,11,9,8,5,3" data-labels="Mon,Tue,Wed,Thu,Fri,Sat,Sun">
<p class="kodza-stat__label">Open bugs</p>
<p class="kodza-stat__value">3</p>
<p class="kodza-stat__delta" data-trend="down">40% <span>and falling</span></p>
<svg class="kodza-stat__chart" role="img" aria-label="Open bugs per day, from 14 on Monday to 3 on Sunday"></svg>
</article>
/* Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license */
/* Kodza — Stat Card
A key figure, its change and the curve that led there. When the card comes into view the
figure rolls into place, the curve draws itself and its area rises; point at the curve and
a guide follows you, telling the value of the day. The trend (data-trend: up, down, flat)
is said by an arrow, never by a color. The curve comes from data-points (and data-labels).
Everything is tuned with the --kz-stat-* variables. */
.kodza-stat {
--kz-stat-bg: #ffffff;
--kz-stat-fg: #0a0a0a;
--kz-stat-muted: rgba(10, 10, 10, 0.62);
--kz-stat-border: rgba(10, 10, 10, 0.1);
--kz-stat-soft: rgba(10, 10, 10, 0.06);
--kz-stat-ink: #0a0a0a;
--kz-stat-on-ink: #ffffff;
--kz-stat-ease: cubic-bezier(0.22, 1, 0.36, 1);
position: relative;
display: grid;
box-sizing: border-box;
justify-items: start;
padding: 28px 28px 22px;
border-radius: 16px;
background: var(--kz-stat-bg);
box-shadow: inset 0 0 0 1px var(--kz-stat-border);
color: var(--kz-stat-fg);
}
.kodza-stat__label {
margin: 0;
color: var(--kz-stat-muted);
font-size: 14px;
}
.kodza-stat__value {
display: flex;
margin: 10px 0 0;
font-size: 40px;
font-weight: 600;
font-variant-numeric: tabular-nums;
line-height: 1.1;
letter-spacing: -0.045em;
}
.kodza-stat__digit {
display: inline-block;
height: 1.1em;
overflow: hidden;
}
.kodza-stat__digit > span,
.kodza-stat__digit > span > span {
display: block;
height: 1.1em;
}
.kodza-stat__digit > span {
height: auto;
}
.kodza-stat__char {
white-space: pre;
}
.kodza-stat__sr {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
/* The change: an arrow says which way */
.kodza-stat__delta {
display: inline-flex;
align-items: center;
gap: 6px;
margin: 12px 0 0;
font-size: 13px;
font-weight: 500;
font-variant-numeric: tabular-nums;
}
.kodza-stat__delta::before {
content: "";
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--kz-stat-soft);
}
.kodza-stat__delta > span {
color: var(--kz-stat-muted);
font-weight: 400;
}
/* The arrow itself, drawn with two borders */
.kodza-stat__delta {
position: relative;
}
.kodza-stat__delta::after {
content: "";
position: absolute;
top: 50%;
left: 6px;
width: 5px;
height: 5px;
border-top: 1.5px solid currentColor;
border-right: 1.5px solid currentColor;
transform: translateY(-50%) rotate(-45deg) translate(-1px, 1px);
}
.kodza-stat__delta[data-trend="down"]::after {
transform: translateY(-50%) rotate(135deg) translate(-1px, 1px);
}
.kodza-stat__delta[data-trend="flat"]::after {
transform: translateY(-50%) rotate(45deg) translate(-1px, 1px);
}
/* The curve */
.kodza-stat__chart {
justify-self: stretch;
width: 100%;
height: 64px;
margin-top: 22px;
overflow: visible;
touch-action: pan-y;
}
.kodza-stat__line {
fill: none;
stroke: var(--kz-stat-ink);
stroke-width: 1.75;
stroke-linecap: round;
stroke-linejoin: round;
}
.kodza-stat__area {
stop-color: var(--kz-stat-ink);
}
.kodza-stat__end {
fill: var(--kz-stat-ink);
stroke: var(--kz-stat-bg);
stroke-width: 2;
}
/* The guide that follows the pointer */
.kodza-stat__guide {
stroke: var(--kz-stat-border);
stroke-width: 1;
stroke-dasharray: 2 3;
opacity: 0;
transition: opacity 0.2s ease;
}
.kodza-stat__spot {
fill: var(--kz-stat-bg);
stroke: var(--kz-stat-ink);
stroke-width: 1.75;
opacity: 0;
transition: opacity 0.2s ease;
}
.kodza-stat__tip {
position: absolute;
top: 0;
left: 0;
padding: 5px 9px;
border-radius: 8px;
background: var(--kz-stat-ink);
color: var(--kz-stat-on-ink);
font-size: 12px;
font-weight: 500;
font-variant-numeric: tabular-nums;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s ease;
}
.kodza-stat.is-pointing .kodza-stat__guide,
.kodza-stat.is-pointing .kodza-stat__spot,
.kodza-stat.is-pointing .kodza-stat__tip {
opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
.kodza-stat__guide,
.kodza-stat__spot,
.kodza-stat__tip {
transition-duration: 0.01ms !important;
}
}
/* Theming example — on a dark background:
.dark-section .kodza-stat {
--kz-stat-bg: #161616;
--kz-stat-fg: #f2f2f0;
--kz-stat-muted: rgba(242, 242, 240, 0.62);
--kz-stat-border: rgba(255, 255, 255, 0.12);
--kz-stat-soft: rgba(255, 255, 255, 0.08);
--kz-stat-ink: #f2f2f0;
--kz-stat-on-ink: #0a0a0a;
}
*/
/* kodza-guard a1b85527e1e9 — generated by Kodza, do not edit.
Theme guard: keeps a site's own CSS (WordPress themes, Bootstrap, CSS resets…) off this
component. :not(#_) is always true and weighs as much as an id, so these lines win over
theme rules made of classes and tags. Customise the component through its --kz-* variables. */
/* What the browser gives, on everything the component leaves to it */
.kodza-stat:not(#_) {
border: revert;
outline: revert;
text-decoration: revert;
transition: revert;
list-style: revert;
outline-offset: revert;
appearance: revert;
max-height: revert;
font-family: revert;
font-size: revert;
font-weight: revert;
font-style: revert;
line-height: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-stat__label:not(#_) {
border: revert;
padding: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
transition: revert;
list-style: revert;
box-sizing: revert;
float: revert;
position: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: revert;
max-width: revert;
max-height: revert;
font-family: revert;
font-weight: revert;
font-style: revert;
line-height: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-stat__value:not(#_) {
border: revert;
padding: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
transition: revert;
list-style: revert;
box-sizing: revert;
float: revert;
position: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: revert;
max-width: revert;
max-height: revert;
color: revert;
font-family: revert;
font-style: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-stat__digit:not(#_),
.kodza-stat__char:not(#_),
.kodza-stat__sr:not(#_),
.kodza-stat__area:not(#_),
.kodza-stat__guide:not(#_),
.kodza-stat__line:not(#_),
.kodza-stat__end:not(#_),
.kodza-stat__spot:not(#_),
.kodza-stat__tip:not(#_),
.kodza-stat__digit span:where(:not([class*="kodza-"])):not(#_),
.kodza-stat__delta span:where(:not([class*="kodza-"])):not(#_) {
box-sizing: revert;
}
.kodza-stat__delta:not(#_) {
border: revert;
padding: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
transition: revert;
list-style: revert;
box-sizing: revert;
float: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: revert;
max-width: revert;
max-height: revert;
color: revert;
font-family: revert;
font-style: revert;
line-height: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-stat__chart:not(#_) {
border: revert;
padding: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
transition: revert;
list-style: revert;
box-sizing: revert;
float: revert;
position: revert;
margin-right: revert;
margin-bottom: revert;
margin-left: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: revert;
max-width: revert;
max-height: revert;
color: revert;
font-family: revert;
font-size: revert;
font-weight: revert;
font-style: revert;
line-height: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-stat__label:not(#_):where(:not([hidden])),
.kodza-stat__chart:not(#_):where(:not([hidden])) {
display: revert;
}
.kodza-stat__label:not(#_):where([hidden]),
.kodza-stat__chart:not(#_):where([hidden]) {
display: none;
}
.kodza-stat__delta:not(#_)::before,
.kodza-stat__delta:not(#_)::after {
box-sizing: revert;
}
/* The component's own rules, one step stronger: real buttons and fields, what it sets on tags themes style (links, lists, headings, images…), and the root width (a theme may narrow it, never widen it) */
.kodza-stat:not(#_) {
position: relative;
display: grid;
box-sizing: border-box;
padding: 28px 28px 22px;
border-radius: 16px;
background: var(--kz-stat-bg);
box-shadow: inset 0 0 0 1px var(--kz-stat-border);
color: var(--kz-stat-fg);
}
.kodza-stat__label:not(#_) {
margin: 0;
color: var(--kz-stat-muted);
font-size: 14px;
}
.kodza-stat__value:not(#_) {
display: flex;
margin: 10px 0 0;
font-size: 40px;
font-weight: 600;
line-height: 1.1;
letter-spacing: -0.045em;
}
.kodza-stat__delta:not(#_) {
display: inline-flex;
margin: 12px 0 0;
font-size: 13px;
font-weight: 500;
}
.kodza-stat__delta:not(#_) {
position: relative;
}
.kodza-stat__chart:not(#_) {
margin-top: 22px;
}
// Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license
// Kodza — Stat Card
// Draws the curve from data-points at the chart's real size (and again when it resizes),
// rolls the figure and draws the curve when the card comes into view, and moves a guide with
// the value of the nearest day under the pointer. "kodza:replay" plays it again.
(() => {
const SELECTOR = '.kodza-stat';
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const NS = 'http://www.w3.org/2000/svg';
const make = (tag, attrs = {}) => {
const el = document.createElementNS(NS, tag);
Object.entries(attrs).forEach(([key, value]) => el.setAttribute(key, value));
return el;
};
let uid = 0;
// A smooth curve through the points (Catmull-Rom as cubic Beziers)
const curve = (pts) => pts.reduce((d, [x, y], i) => {
if (!i) return `M${x},${y}`;
const [x0, y0] = pts[i - 2] || pts[i - 1];
const [x1, y1] = pts[i - 1];
const [x3, y3] = pts[i + 1] || [x, y];
return `${d} C${x1 + (x - x0) / 6},${y1 + (y - y0) / 6} ${x - (x3 - x1) / 6},${y - (y3 - y1) / 6} ${x},${y}`;
}, '');
// One instance. Everything it starts is tied to `signal`, so kodza:destroy stops it all
function mount(root, signal) {
const svg = root.querySelector('.kodza-stat__chart');
const value = root.querySelector('.kodza-stat__value');
const data = (root.dataset.points || '').split(',').map(Number).filter((n) => !Number.isNaN(n));
const labels = (root.dataset.labels || '').split(',');
const format = (n) => n.toLocaleString('en-US');
if (!svg || data.length < 2) return;
// The figure: digits in windows, the plain text for screen readers
const strips = [];
if (value) {
const text = value.textContent.trim();
let rank = 0;
value.setAttribute('aria-hidden', 'true');
value.after(Object.assign(document.createElement('span'), { className: 'kodza-stat__sr', textContent: text }));
value.replaceChildren(...[...text].map((char) => {
if (!/\d/.test(char)) return Object.assign(document.createElement('span'), { className: 'kodza-stat__char', textContent: char });
const column = Object.assign(document.createElement('span'), { className: 'kodza-stat__digit' });
const strip = document.createElement('span');
for (let i = 0; i < 20; i++) strip.append(Object.assign(document.createElement('span'), { textContent: i % 10 }));
column.append(strip);
strips.push({ strip, stop: 10 + Number(char), rank: rank++ });
return column;
}));
}
// The chart parts
const id = `kodza-stat-${++uid}`;
const defs = make('defs');
const gradient = make('linearGradient', { id, x1: 0, y1: 0, x2: 0, y2: 1 });
gradient.append(make('stop', { offset: '0', class: 'kodza-stat__area', 'stop-opacity': '0.14' }), make('stop', { offset: '1', class: 'kodza-stat__area', 'stop-opacity': '0' }));
defs.append(gradient);
const area = make('path', { fill: `url(#${id})` });
const line = make('path', { class: 'kodza-stat__line', pathLength: '1' });
const end = make('circle', { class: 'kodza-stat__end', r: '3.5' });
const guide = make('line', { class: 'kodza-stat__guide' });
const spot = make('circle', { class: 'kodza-stat__spot', r: '4' });
svg.append(defs, area, guide, line, end, spot);
const tip = Object.assign(document.createElement('span'), { className: 'kodza-stat__tip' });
tip.setAttribute('aria-hidden', 'true');
root.append(tip);
let points = [];
const draw = () => {
const w = svg.clientWidth;
const h = svg.clientHeight;
if (!w || !h) return;
svg.setAttribute('viewBox', `0 0 ${w} ${h}`);
const min = Math.min(...data);
const max = Math.max(...data);
const pad = 6;
points = data.map((v, i) => [i / (data.length - 1) * w, max === min ? h * 0.6 : h - pad - (v - min) / (max - min) * (h - pad * 2)]);
const d = curve(points);
line.setAttribute('d', d);
area.setAttribute('d', `${d} L${w},${h} L0,${h} Z`);
const [ex, ey] = points[points.length - 1];
end.setAttribute('cx', ex);
end.setAttribute('cy', ey);
guide.setAttribute('y1', 0);
guide.setAttribute('y2', h);
};
draw();
const resize = new ResizeObserver(draw);
resize.observe(svg);
signal.addEventListener('abort', () => resize.disconnect());
const play = () => {
if (reduceMotion) {
strips.forEach(({ strip, stop }) => (strip.style.transform = `translateY(${-stop * 1.1}em)`));
return;
}
const ease = 'cubic-bezier(0.16, 1, 0.3, 1)';
strips.forEach(({ strip, stop, rank }) => strip.animate([{ transform: 'translateY(0)' }, { transform: `translateY(${-stop * 1.1}em)` }], { duration: 1300 + rank * 140, easing: ease, fill: 'forwards' }));
line.animate([{ strokeDasharray: '1', strokeDashoffset: '1' }, { strokeDasharray: '1', strokeDashoffset: '0' }], { duration: 1400, easing: 'cubic-bezier(0.65, 0, 0.35, 1)', fill: 'both' });
area.animate([{ opacity: 0, transform: 'translateY(10px)' }, { opacity: 1, transform: 'none' }], { duration: 900, delay: 500, easing: ease, fill: 'both' });
end.animate([{ transform: 'scale(0)' }, { transform: 'scale(1.4)', offset: 0.6 }, { transform: 'none' }], { duration: 500, delay: 1250, easing: ease, fill: 'both' });
};
end.style.transformBox = 'fill-box';
end.style.transformOrigin = 'center';
if (reduceMotion || !('IntersectionObserver' in window)) play();
else {
strips.forEach(({ strip }) => (strip.style.transform = 'translateY(0)'));
const io = new IntersectionObserver((entries) => {
if (!entries.some((entry) => entry.isIntersecting)) return;
io.disconnect();
strips.forEach(({ strip }) => (strip.style.transform = ''));
play();
}, { threshold: 0.5 });
io.observe(root);
signal.addEventListener('abort', () => io.disconnect());
}
root.addEventListener('kodza:replay', () => {
[line, area, end, ...strips.map(({ strip }) => strip)].forEach((el) => el.getAnimations().forEach((a) => a.cancel()));
play();
}, { signal });
// The guide: the nearest day under the pointer
svg.addEventListener('pointermove', (event) => {
const box = svg.getBoundingClientRect();
const k = box.width / svg.clientWidth || 1;
// An <svg> has no offsetTop: its place in the card, in the card's own pixels
const card = root.getBoundingClientRect();
const left0 = (box.left - card.left) / k;
const top0 = (box.top - card.top) / k;
const x = (event.clientX - box.left) / k;
const i = Math.round(x / svg.clientWidth * (data.length - 1));
const [px, py] = points[Math.max(0, Math.min(points.length - 1, i))];
const index = Math.max(0, Math.min(data.length - 1, i));
guide.setAttribute('x1', px);
guide.setAttribute('x2', px);
spot.setAttribute('cx', px);
spot.setAttribute('cy', py);
tip.textContent = `${labels[index] ? `${labels[index]} · ` : ''}${format(data[index])}`;
const left = left0 + px - tip.offsetWidth / 2;
tip.style.transform = `translate(${Math.max(8, Math.min(root.clientWidth - tip.offsetWidth - 8, left))}px, ${top0 + py - tip.offsetHeight - 12}px)`;
root.classList.add('is-pointing');
}, { signal });
svg.addEventListener('pointerleave', () => root.classList.remove('is-pointing'), { signal });
}
// Kodza lifecycle, the same in every component: `kodza:init` starts the instances inside
// event.detail.root (the whole page by default), `kodza:destroy` stops them and puts their
// markup back. For themes and apps that swap content without a page load.
const instances = new WeakMap();
const find = (scope = document) => [scope, ...scope.querySelectorAll(SELECTOR)].filter((el) => el.matches?.(SELECTOR));
const init = (scope) => find(scope).forEach((root) => {
if (root.classList.contains('is-ready')) return; // already running, maybe from another copy of this script
const controller = new AbortController();
instances.set(root, { controller, html: root.innerHTML, attrs: [...root.attributes].map((a) => [a.name, a.value]) });
mount(root, controller.signal);
root.classList.add('is-ready');
});
const destroy = (scope) => find(scope).forEach((root) => {
const instance = instances.get(root);
if (!instance) return;
instances.delete(root);
instance.controller.abort();
[...root.attributes].forEach((a) => root.removeAttribute(a.name));
instance.attrs.forEach(([name, value]) => root.setAttribute(name, value));
root.innerHTML = instance.html;
});
document.addEventListener('kodza:init', (event) => init(event.detail?.root));
document.addEventListener('kodza:destroy', (event) => destroy(event.detail?.root));
init();
// Loaded in <head>, the script runs before the markup exists: once more when it is parsed
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () => init(), { once: true });
})();
'use client';
// Kodza — Stat Card, for React. From https://kodza.dev/components/stat-card/
// Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license
// Next to this file: stat-card.css and stat-card.js, the component's CSS and JS from Kodza, as they are.
// Yours to edit: the markup is plain JSX below.
// The script changes this markup once React has rendered it (it adds layers, splits text, moves
// nodes), so React must not patch it afterwards. When a prop changes, the key gives React a fresh
// element: kodza:destroy stops the script on the old one, kodza:init starts it on the new one.
import { useRef, useEffect, type ComponentPropsWithoutRef, type ComponentRef, type CSSProperties } from 'react';
import './stat-card.css';
export type StatCardProps = Omit<ComponentPropsWithoutRef<'article'>, "children" | "vars" | "points" | "labels"> & {
/** data-points on the root (default: 1420,1610,1580,1890,2050,1730,2200) */
points?: "1420,1610,1580,1890,2050,1730,2200" | "0,0,0,0,0,0,0" | "14,12,11,9,8,5,3";
/** data-labels on the root (default: Mon,Tue,Wed,Thu,Fri,Sat,Sun) */
labels?: "Mon,Tue,Wed,Thu,Fri,Sat,Sun";
/** Settings, as CSS variables on the root */
vars?: {
/** default: #ffffff */
"--kz-stat-bg"?: string | number;
/** default: #0a0a0a */
"--kz-stat-fg"?: string | number;
/** default: rgba(10, 10, 10, 0.62) */
"--kz-stat-muted"?: string | number;
/** default: rgba(10, 10, 10, 0.1) */
"--kz-stat-border"?: string | number;
/** default: rgba(10, 10, 10, 0.06) */
"--kz-stat-soft"?: string | number;
/** default: #0a0a0a */
"--kz-stat-ink"?: string | number;
/** default: #ffffff */
"--kz-stat-on-ink"?: string | number;
/** default: cubic-bezier(0.22, 1, 0.36, 1) */
"--kz-stat-ease"?: string | number;
};
className?: string;
};
export function StatCard({ points = "1420,1610,1580,1890,2050,1730,2200", labels = "Mon,Tue,Wed,Thu,Fri,Sat,Sun", vars, className, style, ...rest }: StatCardProps) {
const ref = useRef<ComponentRef<'article'>>(null);
const classes = ["kodza-stat", className].filter(Boolean).join(' ');
// Everything the markup is made of: when it changes, React gets a fresh element (see the top)
const key = JSON.stringify([classes, points, labels, vars, style, rest]);
// Starts the script on this element once React has rendered it, stops it when it goes
useEffect(() => {
const root = ref.current;
let live = true;
// @ts-ignore: a plain script, no types to find
import('./stat-card.js').then(() => {
if (live) document.dispatchEvent(new CustomEvent('kodza:init', { detail: { root } }));
});
return () => {
live = false;
document.dispatchEvent(new CustomEvent('kodza:destroy', { detail: { root } }));
};
}, [key]);
return (
<article key={key} {...rest} className={classes} data-points={points} data-labels={labels} style={{ ...vars, ...style } as CSSProperties} ref={ref}>
<p className="kodza-stat__label">Copies this week</p>
<p className="kodza-stat__value">12,480</p>
<p className="kodza-stat__delta" data-trend="up">12.4% <span>vs last week</span></p>
<svg className="kodza-stat__chart" role="img" aria-label="Copies per day, from 1,420 on Monday to 2,200 on Sunday" />
</article>
);
}
Checked
Robots poked at it before you did. Every component goes through the same checks, and again each time it changes.