Card
The .pl-c-card is a raised surface for grouping related content - the workhorse
of SaaS dashboards (KPI tiles, list items, settings sections, detail panels).
Elevation comes from a soft shadow, not a heavy bg contrast - surface is
intentionally close to bg; the border + shadow define the card.
Semantic HTML
<section class="pl-c-card">
<header class="pl-c-card__header">
<h2 class="pl-c-card__title">Quarterly usage</h2>
<button class="pl-c-button" data-variant="ghost">Export</button>
</header>
<div class="pl-c-card__body">...</div>
</section>
Interactive (clickable) cards
Add data-interactive or use an <a class="pl-c-card"> for a hover lift +
focus ring - for navigation tiles and selectable list items.
<a class="pl-c-card" href="/accounts/acme">
<header class="pl-c-card__header">
<h2 class="pl-c-card__title">Acme Ops</h2>
</header>
<div class="pl-c-card__body">Enterprise · $2,400/mo</div>
</a>
App surfaces
Add data-surface="app" for tighter operational surfaces inside dense desktop
app shells. It keeps the same border, elevation, spacing, and semantics, but
uses the smaller large-surface radius so dashboard cards do not feel overly
soft.
<section class="pl-c-card" data-surface="app">
<header class="pl-c-card__header">
<h2 class="pl-c-card__title">Queue health</h2>
</header>
<div class="pl-c-card__body">23 jobs waiting</div>
</section>
Class contract
| Selector | Role |
|---|---|
.pl-c-card |
The card (<section> or <a> if interactive). |
.pl-c-card__header |
Title + optional action row. |
.pl-c-card__title |
The <h2> heading. |
.pl-c-card__body |
Main content. |
.pl-c-card__footer |
Optional action row with a top divider. |
[data-interactive] / <a> |
Enables hover lift + focus ring. |
[data-surface="app"] |
Uses a tighter app-shell radius. |
Tokens consumed
--pl-color-{surface,border,text,text-muted},
--pl-shadow-{1,2}, --pl-focus-color, --pl-radius-{lg,xl},
--pl-space-{2,3,4,5}, --pl-text-lg, --pl-leading-tight,
--pl-duration-2, --pl-ease-standard.
Tokens exposed
| Token | Default | Purpose |
|---|---|---|
--pl-c-card-padding |
--pl-space-5 |
Inner padding. |
--pl-c-card-bg |
--pl-color-surface |
Card fill. |
--pl-c-card-border |
--pl-color-border |
Border color. |
--pl-c-card-shadow |
--pl-shadow-1 |
Default elevation. |
--pl-c-card-radius |
--pl-radius-xl |
Corner radius. |
Container query
The card establishes container: pl-c-card / inline-size. The header collapses
from a row to a column when the card is narrower than 28rem - not the
viewport. A card in a narrow split pane stacks even on a wide screen.
Badges and actions keep their natural width when the header stacks.
Spacing
The card owns the inset around its header, body, and footer. First and last
body children do not add outer block margins. When the body also uses
.pl-l-stack, its gap supplies the spacing between children.
When composing a toolbar inside a padded card, remove the toolbar’s horizontal padding in your app styles so its content aligns with the body. Keep toolbar padding when the surrounding card has no padding, as in an edge-to-edge table.
Accessibility
- Semantics: use a real
<section>(or<article>for self-contained content). The title should be a heading (<h2>–<h6>at the right level). - Interactive cards: when the card is an
<a>, it’s natively focusable and keyboard-activatable. When it’s a<div data-interactive>, you must addtabindex="0"and arole/keyboard handler - prefer the<a>form. - Focus: interactive cards draw a crisp focus ring + elevated shadow.