Card
A card container with media, header, title, description, content and footer slots. Supports variants, sizes and an interactive state; compose or use slots standalone.
Installation
pnpm dlx @elyri/cli@latest add cardExamples
Project card
A gradient media area, project icon, key metrics, progress and member avatars composed into a rich project card.
Aurora Dashboard
Realtime analytics for the growth team.
import type { CSSProperties } from 'react';
import { MeshGradient } from './components/elyri/MeshGradient';
import { Avatar, AvatarGroup } from './components/elyri/Avatar';
import { Button } from './components/elyri/Button';
import { Card } from './components/elyri/Card';
import { Progress } from './components/elyri/Progress';
const meshColors: [string, string, string, string] = ['#6d5bd0', '#9b6bff', '#ff8fb1', '#38bdf8'];
const mediaStyle: CSSProperties = {
position: 'relative',
height: 148,
};
const identityStyle: CSSProperties = { display: 'flex', alignItems: 'center', gap: 12 };
const titleStackStyle: CSSProperties = { minWidth: 0 };
const statsStyle: CSSProperties = {
display: 'grid',
gridTemplateColumns: 'repeat(3, 1fr)',
gap: 12,
padding: '4px 0 8px',
};
const statValueStyle: CSSProperties = { fontSize: 16, fontWeight: 700, lineHeight: 1.2 };
const statLabelStyle: CSSProperties = { marginTop: 2, fontSize: 12, color: 'var(--elyri-ui-muted)' };
export function Example() {
return (
<Card variant="elevated">
<Card.Media style={mediaStyle}>
<MeshGradient colors={meshColors} speed={0.5} interactive={false} style={{ height: '100%' }} />
</Card.Media>
<Card.Header>
<div style={identityStyle}>
<Avatar src="/logo.svg" alt="Aurora" shape="square" size="lg" fit="contain" />
<div style={titleStackStyle}>
<Card.Title>Aurora Dashboard</Card.Title>
<Card.Description>Realtime analytics for the growth team.</Card.Description>
</div>
</div>
<Card.Action>
<Button size="sm" variant="ghost" iconOnly aria-label="More">
⋯
</Button>
</Card.Action>
</Card.Header>
<Card.Content>
<div style={statsStyle}>
<div>
<div style={statValueStyle}>24</div>
<div style={statLabelStyle}>Tasks</div>
</div>
<div>
<div style={statValueStyle}>6</div>
<div style={statLabelStyle}>In review</div>
</div>
<div>
<div style={statValueStyle}>72%</div>
<div style={statLabelStyle}>Sprint</div>
</div>
</div>
<Progress value={72} size="sm" label="Sprint progress" />
</Card.Content>
<Card.Footer align="between">
<AvatarGroup size="sm">
<Avatar name="Ada Lovelace" />
<Avatar name="Alan Turing" />
<Avatar name="Grace Hopper" />
</AvatarGroup>
<Button size="sm">Open project</Button>
</Card.Footer>
</Card>
);
}Basic
Title, description, content and footer composed into a full card.
Ship faster
A neutral card that inherits the host theme through CSS variables.
import { Tag } from './components/elyri/Tag';
import { Button } from './components/elyri/Button';
import { Card } from './components/elyri/Card';
export function Example() {
return (
<Card>
<Card.Title>Ship faster</Card.Title>
<Card.Description>A neutral card that inherits the host theme through CSS variables.</Card.Description>
<Card.Content>
<div className="demo-ui-row demo-ui-row--start">
<Tag variant="accent">v0.0.1</Tag>
<Tag variant="success">Stable</Tag>
<Tag>Zero deps</Tag>
</div>
</Card.Content>
<Card.Footer>
<Button size="sm">Get started</Button>
<Button size="sm" variant="ghost">
Dismiss
</Button>
</Card.Footer>
</Card>
);
}Variants
outlined / elevated / filled / ghost appearances.
outlined
variant="outlined"
elevated
variant="elevated"
filled
variant="filled"
ghost
variant="ghost"
import { Card } from './components/elyri/Card';
const variants = ['outlined', 'elevated', 'filled', 'ghost'] as const;
export function Example() {
return (
<div className="demo-ui-grid">
{variants.map((variant) => (
<Card key={variant} variant={variant} size="sm">
<Card.Title>{variant}</Card.Title>
<Card.Description>variant="{variant}"</Card.Description>
</Card>
))}
</div>
);
}Interactive
With interactive enabled the card lifts on hover; add tabIndex for keyboard focus.
Starter
Everything you need to prototype.
Pro
Advanced components and priority support.
import { Tag } from './components/elyri/Tag';
import { Card } from './components/elyri/Card';
export function Example() {
return (
<div className="demo-ui-grid">
<Card interactive tabIndex={0}>
<Card.Header>
<Card.Title>Starter</Card.Title>
<Card.Action>
<Tag size="sm">Free</Tag>
</Card.Action>
</Card.Header>
<Card.Description>Everything you need to prototype.</Card.Description>
</Card>
<Card interactive tabIndex={0}>
<Card.Header>
<Card.Title>Pro</Card.Title>
<Card.Action>
<Tag size="sm" variant="accent">
Popular
</Tag>
</Card.Action>
</Card.Header>
<Card.Description>Advanced components and priority support.</Card.Description>
</Card>
</div>
);
}Minimal
Only the title and description slots.
Zero config
Sensible defaults, no setup required.
import { Card } from './components/elyri/Card';
export function Example() {
return (
<Card>
<Card.Title>Zero config</Card.Title>
<Card.Description>Sensible defaults, no setup required.</Card.Description>
</Card>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'outlined' | 'elevated' | 'filled' | 'ghost' | 'outlined' | Visual style |
size | 'sm' | 'md' | 'lg' | 'md' | Padding size |
interactive | boolean | false | Interactive: lift on hover, focus ring on focus |
Card.Media | HTMLAttributes<HTMLDivElement> | — | Full-bleed media area, aligned to the corners when placed first |
Card.Header | HTMLAttributes<HTMLDivElement> | — | Header with title/description on the left and Card.Action on the right |
Card.Action | HTMLAttributes<HTMLDivElement> | — | Action area on the right of the header |
Card.Title | HTMLAttributes<HTMLHeadingElement> | — | Title slot |
Card.Description | HTMLAttributes<HTMLParagraphElement> | — | Description slot |
Card.Content | HTMLAttributes<HTMLDivElement> | — | Free-form content area |
Card.Footer | { align?: 'start' | 'end' | 'between' } | 'start' | Footer, usually for actions; align controls alignment |
...rest | HTMLAttributes | — | Remaining props are forwarded to the native div |