Card
A raised surface: border, radius, padding and elevation, and nothing else. The one component meant to be built on rather than used as it is.
API
Props
| prop | type | default | |
|---|---|---|---|
as? | ElementType | "div" | the element or component to render |
className? | string | — | merged over the defaults, so it wins |
…rest | props of as | — | typed against the chosen element |
Variables
| token | |
|---|---|
--margo-shadow-card | its elevation, declared for both themes |
Examples
Usage
A Card is a surface and nothing else: border, radius, padding, background, elevation. What goes inside is your markup, laid out however that content needs.
<Card>
<h3>Invoice 0142</h3>
<p>Issued on 4 August</p>
</Card>
<Card className="flex flex-col gap-3">
<h3>Invoice 0143</h3>
<div className="flex flex-wrap gap-2">
<Chip>draft</Chip>
<Chip active>overdue</Chip>
</div>
</Card>Invoice 0142
Issued on 4 August
Invoice 0143
As a control
The focus styles ship with the component — the gradient border on focus, an outline on focus-visible — but a div cannot take focus, so they only appear once the card is something that can. When the whole card is clickable, render it as the thing it actually is instead of attaching a click handler to a box.
<BorderGlow>
<Card as={Link} to="/margo-ui/button" className="block">
<h3>Button</h3>
<p>A card rendered as a link</p>
</Card>
</BorderGlow>
<BorderGlow>
<Card as="button" className="block text-left">
<h3>Chip</h3>
<p>A card rendered as a button</p>
</Card>
</BorderGlow>Media and edge to edge
className is merged over the base classes rather than appended, so an override genuinely replaces what it contradicts: dropping the padding does not need a more specific selector, just p-0. Add overflow-hidden and the content is clipped to the radius.
<Card className="overflow-hidden p-0">
<div className="h-20 bg-linear-to-br from-primary-darken to-primary" />
<div className="p-5">
<h3>Cover</h3>
<p>p-0 plus overflow-hidden</p>
</div>
</Card>
<Card className="p-3">
<p>A tighter padding</p>
</Card>Cover
p-0 plus overflow-hidden
A tighter padding
<Card className="p-0 overflow-hidden">
<img src={cover} alt="" />
</Card>Details
A surface, not a layout
Card has no header slot, no footer slot and no title prop. The reason is that card layouts differ far more than card surfaces do: a component with fixed slots would be right for one screen and in the way on the next, whereas the surface — the thing that makes it read as a card at all — is the same everywhere.
It also keeps the component honest about what it costs. Everything you see in a card is markup you wrote, so there is never a hidden wrapper to fight when the design changes.
Composing with the decorators
Because it is a single element with a radius, Card is the natural child of the glow decorators: the pseudo-elements inherit its border-radius, so the light follows the rounded corners instead of cutting across them.
<BorderGlow>
<Card className="margo-col-span-4">…</Card>
</BorderGlow>On a grid, keep the placement on the Card itself — the decorator clones it rather than wrapping it, so the column utilities belong to the card and nothing is inserted between the grid and its item.