Card
Use card with the current published Askr packages.
@askrjs/themes/card0.0.13
How to use card
Card should use the standard themed surface unless the application is deliberately implementing its own design system on top of @askrjs/ui.
- Import the themed component from @askrjs/themes/components; use @askrjs/ui directly only when building a custom visual system.
- Keep card labels, state, and application actions visible at the call site instead of styling internal DOM nodes.
- Verify keyboard behavior, focus movement or return, disabled state, and both standard themes.
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@askrjs/themes/components';
<Card>
<CardHeader><CardTitle>Project health</CardTitle><CardDescription>Updated just now</CardDescription></CardHeader>
<CardContent><ProjectHealth /></CardContent>
</Card>Purpose
Grouping related content into a bounded, readable unit is what Card exists for — a stat, a settings section, a list row, anything that should visually register as one thing on the page. Behind it sits nothing but styling: no focus trap, no keyboard routing, no ARIA role to manage, so @askrjs/ui never had to define a headless Card primitive in the first place. What you get is a styled `<div>` plus a family of layout parts — `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`, `CardAction` — that hand you consistent spacing and typography instead of making you rebuild them by hand each time.
Install and import
Card ships from `@askrjs/themes/card`, which also gets pulled in through the broader `@askrjs/themes/components` barrel if you're already importing from there. Pull in `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`, and `CardAction` as needed — you don't have to use every part on every card. There's nothing to install from `@askrjs/ui` for this one; `@askrjs/themes` is the whole story.
Live examples
A minimal card is just `<Card><CardHeader><CardTitle>...</CardTitle></CardHeader><CardContent>...</CardContent></Card>` — three parts covers most real usage. Add `CardDescription` under the title for a subhead, and `CardFooter` for actions or metadata that should sit visually separate from the body. `CardAction` is meant for a control (a button, a menu trigger) placed inline with the header, like an overflow menu next to a title.
Anatomy
The parts map directly to DOM elements: `Card` renders a `div`, `CardTitle` renders an `h3`, and `CardDescription` renders a `p` — the rest (`CardHeader`, `CardContent`, `CardFooter`, `CardAction`) are `div`s. Nesting is flat and predictable: header parts go inside `CardHeader`, and everything else is a direct child of `Card`. There's no required order beyond what makes visual sense — you can omit any part you don't need.
State model
Nothing about a Card is tracked internally by the component itself. `variant`, typed as `CardVariant` (`"default" | "raised"`), is the one prop that changes how it looks, toggling between a flat surface and an elevated one. Conditions like loading, selected, or disabled aren't modeled here at all — you express those yourself through conditional rendering or a wrapping data attribute.
Keyboard and accessibility
Because Card is static markup, there's no keyboard interaction to account for and nothing to manage with focus or ARIA — accessibility here is about what you put inside it, not the container itself. Use `CardTitle`'s heading semantics correctly: keep heading levels consistent with the rest of the page since it always renders as `h3`. If a card as a whole is interactive (say, a clickable card), wrap it in a real button or link rather than adding a click handler to the div.
Styling and tokens
Card follows the same public contract as the rest of the theme package: style the `data-slot` hooks and `--ak-*` tokens, not internal structure. The `raised` variant is driven by the shared shadow tokens, so if you're adjusting elevation across the app, change the token rather than overriding `box-shadow` directly on individual cards. Spacing inside `CardHeader`, `CardContent`, and `CardFooter` comes from the same density tokens used everywhere else in the theme, so cards stay visually consistent with buttons, inputs, and other surfaces without extra work.
API
`CardProps` extends native `div` props (minus `children` and `ref`, which are re-typed) with an optional `variant?: CardVariant` and forwards a `ref` to `HTMLDivElement`. `CardHeaderProps`, `CardContentProps`, and `CardFooterProps` are all plain `div`-prop wrappers with their own typed `ref`; `CardTitleProps` types its `ref` as `HTMLHeadingElement` and `CardDescriptionProps` as `HTMLParagraphElement`. None of these parts take special props beyond `children` and standard native attributes — `variant` is the only thing that isn't just passthrough.
Edge cases
Because every part is just a styled native element, most "edge cases" are really just CSS: long titles will wrap by default rather than truncate, so add your own `text-overflow` handling if you need single-line titles. If a card's content can overflow (a long table or list inside `CardContent`), you're responsible for scroll containment — Card doesn't clip or scroll its children automatically. Empty cards render fine but will look sparse; pair with `EmptyState` from the same package if you need a real empty-content pattern.
Related pages
For grouped clickable rows rather than a single content block, look at Item and ItemGroup, which are built for lists of interactive entries. If you need elevation and layout coordination at a bigger scale — panels, dashboard sections — Block and Container from `@askrjs/themes` compose well as a wrapper around several cards. Table and Data Table are the better fit once the content inside a card starts looking like rows and columns rather than free-form content.