Layout & navigation

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.

Team members
12 open items

Everything inside sits on one raised surface with consistent padding.

<app-card title="Team members" subtitle="12 open items">
    <p class="mb-0">Everything inside sits on one raised surface with consistent padding.</p>
</app-card>

A basic card

At its simplest a card is a padded surface around whatever you put inside it.

Content on a surface
Put content inside; the card supplies the surface and the body padding.

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>
One idea per cardA card should hold one coherent thing — a single summary, a single form, a single list. When a card starts holding several unrelated blocks, split it into several cards.

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.

default · elevated · outlined · muted · accent
variant sets the surface treatment; on the accent variant, tone colours the left stripe.
Default

Flat surface.

Elevated

Subtle shadow.

Outlined

Firmer border.

Muted

Recessed fill.

Compliant

Success stripe.

Overdue

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 scale + capped width
padding = none | sm | md (default) | lg. max-width takes a preset (xs/sm/md/lg) or a px value.
padding=sm

Denser — good for tiles and widgets.

padding=lg

Roomier — good for a focal card.

Confirm your email

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.

A fuller card
header-icon (a Bootstrap Icons name), subtitle, an <app-card-actions> slot and an <app-card-footer> slot round out the card.
Security
Last reviewed 2 days ago

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.

A card with a top image
Put any markup in <app-card-media>; the band clips to the card's rounded top corners.
Onboarding guide
5-minute read

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.

The whole card is the target
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.

A card with no body padding
With body="false" the content renders directly, so a table sits flush to the card edges.
BuildingStatus
Corporate OfficeDraft
Server RoomCompliant
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>
Only drop the body for edge-to-edge contentKeep the default padded body for text and forms. Reach for 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

Do
Billing

Your plan renews on 12 Aug 2026.

One titled card, one coherent subject — the header tells the user what this block is.
Don't

Your plan renews on 12 Aug 2026.

3 new members joined this week.

Server Room assessment is overdue.

Three unrelated facts crammed into one untitled card — none of them stands out.
Do
Corporate OfficeDraft
A table uses body="false" so its rows sit flush to the card edges.
Don't
Corporate OfficeDraft
The default body padding leaves an awkward inset gap around a full-width table.

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"
Default

Card content.

variant="elevated"
Elevated

Card content.

variant="outlined"
Outlined

Card content.

variant="muted"
Muted

Card content.

variant="accent"
Accent

Card content.

tone — accent variant, every tone

variant="accent" tone="primary"
Primary

Stripe colour.

variant="accent" tone="success"
Success

Stripe colour.

variant="accent" tone="warning"
Warning

Stripe colour.

variant="accent" tone="danger"
Danger

Stripe colour.

variant="accent" tone="info"
Info

Stripe colour.

padding — every value

padding="none"
Padding

Body inset at none.

padding="sm"
Padding

Body inset at sm.

padding="md"
Padding

Body inset at md.

padding="lg"
Padding

Body inset at lg.

max-width — every preset, a number, and a string

max-width="xs"
max-width=xs

Capped and centred.

max-width="sm"
max-width=sm

Capped and centred.

max-width="md"
max-width=md

Capped and centred.

max-width="lg"
max-width=lg

Capped and centred.

max-width="480"
480px

A numeric string resolves to px.

max-width="40rem"
40rem

Any other string passes through unchanged.

header composition

title only
Title only

No subtitle, no icon.

subtitle only (no title)
Subtitle only

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
Security
Reviewed 2 days ago

The full title area.

<app-card-actions> only (no title)

Actions right-align even without a title.

title + <app-card-actions> together
Team

Both share one header row.

no header at all

No .card-header is rendered.

React additionally offers a fully custom 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
Invoice #1042

A row below the body.

<app-card-media> only

Media sits above the header.

media + header + footer together
Resource pack

All three bands stack in order.

body — padded vs flush

body (default) — padded
Padded

Standard .card-body inset.

body="false" — flush, edge-to-edge content
Flush
RowValue

lifted

(default) — no hover lift
Static

Hover: no movement.

lifted="true" — 1px hover translate
Lifted

Hover to see the lift.

clickable — href, onClick, and neither

(no href) — plain div, not clickable
Static card

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
A surprisingly long card title that keeps going and going
And an equally long subtitle line that wraps too

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

AttributeTypeDefaultDescription
(content) *markup—The card content. Rendered inside the body unless body is false.
liftedboolfalseAdds the raised/hover treatment (.card-lifted).
variantdefault | elevated | outlined | muted | accentdefaultThe surface treatment.
toneprimary | success | warning | danger | infoprimaryColours the accent variant's left stripe.
paddingnone | sm | md | lgmdHeader/body/footer padding.
max-widthxs | sm | md | lg | px—Caps and centres a standalone card.
titlestring—Card heading (React title); renders a .card-header.
subtitlestring—A muted line under the title.
header-iconstring—A leading header glyph — a Bootstrap Icons name.
hrefstring—Makes the whole card a link (renders as <a>).
bodybooltrueWrap 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).