Components / Layout

Card

A composable surface: media, header, body, footer and actions; can be a link (with valid inner actions) or a button.

@ruckc/truss-ui Layout

Import

import { Card } from '@ruckc/truss-ui';

Playground

padding
elevation
kind

Recipe: a link card with actions

With href the whole card is clickable, yet a card often needs its own buttons or links. A link or button cannot nest inside another link, so the card is a div and the href link holds the media, header and body. Its ::after stretches over the card, and anything in footer or actions sits above that layer: it stays focusable, clickable and valid HTML. Link attributes (target, rel, aria-label) go to the link; class, id and the rest go to the card.

<Card href="/projects/truss">
  {#snippet media()}<img src="/truss.png" alt="" />{/snippet}
  {#snippet header()}Truss UI{/snippet}
  A design system for dense interfaces.
  {#snippet actions()}
    <Button size="sm" variant="tonal" href="https://example.com/source">Source</Button>
    <Button size="sm" variant="minimal" onclick={share}>Share</Button>
  {/snippet}
</Card>

Recipe: media-only card

With padding="none", a media snippet and no children, the card is just the picture: it has no body element and no padding. Media img, video, canvas and svg render as full-width block elements, and the card's rounded corners clip them.

<Card padding="none" elevation={0}>
  {#snippet media()}<img src="/project.png" alt="Project screenshot" />{/snippet}
</Card>

Recipe: body with its own spacing

padding="none" with children also removes the body padding, so content placed under the media sets its own spacing, as in this example.

Body content that sets its own spacing.
<Card padding="none">
  {#snippet media()}<img src="/project.png" alt="" />{/snippet}
  <div class="intro">Body content that sets its own spacing.</div>
</Card>

Recipe: equal-height card grids

The card is a flex column, so the body grows and footer / actions pin to the bottom edge. Put cards in a Grid and they line up.

Short
One line.
Longer
Several lines of text that make this card taller than its neighbours in the same row, so the others stretch to match.
<Grid min="14rem">
  {#each projects as p}
    <Card href={p.url}>…</Card>   <!-- footers line up, whatever the body length -->
  {/each}
</Grid>

API

Accepts an optional `href` so a link can be switched on conditionally.

Also accepts every attribute of HTMLAnchorAttributes</code>, <code>HTMLButtonAttributes, applied to the underlying element.

PropTypeDefaultDescription
padding 'none' | 'sm' | 'md' | 'none' | 'sm' | 'md' | 'none' | 'sm' | 'md''md'Padding of the body only (the `children` between header and footer): `md` (default) is 16px, `sm` is 8px 12px, `none` removes it so the content can supply its own spacing or a media-only card has none. The header, footer and actions keep their own padding.
elevation 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 31Shadow depth, 0–3.
media snippet Snippet—Edge-to-edge content above the header and body (an image, video or map); clipped by the card's rounded corners.
header snippet Snippet—Header content, as a Svelte snippet.
footer snippet Snippet—Footer content, as a Svelte snippet.
actions snippet Snippet—A row of controls pinned to the bottom edge. Safe in a link card: they stay clickable and are not nested in the link.
children snippet Snippet—Content, as a Svelte snippet.
href string | undefined—Makes the whole card a link (stretched-link pattern: the card is a `div`, the link covers it, and inner actions stay valid). Link attributes (`target`, `rel`, `aria-label`...) go to the link, everything else to the card.
onclick undefined | (e: MouseEvent) => void—Called when the control is clicked.
/docs/card Truss UI · MIT