Skip to content
omaris

Containment

Card

MD3 card — elevated, filled, or outlined.

import { Card } from 'omaris'
Learn

Examples

Basic

Header, body, footer. The body is the part that grows, so a row of cards with different amounts to say still lines its footers up.

Invoice #4021

Due in 14 days

Kurdistan Coffee Roasters — 24 kg green Arabica, delivered 3 March.
<script lang="ts">	import { Button, Card, CardContent, CardFooter, CardHeader } from 'omaris';</script>​<Card class="w-80 max-w-full">	<CardHeader heading="Invoice #4021" description="Due in 14 days" />	<CardContent>Kurdistan Coffee Roasters — 24 kg green Arabica, delivered 3 March.</CardContent>	<CardFooter>		<Button variant="text">Dismiss</Button>		<Button>Pay now</Button>	</CardFooter></Card>

Variants

outlined is the default. elevated lifts — a grid of cards on a page wants it; filled sits in a tinted well, for a card inside another surface.

elevated

The same card, three surfaces.

filled

The same card, three surfaces.

outlined

The same card, three surfaces.
<script lang="ts">	import { Card, CardContent, CardHeader } from 'omaris';</script>​<Card variant="elevated" class="w-56 max-w-full">	<CardHeader heading="elevated" />	<CardContent>The same card, three surfaces.</CardContent></Card><Card variant="filled" class="w-56 max-w-full">	<CardHeader heading="filled" />	<CardContent>The same card, three surfaces.</CardContent></Card><Card variant="outlined" class="w-56 max-w-full">	<CardHeader heading="outlined" />	<CardContent>The same card, three surfaces.</CardContent></Card>

With media

bleed cancels the card's padding so the picture reaches the edges, and rounds only the corners it actually touches.

Erbil citadel

Six thousand years, still occupied

One of the longest continuously inhabited sites on earth.
<script lang="ts">	import { Card, CardContent, CardHeader, CardMedia } from 'omaris';</script>​<Card variant="elevated" class="w-72 max-w-full">	<CardMedia ratio="16/9">		<div			class="size-full bg-linear-to-br from-primary-container to-tertiary-container"			aria-hidden="true"		></div>	</CardMedia>	<CardHeader heading="Erbil citadel" description="Six thousand years, still occupied" />	<CardContent>One of the longest continuously inhabited sites on earth.</CardContent></Card>

Interactive

A picker — plans, delivery options. interactive makes the whole card pressable, with a ripple, and selected marks the chosen one.

<script lang="ts">	import { Card, CardContent, CardHeader } from 'omaris';​	let chosen = $state('standard');</script>​<Card	interactive	selected={chosen === 'standard'}	class="w-52 max-w-full"	onclick={() => (chosen = 'standard')}	aria-pressed={chosen === 'standard'}>	<CardHeader heading="Standard" description="3–5 days" />	<CardContent>Delivered by our own drivers.</CardContent></Card><Card	interactive	selected={chosen === 'express'}	class="w-52 max-w-full"	onclick={() => (chosen = 'express')}	aria-pressed={chosen === 'express'}>	<CardHeader heading="Express" description="Next day" />	<CardContent>Delivered by our own drivers.</CardContent></Card>

Header slots

start and end take an avatar, a badge, a menu button — anything. The header of a card about a person, or a record with a status.

Omer Chetin

Omer Chetin

Opened this pull request

Open
Adds the docs site and moves Storybook to /storybook.
<script lang="ts">	import { Avatar, Badge, Card, CardContent, CardHeader, IconButton } from 'omaris';</script>​<Card class="w-full max-w-96">	<CardHeader heading="Omer Chetin" description="Opened this pull request">		{#snippet start()}			<Avatar name="Omer Chetin" />		{/snippet}		{#snippet end()}			<div class="flex items-center gap-2">				<Badge tone="success" variant="tonal">Open</Badge>				<IconButton aria-label="More actions" size="sm">					<svg viewBox="0 0 24 24" fill="currentColor">						<circle cx="12" cy="5" r="1.6" /><circle cx="12" cy="12" r="1.6" /><circle							cx="12"							cy="19"							r="1.6"						/>					</svg>				</IconButton>			</div>		{/snippet}	</CardHeader>	<CardContent>Adds the docs site and moves Storybook to /storybook.</CardContent></Card>

Overridden

A footer defaults to putting its actions at the end, which MD3 asks for. align moves them, and class on the card wins over its own padding.

Storage

8.2 GB of 10 GB used

Older exports are removed automatically after 90 days.
<script lang="ts">	import { Button, Card, CardContent, CardFooter, CardHeader } from 'omaris';</script>​<Card class="w-full max-w-96 rounded-shape-2xl">	<CardHeader heading="Storage" description="8.2 GB of 10 GB used" />	<CardContent>Older exports are removed automatically after 90 days.</CardContent>	<CardFooter align="between">		<Button variant="text" tone="destructive">Delete all</Button>		<Button variant="tonal">Upgrade</Button>	</CardFooter></Card>

When to use it

Use it for

  • One thing with a heading, a body and its actions: an invoice, an order, a project. CardHeader, CardContent, CardFooter. The body grows, so a row of cards with different amounts of content still lines its footers up.
  • A grid of pressable tiles. Give the card href or onclick and it becomes a real target with a ripple and a focus ring, with no wrapper <a>.
  • A picker, like plans or delivery options. interactive and selected, with aria-pressed on each so the choice is read out.
  • An image-led card. CardMedia with a ratio; bleed is on by default so the picture reaches the edges and rounds only the corners it touches.

Not for

  • Rows of the same shape you read down → List. Several columns you compare or sort → Table.
  • Grouping a form's fields → whitespace and a Text heading. A card around every fieldset is a page of boxes.
  • Pictures of unequal height in a wall → Masonry around the cards, not a grid of cards with cropped images.

Do

  • Pick the variant by what is underneath. outlined (the default) reads well on a plain page. A dashboard grid usually wants elevated, so cards read as separate objects without a hairline each. filled is for a card inside another surface, like a sheet or a dialog.
  • Set level on CardHeader to fit the page outline. The default 3 assumes the page has an h2 above it.
  • Leave CardFooter actions at the end. align="between" is only for keeping a destructive action apart from the safe one.
  • Put a menu or a badge in the header's end slot, an avatar or icon in start.

Don't

  • Nest a card in a card. Use CardContent with a Divider or a List inside it.
  • Put a button, a link or a menu inside an interactive card. Make the whole card the target, or none of it.
  • Give a card more than one filled button. One action commits; the rest are text.
  • Add a border class to make an elevated card visible on a busy surface. Switch to outlined instead.

Quick reference

variant
  • elevated
  • filled
  • outlined (default)
size
  • md (default)
  • lg

API

Card

MD3 card — elevated, filled, or outlined.

A plain card is a passive container. Give it href or onclick and it becomes a real target: it picks up the state layer, a ripple, a focus ring, and lifts a step on hover. That's the whole difference, so a grid of clickable cards doesn't need a wrapper <a> around each one.

import { Card } from 'omaris'

Props

variant

Defaults to 'outlined'

CardVariant
elevated
filled
outlined
size

Defaults to 'md'

CardSize
md
lg
interactive
boolean

Force the interactive treatment on a card that handles its own press.

selected

Defaults to false

boolean

Chosen state — border and ring in the primary color.

ripple

Defaults to true

boolean

Ripple on press. Interactive cards only.

class
string
children
Snippet
tone
ButtonTone

Color role the fill style paints with.

shape
ButtonShape

Pill (round) or MD3 rounded rectangle (square).

icon
boolean

Square, label-less button. Requires an aria-label.

confetti
boolean | ConfettiOptions

Confetti on press. true for the default burst, or any ConfettiOptions — confetti={{ preset: 'fireworks' }}.

loading
boolean

Grows a spinner in at the start of the button and blocks interaction. The label stays put; only an icon button swaps its icon out.

toggle
boolean

Turns the button into a two-state toggle driven by pressed. Ignored when href is set — a link has no pressed state.

pressed bindable
boolean

Selected state of a toggle button. Bindable.

CardContent

A card's body — the text and controls between its header and its footer.

It is the part that grows, so a row of cards with different amounts to say still line their footers up.

import { CardContent } from 'omaris'

Props

class
string
children
Snippet

CardFooter

A card's actions, laid out in a row that wraps.

MD3 puts card actions at the end, which is the default here; align moves them for the cases where that reads wrong.

import { CardFooter } from 'omaris'

Props

align

Defaults to 'end'

'start' | 'end' | 'between'

Where the actions sit. MD3 puts card actions at the end.

class
string
children
Snippet

CardHeader

The heading row of a card: a leading slot, a title block, a trailing slot.

The row wraps rather than crushes. An end slot holding a couple of buttons is wider than a phone leaves over once the icon and the title have had their share, and a shrink-0 trailing slot in a row that cannot wrap takes that width from the title instead — which is how a heading ends up reading one word per line down a 60px column. basis on the body is the threshold: while the title can keep 12rem the row stays on one line and nothing moves, and below that the trailing slot drops to a line of its own.

import { CardHeader } from 'omaris'

Parts

Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.

root
No description in the source yet.
start
An avatar or icon before the title.
body
Title and supporting line, stacked.
title
No description in the source yet.
description
No description in the source yet.
end
An overflow menu or badge after the title.

Props

heading
string

Heading text. Use the title snippet instead for rich markup.

description
string

Supporting line under the heading.

level

Defaults to 3

2 | 3 | 4 | 5 | 6

Heading level, so cards nest correctly in a page outline.

class
string
classes
CardHeaderClasses

Per-part Tailwind overrides. class still covers the root.

start
Snippet

Leading slot — an avatar or icon.

end
Snippet

Trailing slot — an overflow menu, a badge.

title
Snippet

Rich heading, in place of heading.

supporting
Snippet

Rich supporting line, in place of description.

CardMedia

The image at the top of a card.

bleed is the one worth knowing: it cancels the card's padding so the picture reaches the edges, and rounds only the corners it actually touches.

import { CardMedia } from 'omaris'

Props

src
string
alt

Defaults to ''

string
ratio

Defaults to '16/9'

'16/9' | '4/3' | '1/1' | '3/2' | 'auto'

Common MD3 media ratios.

bleed

Defaults to true

boolean

Cancels the card's padding so the media reaches the edges, and keeps only the corners it actually touches rounded.

class
string
children
Snippet

Overlaid content — a play button, a gradient caption.