AppCard
Groups related content onto one raised surface — the default container for a section of a page.
<app-card>…</app-card>A card gathers related content onto a single raised surface, setting it apart from the page around it. <app-card> is the default container for a section — a summary, a form, a list — so surfaces never drift into three different ideas of what a panel looks like.
Beyond a plain surface it carries variants (elevated, outlined, muted, a tone-tinted accent stripe), a padding scale, an optional capped width, a header (icon + title + subtitle + actions), a footer and a top media band, and it can be made a single clickable control. Its look comes entirely from the shared @webority/theme, so the Razor <app-card> and the React <AppCard> render identically.
Playground
Every meaningful attribute, live. Each change re-renders the real tag helper on the server, so the markup underneath is the exact call that produced the card above it.
A basic card
At its simplest a card is a padded surface around whatever you put inside it.
Everything inside sits on one raised surface with consistent padding.
Show code
<app-card>
<p class="mb-0">Everything inside sits on one raised surface with consistent padding.</p>
</app-card>
Header & actions
A title names the card; an action — usually an app-button — sits on the same row, right-aligned.
Team members
8 people have access to this project.
Show code
<app-card>
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0">Team members</h5>
<app-button size="sm" left-icon="user-plus">Invite</app-button>
</div>
<p class="mb-0">8 people have access to this project.</p>
</app-card>
Prefer the title attribute plus a nested <app-card-actions> child over composing the header row by hand — it matches React's headerActions prop and keeps the header row's spacing and alignment out of consumer markup.
8 people have access to this project.
Show code
<app-card title="Team">
<app-card-actions>
<app-button variant="ghost" size="sm">Manage</app-button>
</app-card-actions>
<p class="mb-0">8 people have access to this project.</p>
</app-card>
Variants
One flat default, plus four opt-in treatments. Pick by how much the card should stand off the page — elevated to pop, outlined for a firmer edge, muted to recede, accent to flag a status.
variant sets the surface treatment; on the accent variant, tone colours the left stripe.Flat surface.
Subtle shadow.
Firmer border.
Recessed fill.
Success stripe.
Danger stripe.
Show code
<app-card variant="elevated" title="Elevated">…</app-card> <app-card variant="outlined" title="Outlined">…</app-card> <app-card variant="muted" title="Muted">…</app-card> <app-card variant="accent" tone="success" title="Compliant">…</app-card>
Padding & width
padding tunes the inner spacing across header, body and footer; max-width caps and centres a standalone card so it doesn't stretch edge-to-edge on a wide page.
padding = none | sm | md (default) | lg. max-width takes a preset (xs/sm/md/lg) or a px value.Denser — good for tiles and widgets.
Roomier — good for a focal card.
Capped at a comfortable reading width and centred.
Show code
<app-card padding="sm" title="Compact">…</app-card> <app-card padding="lg" title="Roomy">…</app-card> <app-card max-width="sm" title="Settings">…</app-card>
Subtitle, icon & footer
The header can carry a leading icon and a subtitle under the title; a footer adds a row below the body for meta or actions.
header-icon (a Bootstrap Icons name), subtitle, an <app-card-actions> slot and an <app-card-footer> slot round out the card.Two-factor authentication is enabled for all admins.
Show code
<app-card header-icon="shield-check" title="Security" subtitle="Last reviewed 2 days ago">
<app-card-actions><app-button size="sm" variant="ghost">Review</app-button></app-card-actions>
<p class="mb-0">Two-factor authentication is enabled for all admins.</p>
<app-card-footer><span class="text-body-secondary small">Updated by Ravi</span></app-card-footer>
</app-card>
Media
An <app-card-media> slot renders an image or video band flush across the top of the card, above the header — the image-card pattern for resources, previews and thumbnails.
<app-card-media>; the band clips to the card's rounded top corners.Get your team set up in a few steps.
Show code
<app-card title="Onboarding guide">
<app-card-media><img alt="Cover" src="/cover.jpg" /></app-card-media>
<p class="mb-0">A 5-minute read to get your team set up.</p>
</app-card>
Clickable card
Give the card an href and the whole surface becomes one link, with hover and focus-visible for free. Don't nest other links or buttons inside a clickable card.
href renders the card as an <a>. (React additionally offers onClick for a button card.)Show code
<app-card href="/projects/acme" variant="outlined" title="Acme Corp" subtitle="12 open items">
<p class="mb-0">Open the project →</p>
</app-card>
Bodyless cards
Set body="false" when the content brings its own edge-to-edge layout — a table or a list that should touch the card's sides.
| Building | Status |
|---|---|
| Corporate Office | Draft |
| Server Room | Compliant |
Show code
<app-card body="false">
<table class="table mb-0">
<thead><tr><th>Building</th><th>Status</th></tr></thead>
<tbody>
<tr><td>Corporate Office</td><td>Draft</td></tr>
<tr><td>Server Room</td><td>Compliant</td></tr>
</tbody>
</table>
</app-card>
body="false" only when the child owns its own layout — a table, an image, a list — and should meet the card's edges.When to use
Use it when
- A page section needs to be visually grouped and set apart from the rest.
- You are laying out a dashboard of summaries, each on its own surface.
- A form or list should read as one self-contained block.
Reach for something else when
- You need a masthead with a page title and actions. → AppPageHeader
- The content is a transient message, not a lasting section. → AppAlert
- You are showing an empty section with a call to action. → AppEmptyState
- Everything on the page is already one surface — nesting adds noise. → plain layout
Best practices
Billing
Your plan renews on 12 Aug 2026.
Your plan renews on 12 Aug 2026.
3 new members joined this week.
Server Room assessment is overdue.
| Corporate Office | Draft |
| Corporate Office | Draft |
Every option
The whole surface of the component, one cell per value — every variant and tone, the padding and width scale, every header composition, the bodyless/lifted/clickable shapes, and media + footer together.
variant — all five
variant="default"
Card content.
variant="elevated"
Card content.
variant="outlined"
Card content.
variant="muted"
Card content.
variant="accent"
Card content.
tone — accent variant, every tone
variant="accent" tone="primary"
Stripe colour.
variant="accent" tone="success"
Stripe colour.
variant="accent" tone="warning"
Stripe colour.
variant="accent" tone="danger"
Stripe colour.
variant="accent" tone="info"
Stripe colour.
padding — every value
padding="none"
Body inset at none.
padding="sm"
Body inset at sm.
padding="md"
Body inset at md.
padding="lg"
Body inset at lg.
max-width — every preset, a number, and a string
max-width="xs"
Capped and centred.
max-width="sm"
Capped and centred.
max-width="md"
Capped and centred.
max-width="lg"
Capped and centred.
max-width="480"
A numeric string resolves to px.
max-width="40rem"
Any other string passes through unchanged.
header composition
title only
No subtitle, no icon.
subtitle only (no title)
A header still opens for a bare subtitle.
header-icon only (no title/subtitle)
An icon alone still opens a header.
header-icon + title + subtitle
The full title area.
<app-card-actions> only (no title)
Actions right-align even without a title.
title + <app-card-actions> together
Both share one header row.
no header at all
No .card-header is rendered.
header render prop (overrides title/subtitle/icon/actions) and a headerExtra slot beside the title — Razor has no equivalent tag helper for either; compose the header row by hand inside <app-card> instead (see "Header & actions" above).footer & media
<app-card-footer> only
A row below the body.
<app-card-media> only
Media sits above the header.
media + header + footer together
All three bands stack in order.
body — padded vs flush
body (default) — padded
Standard .card-body inset.
body="false" — flush, edge-to-edge content
| Row | Value |
lifted
(default) — no hover lift
Hover: no movement.
lifted="true" — 1px hover translate
Hover to see the lift.
clickable — href, onClick, and neither
(no href) — plain div, not clickable
No role, no hover.
href — renders <a class="card-clickable">
onClick — React-only (renders role="button"); Razor has no client click state, use href
overflow — long content in a capped-width card
max-width="xs" with a long unbroken title and long body text
Long-form body copy inside a narrow, capped card confirms the text wraps instead of overflowing the fixed width, and that the header titles wrap alongside it.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| (content) * | markup | — | The card content. Rendered inside the body unless body is false. |
| lifted | bool | false | Adds the raised/hover treatment (.card-lifted). |
| variant | default | elevated | outlined | muted | accent | default | The surface treatment. |
| tone | primary | success | warning | danger | info | primary | Colours the accent variant's left stripe. |
| padding | none | sm | md | lg | md | Header/body/footer padding. |
| max-width | xs | sm | md | lg | px | — | Caps and centres a standalone card. |
| title | string | — | Card heading (React title); renders a .card-header. |
| subtitle | string | — | A muted line under the title. |
| header-icon | string | — | A leading header glyph — a Bootstrap Icons name. |
| href | string | — | Makes the whole card a link (renders as <a>). |
| body | bool | true | Wrap the content in .card-body. Set false for edge-to-edge content that owns its own padding — the same meaning as React's body. |
| <app-card-actions> | markup | — | Child slot rendering its content right-aligned in the header row — the Razor equivalent of React's headerActions prop. |
| <app-card-footer> | markup | — | Child slot rendering a footer row below the body (React footer). |
| <app-card-media> | markup | — | Child slot rendering a media band above the header (React media). |