Card
Structured surface with explicit subparts: header bar, media slot, content body, footer action strip. Use Card when the surface has internal regions; reach for Box when it's an atomic single-content surface.
When to use
- Reach for Card when the surface has internal structure — a heading bar,
an actions strip, a media slot, distinct head / body / foot regions. The
.card__header,.card__image,.card__content,.card__footersubparts give you the structural vocabulary. - Reach for Box for atomic single-content surfaces — callouts, summary tiles, list-item containers. Box keeps its drop-shadow as the elevation signature.
- For full-bleed page bands, prefer Section.
Live
Default + combinations (.is-primary / .is-warning)
Default
Surface uses --kc-card-bg and --kc-card-fg.
Primary
Combination tokens: --kc-primary-bg + --kc-primary-fg.
Warning
Pair with role="status" for a soft alert; not a substitute for a real aria-live region.
<div class="grid is-cols-3">
<article class="card cell">
<h3 class="title">Default</h3>
<p>Surface uses <code>--kc-card-bg</code> and <code>--kc-card-fg</code>.</p>
</article>
<article class="card is-primary cell">
<h3 class="title">Primary</h3>
<p>Combination tokens: <code>--kc-primary-bg</code> + <code>--kc-primary-fg</code>.</p>
</article>
<article class="card is-warning cell">
<h3 class="title">Warning</h3>
<p>Pair with <code>role="status"</code> for a soft alert; not a substitute for a real <code>aria-live</code> region.</p>
</article>
</div> @use '@adnap/krysalicss' with (
$feature-list: ('base-reset', 'base-global', 'module-card')
); Structured card (header / content / footer)
Pricing
Per project, per month
Three concurrent environments, unlimited deploys, 24h support window.
<article class="card">
<header class="card__header">
<h3 class="card__header-title">Pricing</h3>
<button class="card__header-icon" aria-label="More options" type="button">⋯</button>
</header>
<div class="card__content">
<p class="subtitle">Per project, per month</p>
<p>Three concurrent environments, unlimited deploys, 24h support window.</p>
</div>
<footer class="card__footer">
<a class="card__footer-item" href="#choose">Choose plan</a>
<a class="card__footer-item" href="#compare">Compare</a>
</footer>
</article> @use '@adnap/krysalicss' with (
$feature-list: ('base-reset', 'base-global', 'module-card')
); Card with image (.card__image + element/image)
Featured
Media slot above a content body. Pair .card__image with element/image's aspect-ratio classes (.image.is-16by9, .image.is-4by3, …) so the figure ratio is part of the design system, not an inline magic number.
<article class="card">
<div class="card__image">
<figure class="image is-16by9" style="background: color-mix(in srgb, currentColor 8%, transparent);">
<img src="data:image/svg+xml;utf8,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 16 9%27%3E%3Crect width=%2716%27 height=%279%27 fill=%27%23cbd5e1%27/%3E%3C/svg%3E" alt="" />
</figure>
</div>
<div class="card__content">
<h3 class="title">Featured</h3>
<p>Media slot above a content body. Pair <code>.card__image</code> with <a href="/reference/components/element/image">element/image</a>'s aspect-ratio classes (<code>.image.is-16by9</code>, <code>.image.is-4by3</code>, …) so the figure ratio is part of the design system, not an inline magic number.</p>
</div>
<footer class="card__footer">
<a class="card__footer-item" href="#read">Read more</a>
</footer>
</article> @use '@adnap/krysalicss' with (
$feature-list: ('base-reset', 'base-global', 'module-card')
); Light variant on combinations (.is-<combo>.is-light)
Primary light
Tonal surface: pale wash of the combo bg against the theme bg.
Warning light
Lower-intensity surface for soft-warning callouts.
<div class="grid is-cols-2">
<article class="card is-primary is-light cell">
<h3 class="title">Primary light</h3>
<p>Tonal surface: pale wash of the combo bg against the theme bg.</p>
</article>
<article class="card is-warning is-light cell">
<h3 class="title">Warning light</h3>
<p>Lower-intensity surface for soft-warning callouts.</p>
</article>
</div> @use '@adnap/krysalicss' with (
$feature-list: ('base-reset', 'base-global', 'module-card')
); Markup
<article class="card">
<header class="card__header">
<h3 class="card__header-title">Card title</h3>
<button class="card__header-icon" aria-label="More actions">…</button>
</header>
<figure class="card__image">
<img src="/hero.jpg" alt="" width="800" height="450" />
</figure>
<div class="card__content">
<p>Body copy lives inside <code>.card__content</code>. The outer card
collapses its padding to zero when any subpart is present.</p>
</div>
<footer class="card__footer">
<a class="card__footer-item" href="#save">Save</a>
<a class="card__footer-item" href="#edit">Edit</a>
<a class="card__footer-item" href="#delete">Delete</a>
</footer>
</article> Subparts
| Selector | Role |
|---|---|
.card__header | Flex row at the top of the card with a divider against the content. Wraps the title and any header icons. |
.card__header-title | Grows to fill the header row; carries the bold weight. Pair with <h2>–<h4> as appropriate. |
.card__header-icon | Trailing icon slot, square-ish, aligned to the header height. Use a real <button> when interactive. |
.card__image | Media slot. Host a <figure> or <img>; child <img> stretches to 100% width. Recommended pairing: <figure class="image is-16by9"> from element/image so the aspect ratio is themable rather than baked inline. |
.card__content | Primary body region with $content-padding insets. |
.card__footer | Flex row at the bottom of the card with a divider against the content. |
.card__footer-item | Equal-width footer slot. Side dividers separate adjacent items. Centred inline-flex so a link or button reads the same. |
Accessibility
- A
.card__header-iconthat triggers an action should be a real<button type="button">with anaria-label. Decorative glyphs can stay in a<span>. - The header / footer dividers are decorative — no
aria-markup required. - Under
forced-colors: activethe dividers are pinned toCanvasTextso the structural separators remain visible (WCAG 1.4.11).
See also
- Element / Image — aspect-ratio
(
is-1by1,is-4by3,is-16by9,is-21by9, …) and fixed-size (is-32x32,is-64x64, …) helpers. Recommended pairing with.card__image. - Element / Box — atomic single-content surface. Reach for it when the card's header / image / footer subparts aren't needed.
- Layout / Section — full-bleed page band. Use Section instead of Card for hero-shaped page regions.
Variables
| Variable | Default | Notes |
|---|---|---|
$selector | '.card' | Class only: there is no semantic HTML "card". |
$header-selector | '.card__header' | BEM subpart; override in lockstep with $selector. |
$header-title-selector | '.card__header-title' | Grows to fill the header row. |
$header-icon-selector | '.card__header-icon' | Trailing icon slot. |
$image-selector | '.card__image' | Media slot. |
$content-selector | '.card__content' | Body region. |
$footer-selector | '.card__footer' | Action strip container. |
$footer-item-selector | '.card__footer-item' | Equal-width footer slot. |
$gap | $global-gap (1rem) | Inner padding when no subpart is present. Collapsed to 0 via :has() when structured. |
$border-radius | $global-border-radius (5px) | Surface corner radius. |
$header-padding-y | $size-small (0.75rem) | Vertical inset of .card__header-title / .card__header-icon. |
$header-padding-x | $global-gap (1rem) | Horizontal inset of header children. |
$header-title-weight | 600 | Bold weight on the header title. |
$header-min-height | $global-min-touch-target (2.75rem) | Floor so a header-icon button clears the AAA touch-target minimum. |
$content-padding | $global-gap (1rem) | Body region padding. |
$footer-padding-y | $size-small (0.75rem) | Vertical inset of footer items. |
$footer-item-padding | $size-small (0.75rem) | Horizontal inset of footer items. |
$subpart-divider-mix | 12% | Opacity for header / footer / inter-item dividers, mixed against currentColor. |
$container-name | null | Opt-in container-query host. When set to a string, emits container-type: inline-size; container-name: <value> on the card so consumers can scope rules to card width instead of the viewport. |
See Combinations and light variants for $enable-light, $default-combination, and $controls — cross-cutting plumbing common to every theme-aware component.
Override example
@use '@adnap/krysalicss/module/card' with (
$selector: '.surface',
$header-title-weight: 700,
$footer-item-padding: 1rem,
$border-radius: 12px,
); Tokens consumed
| Token | Used for |
|---|---|
--kc-card-bg / --kc-card-fg | Plain .card. |
--kc-{label}-bg / --kc-{label}-fg | Each combination modifier. |
--kc-{label}-light-bg / --kc-{label}-light-fg | .is-<combo>.is-light soft-tint variant. Emitted per theme by theme/_create.scss so the tint tracks the active surface. |