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
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.
<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.
<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.
| Prop | Type | Default | Description |
|---|---|---|---|
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 | 3 | 1 | Shadow 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. |