Veto
Components

Card

A surface that groups related content - composed from header, content and footer sections, or driven by plain parameters when that is all you need.

Header, content, footer

The composed form. VetoCardHeader holds a VetoCardTitle and a VetoCardDescription for structured metadata, VetoCardContent holds the body, VetoCardFooter holds the actions. Each section pads itself, so the card needs no padding of its own and the sections never double up.

Deploy to production

Three commits are waiting. The last deploy was four days ago.

Deploying runs the full test suite first. If anything fails the release is held and nothing reaches production.

razor
<VetoCard Padding="VetoSpacing.None">
    <VetoCardHeader>
        <VetoCardTitle>Deploy to production</VetoCardTitle>
        <VetoCardDescription>Three commits are waiting.</VetoCardDescription>
    </VetoCardHeader>

    <VetoCardContent>
        <p>Deploying runs the full test suite first.</p>
    </VetoCardContent>

    <VetoCardFooter Justify="VetoJustify.End">
        <VetoButton Variant="VetoVariant.Text">Cancel</VetoButton>
        <VetoButton Color="VetoColor.Primary">Deploy</VetoButton>
    </VetoCardFooter>
</VetoCard>

Media card

Put an image at the top of the card, before the header. The card clips to its own rounded corners without being asked, so the image's top edge is rounded and its sides run flush - which is what blog posts, product listings and portfolio items all want.

Building a component library

12 September 2026 ยท 8 minute read

What a component contract is for, and why the second component is the one that tells you whether the first one was designed properly.

razor
@* The image goes before the header. The card clips to its own corners already,
   so nothing has to ask for the rounded top. *@
<VetoCard Padding="VetoSpacing.None" Elevation="VetoElevation.Raised">
    <img src="/media/post.jpg" alt="" />

    <VetoCardHeader>
        <VetoCardTitle>Building a component library</VetoCardTitle>
        <VetoCardDescription>12 September 2026 · 8 minute read</VetoCardDescription>
    </VetoCardHeader>

    <VetoCardContent>
        <p>What a component contract is for.</p>
    </VetoCardContent>
</VetoCard>

Statistic tiles

A grid of cards, each reversing the usual order inside the header: description first, then title. That puts the label above the figure, which is the order a KPI reads in. There is no parameter for it - writing the two children the other way round is the whole feature. Level="0" keeps the figures out of the page's heading outline, where twenty of them would do real damage.

Revenue

$48,290

+12.4% on last month

Active users

2,411

+3.1% on last month

Churn

1.8%

-0.4% on last month

P95 latency

184 ms

+22 ms on last month

razor
<VetoGrid Columns="1" ColumnsMedium="2" ColumnsLarge="4" Gap="VetoSpacing.Medium">
    <VetoGridItem>
        <VetoCard Padding="VetoSpacing.None">
            <VetoCardHeader>
                @* Description first: the label sits above the figure. *@
                <VetoCardDescription>Revenue</VetoCardDescription>
                <VetoCardTitle Level="0" Display="true">$48,290</VetoCardTitle>
                <VetoCardDescription>+12.4% on last month</VetoCardDescription>
            </VetoCardHeader>
        </VetoCard>
    </VetoGridItem>
</VetoGrid>

The plain form

When a card is just a padded box with a heading, the parameters are less ceremony than the sections. Title, Header and Footer still work exactly as they did - the composed form is an addition, not a replacement.

Storage

41.2 GB of 100 GB used.

razor
<VetoCard Title="Storage">
    <p>41.2 GB of 100 GB used.</p>
</VetoCard>

Elevation and interactivity

Flat, outlined or raised. Giving a card OnClick makes the whole surface activate - and renders it as a button, so it is focusable, keyboard operable and announced as a control rather than being a div that happens to respond to clicks.

Flat

Sits on the background.

Outlined

A hairline border.

Raised

A soft shadow.
razor
<VetoCard Elevation="VetoElevation.Flat" Title="Flat">Sits on the background.</VetoCard>
<VetoCard Elevation="VetoElevation.Raised" Title="Raised">A soft shadow.</VetoCard>
<VetoCard Title="Clickable" OnClick="Activate">Renders as a button.</VetoCard>

Parameters

Parameter Type Default Description
VetoCardHeader component — The title/description block. Bordered draws a hairline under it.
VetoCardTitle component — The heading. Level sets h1-h6, or 0 for a plain div. Display renders it at figure size.
VetoCardDescription component — The muted supporting line. Before the title for a KPI label, after it for a subtitle.
VetoCardContent component — The body of a composed card.
VetoCardFooter component — The actions. Justify distributes them; Bordered draws a hairline above.
Title / Header / Footer string? / RenderFragment? null The plain form, unchanged.
ChildContent RenderFragment? null The card's content.
Elevation VetoElevation Outlined Flat, Outlined or Raised.
Padding VetoSpacing Large Padding inside each region. Use None with the composed sections, which pad themselves.
OnClick EventCallback<MouseEventArgs> — Makes the whole card activate, and renders it as a button.
Disabled bool false Disables an interactive card.
Something went wrong. Reload ๐Ÿ—™