Default
import { PixelCard } from '@pxlkit/ui-kit';
export function Default() {
return (
<PixelCard title="Project Atlas">
<p>Compact dossier on the Atlas migration. Status nominal.</p>
</PixelCard>
);
}Container card with title, optional icon, description, media, ribbon badge, body, and footer — surfaces as <article>, <a href>, or role="button" depending on props.
import { PixelCard } from '@pxlkit/ui-kit';| Prop | Type | Default | Description |
|---|---|---|---|
title | string | — | Heading rendered inside the auto-generated header. Omit for a headerless container/well card. |
icon | React.ReactNode | — | Optional leading icon for the auto-generated header. |
children | React.ReactNode | — | Card body content. |
footer | React.ReactNode | — | Optional footer slot rendered after the body with a divider. |
surface | 'pixel' | 'linear' | — | Visual surface override. Inherits from provider when omitted. |
tone | 'neutral' | 'green' | 'cyan' | 'gold' | 'red' | 'purple' | 'pink' | — | Optional tone tint applied to border + soft background. |
interactive | boolean | — | When true, adds a hover lift + focus ring. If no href is set, an onClick is REQUIRED — the card renders with role="button" + tabIndex={0} + Enter/Space activation for keyboard parity. |
media | React.ReactNode | — | Top media slot rendered above the header. Clipped by overflow:hidden. |
badge | { label: string; tone?: ToneKey } | — | Corner ribbon badge — renders PixelRibbon. |
description | string | — | Muted paragraph rendered under the title. |
descriptionLines | 2 | 3 | 4 | — | Apply line-clamp-N + min-h-[N em] to the description. |
href | string | — | When provided, the root renders as <a href> instead of <article>. ⚠️ Nesting interactive children (PixelButton, PixelTextLink, etc.) inside footer / media / children is invalid HTML in href mode — screen readers cannot navigate nested interactives inside an anchor. Render those outside the card when you need an actionable area. |
target | '_self' | '_blank' | '_parent' | '_top' | (string & {}) | — | Anchor target — only meaningful when href is set. |
rel | string | — | Anchor rel — only meaningful when href is set. |
padding | 'none' | 'sm' | 'md' | 'lg' | — | Padding scale; default keeps the legacy p-4 rhythm. |
bordered | boolean | true | Render with surface-aware border + radius chrome. Defaults to true — a card should look like a card. |
Also takes the native attributes and event handlers of its element (HTMLAttributes<HTMLElement>).
ref points to its element (HTMLElement | HTMLAnchorElement).
No props of its own.
Also takes the native attributes and event handlers of its element (HTMLAttributes<HTMLElement>).
No props of its own.
Also takes the native attributes and event handlers of <div> (HTMLAttributes<HTMLDivElement>).
No props of its own.
Also takes the native attributes and event handlers of its element (HTMLAttributes<HTMLElement>).
WCAG target: 2.1 AA
buttonWhen interactive without href, the root renders as <div role="button" tabindex="0"> with Enter/Space activation parity (<article> does not permit role="button"). When href is set, the root renders as a native <a> — nesting interactive children (buttons, links) inside footer or media is invalid in href mode and breaks screen reader navigation. Focus-visible ring is provided automatically in both interactive modes.
| Key | Action | When |
|---|---|---|
| Enter | Activates the card when interactive (role="button") or follows the link when href is set | — |
| Space | Activates the card when interactive (role="button") | interactive prop is true and href is not set |
| Tab | Moves focus to the card when interactive or anchored | — |
import { PixelCard } from '@pxlkit/ui-kit';
export function Default() {
return (
<PixelCard title="Project Atlas">
<p>Compact dossier on the Atlas migration. Status nominal.</p>
</PixelCard>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function Default() {
return (
<PixelCard title="Project Atlas">
<p>Compact dossier on the Atlas migration. Status nominal.</p>
</PixelCard>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function Headerless() {
return (
<PixelCard>
<p>Omit the title to get a plain well container — no header, no divider.</p>
</PixelCard>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function WithIcon() {
const Icon = (
<svg viewBox="0 0 8 8" shapeRendering="crispEdges" fill="currentColor" className="h-3 w-3">
<rect x="3" y="0" width="2" height="8" />
<rect x="0" y="3" width="8" height="2" />
</svg>
);
return (
<PixelCard title="System Health" icon={Icon}>
<p>All checks green. Last sync 3 minutes ago.</p>
</PixelCard>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function WithDescription() {
return (
<PixelCard
title="Release Notes"
description="A short summary of what changed in this release, useful as a card subtitle."
>
<p>Body content sits under the description.</p>
</PixelCard>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function WithFooter() {
return (
<PixelCard
title="Invoice #1042"
footer={<span className="text-xs text-retro-muted">Due in 7 days</span>}
>
<p>Total: $1,250.00</p>
</PixelCard>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function Tones() {
return (
<div className="grid grid-cols-2 gap-3">
<PixelCard title="Cyan" tone="cyan">Tinted border + soft background.</PixelCard>
<PixelCard title="Green" tone="green">Tinted border + soft background.</PixelCard>
<PixelCard title="Gold" tone="gold">Tinted border + soft background.</PixelCard>
<PixelCard title="Purple" tone="purple">Tinted border + soft background.</PixelCard>
</div>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function Surfaces() {
return (
<div className="grid grid-cols-2 gap-3">
<PixelCard title="Pixel" surface="pixel">Thick border + offset shadow.</PixelCard>
<PixelCard title="Linear" surface="linear">Soft border + smooth radius.</PixelCard>
</div>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function Interactive() {
return (
<PixelCard
title="Click me"
interactive
onClick={() => alert('card clicked')}
description="Press Enter or Space to activate via keyboard."
>
<p>Renders as role=button with focus ring.</p>
</PixelCard>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function AsLink() {
return (
<PixelCard
title="Read the docs"
href="https://example.com"
target="_blank"
rel="noopener noreferrer"
description="Root renders as <a href> when href is provided."
/>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function WithMedia() {
const Media = (
<div className="h-24 w-full bg-gradient-to-br from-retro-cyan/40 to-retro-purple/40" />
);
return (
<PixelCard title="Cover Story" media={Media} description="Media slot sits above the header.">
<p>Card body.</p>
</PixelCard>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function WithBadge() {
return (
<PixelCard
title="New Feature"
badge={{ label: 'NEW', tone: 'gold' }}
description="Ribbon badge anchors in the top-right corner."
>
<p>Useful for highlighting fresh content.</p>
</PixelCard>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function ClampedDescription() {
return (
<PixelCard
title="Long Description"
description="This description is intentionally long to demonstrate the line-clamp behavior. It will be truncated to the configured number of lines with an ellipsis, while maintaining a minimum height so cards stay aligned in a grid."
descriptionLines={2}
>
<p>Body still renders below the clamp.</p>
</PixelCard>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function PaddingScale() {
return (
<div className="grid grid-cols-2 gap-3">
<PixelCard title="Small" padding="sm">Tight padding.</PixelCard>
<PixelCard title="Large" padding="lg">Roomy padding.</PixelCard>
</div>
);
}import { PixelCard } from '@pxlkit/ui-kit';
export function WithSubcomponents() {
return (
<PixelCard title="Composed">
<PixelCard.Header>
<span className="text-sm font-semibold">Custom header</span>
</PixelCard.Header>
<PixelCard.Body>
<p>Body slot via subcomponent.</p>
</PixelCard.Body>
<PixelCard.Footer>
<span className="text-xs text-retro-muted">Footer slot</span>
</PixelCard.Footer>
</PixelCard>
);
}