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__footer subparts 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')
);
Playground

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')
);
Playground

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')
);
Playground

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')
);
Playground

Markup

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

SelectorRole
.card__headerFlex row at the top of the card with a divider against the content. Wraps the title and any header icons.
.card__header-titleGrows to fill the header row; carries the bold weight. Pair with <h2><h4> as appropriate.
.card__header-iconTrailing icon slot, square-ish, aligned to the header height. Use a real <button> when interactive.
.card__imageMedia 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__contentPrimary body region with $content-padding insets.
.card__footerFlex row at the bottom of the card with a divider against the content.
.card__footer-itemEqual-width footer slot. Side dividers separate adjacent items. Centred inline-flex so a link or button reads the same.

Accessibility

  • A .card__header-icon that triggers an action should be a real <button type="button"> with an aria-label. Decorative glyphs can stay in a <span>.
  • The header / footer dividers are decorative — no aria- markup required.
  • Under forced-colors: active the dividers are pinned to CanvasText so 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

VariableDefaultNotes
$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-weight600Bold 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-mix12%Opacity for header / footer / inter-item dividers, mixed against currentColor.
$container-namenullOpt-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

app.scss
@use '@adnap/krysalicss/module/card' with (
  $selector: '.surface',
  $header-title-weight: 700,
  $footer-item-padding: 1rem,
  $border-radius: 12px,
);

Tokens consumed

TokenUsed for
--kc-card-bg / --kc-card-fgPlain .card.
--kc-{label}-bg / --kc-{label}-fgEach 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.