Card · stat · media

Cards with media, header actions, footers, folding, selection and loading placeholders — plus stat cards whose numbers count up, deltas that know which way is good, and sparklines that draw themselves.

Stats · numbers count to their new value · sparklines draw in

Revenue
$48,210
12.4%vs last month
Active users
2,931
4.1%this week
Churn
2.4%
0.8%lower is better
Orders
612
3.2%vs yesterday

Media · the whole card is a link · hover lifts it

🏔️
Dolomites loop

6 days · 84 km

Popular
Hut to hut under the pale peaks — via ferratas optional.
$1,240★ 4.9
🌊
Algarve coast

4 days · 52 km

Cliff paths, sea caves and a swim at the end of every day.
$780★ 4.7
🌲
Black Forest

3 days · 45 km

Quiet trails, cuckoo clocks and cake that deserves its name.
$540★ 4.6

A choice · Space or Enter picks it

Header actions · a footer · folding · loading · looks

Team members

3 of 5 seats used

AAda Lovelace Owner
GGrace Hopper Admin
AAlan Turing Member

Loading body

Placeholders while it fetches

Loaded — press “Refresh data” above to see the placeholders again.

Filled

variant: "filled"

For grouping on a busy page, without a border.
🎧

Horizontal

media on the left

Rows of results, a cart line, a notification with a picture.

Usage

JavaScript
import { signal, html } from "./index.js";import { Card, StatCard } from "./components/card.js"; Card({ title: "Team", subtitle: "3 of 5 seats", icon, actions: inviteButton,       children: list, footer: html``, collapsible: true, variant: "outlined" | "elevated" | "filled" | "ghost" })Card({ media: "/cover.jpg", title: "Dolomites", href: "/trips/dolomites", badge })   // one big linkCard({ title: "Team plan", selected: isTeam })                                       // a checkable choiceCard({ title: "Report", loading: busy, children: () => table })                      // placeholders while busy StatCard({ label: "Revenue", value: revenue, format: { style: "currency", currency: "USD" },           delta: 12.4, deltaLabel: "vs last month", spark: last30, loading: busy })StatCard({ label: "Churn", value: churn, suffix: "%", delta: -0.8, invert: true })   // a fall is good