R RenDS v0.13.0

Primitive

Card CSS-only

A flexible content container with optional header, body, and footer sections. Five visual variants, status accents, and built-in interactive / selectable patterns. Pure CSS — no JavaScript required.

About

Overview

A card groups related content into a single visual unit — a profile, a stat, a settings panel, a list item, an article preview. RenDS gives you one base component with optional header / body / footer slots, five visual variants, and three interaction states (static, interactive, selectable).

Cards are entirely CSS-driven. There's no <ren-card> Web Component because there's nothing for JS to do — the structure is plain HTML, the variants are class modifiers, and the interactivity (hover, focus, selection) is handled by CSS pseudo-classes plus standard ARIA attributes.

When to use

  • To group a chunk of content (text + actions + metadata) that the user will visually scan as a single unit.
  • To present a list of similar items (a grid of products, posts, profiles, settings groups).
  • For status surfaces — use the data-status attribute to signal success, warning, danger, or info at a glance.

When not to use

  • For a single line of content — a card adds visual weight that body text doesn't need. Use a paragraph or a banner.
  • For a modal dialog — that's ren-dialog, which traps focus and dims the rest of the page.
  • For navigation between pages — wrap an anchor or use ren-link; cards make poor primary navigation.

Cards are containers, not components. They don't dictate what goes inside. Mix and match — drop in a button group, an avatar, a chart, a form. The card supplies the surface; you supply the content.

Structure

Anatomy

A card has a container plus three optional section slots. Each slot has its own padding rules so they nest cleanly without you fighting margins.

Assembled

Project update

Last edited 2 hours ago

Dashboard ready for review. All components passed a11y audit.

Container (.ren-card)

The outer surface. Sets background (--color-surface-raised), border, radius, and clips children with overflow: hidden.

Header (.ren-card-header)

Optional. Top section for title + description. Stack of .ren-card-title + .ren-card-description.

Body (.ren-card-body)

The main content area. Padded uniformly. When following a header, top padding tightens automatically.

Footer (.ren-card-footer)

Optional. Bottom row for actions. Use .ren-card-footer-border for a separating line above the footer.

Live

Demo

The default card with all three sections. Drop in your own content — RenDS won't fight you.

Welcome back

Pick up where you left off

Three drafts saved, two pull requests waiting for review, and a deployment scheduled for tomorrow.

<div class="ren-card">
  <div class="ren-card-header">
    <h4 class="ren-card-title">Welcome back</h4>
    <p class="ren-card-description">Pick up where you left off</p>
  </div>
  <div class="ren-card-body">
    <p>Three drafts saved, two pull requests waiting for review…</p>
  </div>
  <div class="ren-card-footer">
    <button class="ren-btn ren-btn-ghost ren-btn-sm">Later</button>
    <button class="ren-btn ren-btn-primary ren-btn-sm">Open dashboard</button>
  </div>
</div>

Sections are all optional. A card with only .ren-card-body works. A card with no sections at all (just children) auto-pads its first and last child via :first-child / :last-child selectors.

Catalog

Variants

Five visual variants — pick the one that matches the card's role. Plus three interaction states layered on top, and four status accents for at-a-glance signals.

Visual
Default

Surface + border

Elevated

Shadow, no border

Outline

Border, transparent bg

Ghost

No surface, grouping only

Sunken

Recessed surface

Interaction
Interactive

Hover lifts, click feedback

Selectable

Click to set aria-selected

Selectable (selected)

Accent border + ring

Status
Success
Warning
Danger
Info

Reference

API

CSS classes only. There is no Web Component for card.

Container

ClassEffect
.ren-cardBase container. Required.
.ren-card-elevatedRemoves border, adds --shadow-md. For floating UI.
.ren-card-outlineBorder only, transparent background. Lighter weight.
.ren-card-ghostRemoves both border and background. Pure structural grouping.
.ren-card-sunkenInverted depth — sits below the surface. Use sparingly.

Sections

ClassUse
.ren-card-headerTop section. Stack of title + description with auto-spacing.
.ren-card-titleHeading inside the header. Set on the actual heading tag (<h3>, <h4>, etc).
.ren-card-descriptionSubtitle / metadata under the title. Muted color.
.ren-card-bodyMain content. Self-padded.
.ren-card-footerBottom row, no separator. For action clusters.
.ren-card-footer-borderFooter with a top border separator. Heavier visual weight.
.ren-card-simpleQuick padding shorthand for cards without explicit sections.

Interaction

Class / attributeEffect
.ren-card-interactiveHover lift + shadow + border emphasis. Adds cursor: pointer, focus ring on :focus-visible. Wrap in or apply to a focusable element (<a>, <button>, or tabindex="0").
.ren-card-selectableHover swaps border to accent. Combine with aria-selected="true" or data-selected for the chosen state.
[data-status="success|warning|danger|info"]Adds a 3px accent border on top in the matching status color. Stacks with any visual variant.

Layout helper

ClassEffect
.ren-card-groupAuto-fill grid of cards at min(100%, 280px) per column. Drop on the parent of the cards.
.ren-card-coverFor a full-bleed image at the top of a card. width: 100%; object-fit: cover.

Inclusive by default

Accessibility

Static cards are non-interactive — they're just visual containers and don't need any ARIA. Interactive and selectable cards inherit semantics from the wrapping element you choose.

Static cards

  • Use semantic content inside — proper heading levels (<h2><h4>) for titles, real <p> tags for prose, real <button>s for actions.
  • Don't add ARIA roles to the card itself — it's a generic <div> by intent.
  • For status accents, the visual data-status="danger" top border isn't enough on its own. Pair with a text label, an icon, or an aria-describedby for screen reader users.

Interactive cards

If the entire card is clickable, wrap the card content in an <a> or <button>, or set tabindex="0" + role="button" if neither makes sense.

TabMoves focus into the interactive card.
EnterActivates the card (follows the wrapping <a> or fires <button>'s click).
SpaceActivates if the wrapper is a <button>. Anchors don't respond to Space — use Enter.

Don't nest interactive elements. An interactive card that contains a button creates a confusing focus order — Tab, Tab, Tab to land somewhere predictable. Pick one: either the whole card is clickable, or the card has internal buttons. Not both.

Selectable cards (radio / checkbox patterns)

  • For exclusive choice (one of N), wrap each card in a <label> with a hidden <input type="radio">, or use role="radio" + aria-checked.
  • For multi-select, use role="checkbox" + aria-checked, or a hidden <input type="checkbox"> in a label.
  • Set aria-selected="true" on the chosen card so screen readers announce "selected". The CSS targets both aria-selected and data-selected for the accent ring.

Reduced motion

The hover-lift on interactive cards uses --duration-tactile and respects prefers-reduced-motion via the semantic motion tokens. Users who request reduced motion get a quieter transition (color change without scale).

Recipes

Examples

Common compositions. Copy the markup, swap the content.

KPI / stat card

For dashboards. One number, one label, optional trend.

Active users

12,430

↑ 12.5% vs last month

<div class="ren-card">
  <div class="ren-card-body">
    <p class="stat-label">Active users</p>
    <p class="stat-value">12,430</p>
    <p class="stat-trend">↑ 12.5% vs last month</p>
  </div>
</div>

Selectable plan card (radio pattern)

For pricing tiers, plan switchers, theme pickers.

<label class="ren-card ren-card-selectable">
  <input type="radio" name="plan" />
  <div class="ren-card-body">
    <strong>Pro</strong>
    <p>$12/mo · unlimited</p>
  </div>
</label>

Article preview (interactive card with cover)

Wrap the whole card in <a> so the entire surface is one click target.

<a href="/post/123" class="ren-card ren-card-interactive">
  <div class="cover"></div>
  <div class="ren-card-header">
    <h4 class="ren-card-title">From Tailwind to vanilla…</h4>
    <p class="ren-card-description">5 min read · Engineering</p>
  </div>
  <div class="ren-card-body">
    <p>How we replaced a 180 KB toolchain…</p>
  </div>
</a>

Danger-zone card

For destructive actions in settings panels. Status border + danger button.

Delete account

Permanently remove your account and all associated data.

<div class="ren-card" data-status="danger">
  <div class="ren-card-header">
    <h4 class="ren-card-title">Delete account</h4>
    <p class="ren-card-description">Permanently remove…</p>
  </div>
  <div class="ren-card-footer-border">
    <button class="ren-btn ren-btn-danger ren-btn-sm">Delete account</button>
  </div>
</div>

Card group (auto-grid)

One class on the parent gives you a responsive grid of cards.

Item one

Auto-sized

Item two

Wraps responsively

Item three

No JS needed

<div class="ren-card-group">
  <div class="ren-card">…</div>
  <div class="ren-card">…</div>
  <div class="ren-card">…</div>
</div>