Skip to content

component :: Card

The .card component, in its simplest form, is a container with 3 style options:

  • Default: transparent background, border outline.
  • Filled: solid background color.
  • Raised: drop shadow and hover effect.

Every part is optional: header with title and subtitle, image (inset, or edge-to-edge with the flush modifier), badges, tags, byline with avatar and date, actions footer, clickable body, disabled state, and loading state with skeleton UI. The Astro component makes it easy to use all those features, and CardList lays cards out in equal-height responsive columns. This site’s own blog index is built from exactly these two components.

A simple filled card

Natural Law, is a set of universal, non-man-made, binding and unchangeable conditions which govern the consequence of our actions.

A simple raised card

Natural Law, is a set of universal, non-man-made, binding and unchangeable conditions which govern the consequence of our actions.

File Description Source
component.card.css All card styles (.card) Github
component.cardList.css Equal-height list layout Github
Card.astro, CardList.astro The Astro components Github
Tags.astro, Avatar.astro Composed components (dependencies) Github
--card-* block in settings.ui.css Interface tokens (see below) Github

Playground

Card title

Category

This is where the content goes!

Variation

Variant
Image

Parts

State

Content

Copied!
<article class="card">
<header class="card_header">
  <div class="card_header_text">
    <h2 class="card_title">Card title</h2>
    <p>Category</p>
  </div>
</header>
<section class="card_content">
  <p>This is where the content goes!</p>
</section>
</article>

HTML

Available modifiers

Available modifiers Description
.card Default style. Transparent background with border.
.card + .card-filled Filled with a background color.
.card + .card-raised Drop shadow, animated on hover.
.card + .is-disabled Lowered opacity. Interactivity disabled.
.card_media + .card_media-flush Edge-to-edge image on top of the card.

Clickable cards

A card carries one link: put .card_link on a real anchor (usually the title link) and it stretches over the whole card with a ::after, so screen readers hear one link named by the title. Actions stay clickable because .card_actions is positioned and comes later in the DOM; tags stay clickable through their own z-index bump. The blog-index pattern combines this with a flush image, tags, and a byline, wrapped in a .cardList over the grid system:

<ul class="cardList grid" col="1" col-md="2" col-lg="2">
<li>
<article class="card">
<figure class="card_media card_media-flush">
<img class="card_image" src="cover.jpg" alt="" loading="lazy" />
</figure>
<nav aria-label="Tags"><ul class="tags tags-inline card_tags">
<li><a href="/blog/tags/css">css</a></li>
</ul></nav>
<header class="card_header">
<div class="card_header_text">
<h3 class="card_title"><a class="card_link" href="/blog/my-post">Designing with cascade layers</a></h3>
</div>
</header>
<div class="card_content"><p>Why layer order beats specificity wars.</p></div>
<footer class="card_byline">
<!-- avatar markup, see the Avatar docs -->
<div>
<span class="card_author">Sonny Mueller</span>
<time datetime="2026-05-02">May 2, 2026</time>
</div>
</footer>
</article>
</li>
</ul>

Custom properties

The following custom properties are available in settings.ui.css:

Property Color
--card-spacing General padding inside the card.
--card-color Text color.
--card-secondary-text-color Subtitle, date, and secondary text color.
--card-bg-color Background color of the “filled” variant.
--card-border-radius Border radius.
--card-border-color Border color.
--card-border-width Border width.
--card-shadow Default drop shadow or the “raised” variant.
--card-shadow-hover Hover state drop shadow.
--card-aspect-ratio Image aspect ratio (defaults to --ar-golden).
--card-loading-bg Loading skeleton background color.

Badge colors come from the badge component’s --badge-* tokens.

Astro component

Card

Every part is optional; render only what you pass. Badges and tags can coexist, but they compete for attention: badges are non-interactive status labels pinned top-right, tags are taxonomy links in the content flow. Prefer one or the other on a given card.

Prop Type Default Description
variant string undefined Card’s visual style: filled, raised.
isDisabled boolean false Disabled style. Disabled interactions.
isLoading boolean false Loading state with skeleton UI.
href string undefined Makes the whole card clickable (through the title link, if any).
title string undefined Header title text.
subtitle string undefined Header subtitle text.
headingLevel number 2 Heading level of the title: 2, 3, or 4.
imageSrc ImageMetadata | string undefined Add an image to the card (imported asset or URL/path).
imageAlt string '' Alt text for the image.
aspectRatio string golden Image aspect ratio (available aspect ratios).
flushImage boolean false Edge-to-edge image on top of the card instead of inset.
badges array [] Array of objects (see example below).
tags string[] [] Tag names, rendered with the Tags component.
tagsUrl string '/blog/tags' Base URL for tag links.
author string undefined Byline name (also feeds the Avatar initials).
avatarSrc ImageMetadata | string undefined Byline avatar image.
date Date undefined Publication date, rendered as a <time> in the byline.
locale string 'en-US' Locale for the formatted date.
data-testid string 'card' Test ID for testing frameworks.
class string undefined Additional CSS classes, useful for helper classes.

CardList

A ul over the grid system whose items stretch, so cards in a row share the same height. Children go in the default slot; wrap each card in an <li>.

Prop Type Default Description
cols number 2 Columns from --lg: 1, 2, or 3.

Slots

The Card component supports the following slots for content projection:

  • Default slot: Main content area (e.g., text or paragraphs).
  • Actions slot: Action buttons or links in the footer.

It is recommended to use the button component in the actions slot, e.g.:

<Fragment slot="actions">
<a class="bt bt-text" href="/home">Cancel</a>
<button class="bt bt-primary">Add to Cart</button>
</Fragment>

Badge Styling

The badges prop takes an array of objects. Each object consists of a label property which defines the badge’s text, and an optional color property that defines the badge’s background color. If no color is provided, it defaults to --badge-background-color from the badge component.

A card with too many badges

WayTooManyBadges

This is where the content goes. Unless you only want a bunch of badges!

<Card
title="A card with too many badges"
badges={[
{ label: "Way", color: "#a6b65a" },
{ label: "Too", color: "#86615c" },
{ label: "Many", color: "#81a598" },
{ label: "Badges", color: "#e3863d" },
]}
>
<p>This is where the content goes. Unless you only want a bunch of badges!</p>
</Card>

Examples