Data display
Card
An object one could have dragged. There are as many cards as came from the data — unlike a panel, which is a place on the screen.
Markup
<div class="inst-card">
<div class="inst-card-title">worldgen-01</div>
<div class="inst-card-sub">terrain/heightmap.ts · 4 files</div>
</div>
A card is a column with a gap between its children. The header does not press against the content, and the heading does not read as part of the first row of data.
Contract
| What | Required | Why |
|---|---|---|
inst-card-title |
no | The heading of an object. A card without one is just a frame |
An <a> or a <button> for a clickable one |
yes | The modifier gives the look, the tag gives the role. A div with a handler is unreachable from the keyboard |
A real heading (<h3>) in a list of cards |
no, but usually yes | inst-card-title styles but does not declare: navigation by headings does not work without one |
Accessibility
| Clickability | Through an <a> or a <button> only. The modifier gives the look, the tag gives the role |
| One target rather than two | If the whole card is clickable, there must be no second link inside it: the keyboard gets two stops for one object |
| The heading | inst-card-title is visual. For a list of cards a real heading (<h3>) is in order, so that navigation by headings works |
| Focus | A ring with a 1px gap along the outer edge. The radius is inherited from the card |
| No shadow | Depth is carried by the surface. A shadow in the library means "temporary and above" and belongs to the popover and the modal alone |
Variants
Rank
A card takes data-rank like any region — lead · default · support — and
the contract is in
design principles.
The case worth showing is the one a card is usually in: inside something else. A card carrying no rank of its own is an ordinary card however loud the region holding it, and a card nested in a card recedes a second time, because depth decides that and rank does not.
Markup
<div class="inst-panel" data-rank="lead">
<div class="inst-panel-header"><span class="inst-panel-title">Current run</span></div>
<div class="inst-panel-body inst-stack inst-stack--tight">
<div class="inst-card">
<div class="inst-card-title">Ordinary, inside a lead panel</div>
<div class="inst-card"><div class="inst-card-title">And one step deeper</div></div>
</div>
<div class="inst-card" data-rank="support">
<div class="inst-card-title">Support: a rung down, no edge</div>
</div>
<div class="inst-card" data-tone="error">
<div class="inst-card-title">And one in error, nested</div>
<div class="inst-card-sub">The tone takes the ground; the edge stays ordinary</div>
</div>
</div>
</div>
A nested card carrying a tone takes it on its ground instead of the recess film. It still recedes from its container in every theme — a light theme's tone fill sits under the raised surface, a dark theme's is a translucent lift over a dark one, and the recess travels the same way in each — so containment is not lost to meaning.
Interactive
Markup
<a class="inst-card inst-card--interactive" href="#">
<div class="inst-card-title">Open the run</div>
<div class="inst-card-sub">The whole card is the tap target</div>
</a>
The modifier adds a response to hover and focus but does not make the element
clickable: that has to be an <a> or a <button>. A card that is a div
with a click handler is unreachable from the keyboard and has no role.
API
| Name | What it does |
|---|---|
| class | |
inst-card | The base one |
inst-card-title | The heading of an object |
inst-card-sub | The caption under the heading |
| modifier | |
inst-card--interactive | A response to hover and focus |
inst-card--plain | No edge: the boundary is carried by the step of surface alone. For large regions |
| Name | Value | Where from |
|---|---|---|
| token | ||
--surface-raised | light-dark(var(--n-0), var(--n-12)) | base +1: theme |
--border | light-dark(oklch(0 0 0 / 0.12), oklch(1 0 0 / 0.11)) | one across every cell |
--hairline | 1px | base +1: screen |
--radius-lg | 12px | base +4: scale |
--pad-card | var(--space-6) | base +14: density · scale |
Why the kit works this way — the constitution · Open source under MIT, github.com/keshon/instrument