ComponentPorchlight CSS

Card

A raised surface grouping related content - header, body, and optional footer, with a container-query header and an interactive variant.

51 components9 patterns46 stable5 experimental

Command

Search Porchlight

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 add tabindex="0" and a role/keyboard handler - prefer the <a> form.
  • Focus: interactive cards draw a crisp focus ring + elevated shadow.