role container
Card draft v0.1.0
A surface that groups a title and related content as one unit.
A card gathers what belongs together, a title and its content, on a raised surface with its own edge, so a page of many things reads as a grid of units: a project in a list of projects, a plan in a pricing page, a setting group. It is a surface, not a control: a card that opens something holds a link or a button; a card is never itself clickable. Its surface level follows the Color foundation.
Web implementation
Root .ds-card on a <div>. Properties map to attributes: surface → data-surface
packages/components/src/components/card/card.css
/*
* Card — vanilla CSS reference implementation.
* Spec: packages/components/specs/components/card.spec.json
* Tokens: packages/tokens/dist/css/components/card.css (generated from packages/tokens/src/components/card.tokens.json)
*
* Rules:
* - Only --card-* custom properties may appear here. No raw values, no semantic tokens.
* - The root is a plain container on a surface level of the Color foundation; the surface is a
* data attribute; a card has no interaction state of its own.
*/
.ds-card {
--_background: var(--card-container-background-level-1);
--_border-color: var(--card-container-border-color-level-1);
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: var(--card-container-gap);
min-inline-size: 0;
padding: var(--card-container-padding);
border: var(--card-container-border-width) solid var(--_border-color);
border-radius: var(--card-container-border-radius);
background-color: var(--_background);
color: var(--card-description-color);
}
.ds-card[data-surface="level-2"] {
--_background: var(--card-container-background-level-2);
--_border-color: var(--card-container-border-color-level-2);
}
.ds-card__title {
margin: 0;
color: var(--card-title-color);
font-family: var(--card-title-typography-font-family);
font-size: var(--card-title-typography-font-size);
font-weight: var(--card-title-typography-font-weight);
line-height: var(--card-title-typography-line-height);
letter-spacing: var(--card-title-typography-letter-spacing);
}
.ds-card__description {
margin: 0;
color: var(--card-description-color);
font-family: var(--card-description-typography-font-family);
font-size: var(--card-description-typography-font-size);
font-weight: var(--card-description-typography-font-weight);
line-height: var(--card-description-typography-line-height);
letter-spacing: var(--card-description-typography-letter-spacing);
}
packages/components/src/components/card/card.template.mjs
/**
* Card — HTML template for the web implementation.
* Pure function of the spec properties (packages/components/specs/components/card.spec.json) -> HTML string.
* A plain container (a div: no role of its own) on a surface level, with a heading as its title
* and a paragraph as its description; `headingLevel` (2 to 6, default 3) fits the card into the
* page's heading outline.
*/
import { escapeHtml, escapeAttr } from '../../lib/template-utils.mjs';
export const meta = { id: 'card', selector: '.ds-card' };
export function render(props = {}) {
const { title = 'Title', description = null, surface = 'level-1', headingLevel = 3, state = null } = props;
const level = Math.min(6, Math.max(2, Number(headingLevel) || 3));
const attrs = ['class="ds-card"', `data-surface="${escapeAttr(surface)}"`];
if (state && state !== 'default') attrs.push(`data-state="${escapeAttr(state)}"`);
const parts = [`<h${level} class="ds-card__title" data-part="title">${escapeHtml(title)}</h${level}>`];
if (description) parts.push(`<p class="ds-card__description" data-part="description">${escapeHtml(description)}</p>`);
return `<div ${attrs.join(' ')}>${parts.join('')}</div>`;
}
No other platform implementation recorded yet. Platform token outputs live under Platforms.