Card
A block container with a clean surface, hairline border, and an interactive state when the card is rendered as a link or button. Class + data-attribute API: one .x-card class plus data-variant and data-size.
Install
import "elements-kit/ui/styles/theme.css";import "elements-kit/ui/styles/scaling.css";import "elements-kit/ui/styles/radius.css";import "elements-kit/ui/styles/space.css";import "elements-kit/ui/styles/typography.css";import "elements-kit/ui/styles/shadow.css";import "elements-kit/ui/styles/material.css";import "elements-kit/ui/styles/palette/gray.css";import "elements-kit/ui/styles/neutral/gray.css";
import "elements-kit/ui/card/card.css";unset.css is not required for <div>s. When the card is a <button> or <a>, add unset first to neutralise the native rendering before applying .x-card.
API
<div class="x-card" data-variant="surface" data-size="3"> Card content.</div>| Attribute | Values |
|---|---|
data-variant | surface (default), elevated, borderless |
data-size | 1, 2, 3 (default), 4, 5 |
data-radius (on a parent) | none, small, medium, large, pill |
data-material-background (on a parent) | solid, translucent |
data-accent (on a parent) | any imported color scale β used by the focus ring |
Sizing
data-size scales padding and border-radius together.
data-size | padding | radius |
|---|---|---|
1 | --space-3 | --radius-4 |
2 | --space-4 | --radius-4 |
3 (default) | --space-5 | --radius-5 |
4 | --space-6 | --radius-5 |
5 | --space-8 | --radius-6 |
Set data-radius on an ancestor to scale corner curvature globally; --scaling on <html> scales padding and radius together.
Variants
surfaceβ 1px hairline border (--neutral-a5). Default. The everyday card.elevatedβ same border plus a tailored drop shadow. Uses dual outer-on-element + inner-on-::aftershadows (--card-elevated-box-shadow-outer/-inner) with matching 6-layer counts across neutral/hover/active so the lift transitions smoothly. Use for primary, hover-able cards.borderlessβ no border, no background. Hover fills with--neutral-a3. Use inside dense lists or as clickable rows.
Inset
data-inset on a direct child of .x-card makes that child bleed past the cardβs padding to the cardβs edge. Common case: a hero image at the top of a card.
without inset: with data-inset="top":
βββββββββββββββββββββββ ββββββββββββββββββββββββ βββββββββββββββββ β ββββββββββββββββββββββββ βββ HERO IMAGE ββ β βββββ HERO IMAGE βββββββ βββββββββββββββββ β ββββββββββββββββββββββββ β βββββββββββββββββββββββ€β Heading β β Heading ββ Body β β Body ββββββββββββββββββββββββ βββββββββββββββββββββββ<div class="x-card"> <img data-inset="top" src="/hero.webp" alt="" /> <h3>Title</h3> <p>Body</p></div>| Value | Side | Use case |
|---|---|---|
data-inset="top" | block-start | Vertical card β hero at top |
data-inset="bottom" | block-end | Vertical card β footer media at bottom |
data-inset="start" | inline-start (left LTR / right RTL) | Horizontal card β leading media |
data-inset="end" | inline-end | Horizontal card β trailing media |
Pick the pair that matches your cardβs orientation. top / bottom are physical (block axis). start / end are logical (inline axis) and flip automatically in RTL.
The inset element rounds the corners it bleeds into and squares the others. The corner radius is computed from the cardβs --card-border-radius, so it always lines up flush with the cardβs border.
Surface material
The card reads var(--color-material, var(--color-surface)) for its background, so by default it sits on the theme surface color. Wrap any ancestor with data-material-background="translucent" to swap to a frosted backdrop:
<section data-material-background="translucent"> <div class="x-card">Frosted</div></section>Available values: solid, translucent. translucent adds a 64px backdrop blur.
Interactive cards
When the card itself is an anchor, button, or label, hover and active states apply. Wrap the underlying element with unset to drop native styling first:
<a href="/post" class="unset x-card" data-variant="elevated"> <h3>Post title</h3> <p>Bodyβ¦</p></a>| State | surface | elevated | borderless |
|---|---|---|---|
| hover | border bumps to --neutral-a7 | shadow lifts to --shadow-3 | fills with --neutral-a3 |
| active | border at --neutral-a6 | shadow returns to --shadow-2 | fills with --neutral-a4 |
Non-interactive cards (a plain <div>) skip these states entirely β no need to gate them with media queries.
Focus
When a card is focusable, focus shows a 2px outline using --focus-8. Set data-accent on an ancestor to pick the accent color for the focus ring.
<div data-accent="mint"> <a href="/post" class="unset x-card">β¦</a></div>Theming
Light/dark flips automatically via the .dark class on a parent β see Light & dark. The surface picks up --color-material (translucent backdrop) or --color-surface (solid neutral), both defined in theme.css.
Accessibility
.x-card ships with no ARIA role β itβs a pure visual class. When the card is interactive, render it as an <a> or <button> so it picks up the correct semantics, keyboard handling, and the kitβs focus styles. Wrap nested links in a non-interactive card with care: nested interactives are an accessibility footgun.