Selection Card

Card-based radio-selection component pair (FodSelectionGroup + FodSelectionCard) for mutually exclusive single-selection using visually prominent cards. Fully keyboard navigable, accessible, and generic over any value type.

Quick Start

Wrap FodSelectionCard components inside a FodSelectionGroup and bind the selected value with two-way binding.

Basic selection

Selected: basic
razor
<FodSelectionGroup @bind-Value="_plan" AriaLabel="Choose a plan">
    <FodSelectionCard Value="@("basic")" IsRecommended="true">
        <Header><strong>Basic</strong></Header>
        <ChildContent>Free — 5 GB storage, up to 3 users.</ChildContent>
    </FodSelectionCard>
    <FodSelectionCard Value="@("pro")">
        <Header><strong>Pro</strong></Header>
        <ChildContent>$9/month — 50 GB storage, unlimited users.</ChildContent>
    </FodSelectionCard>
    <FodSelectionCard Value="@("enterprise")">
        <Header><strong>Enterprise</strong></Header>
        <ChildContent>Custom pricing — unlimited everything.</ChildContent>
    </FodSelectionCard>
</FodSelectionGroup>

@code {
    private string? _plan;
}

Recommended Card

Mark a card with IsRecommended="true" to pre-select it when the group has no initial value.

Middle card is pre-selected on load

Selected: express
razor
<FodSelectionGroup @bind-Value="_shipping">
    <FodSelectionCard Value="@("standard")">
        <Header><strong>Standard</strong></Header>
        <ChildContent>5–7 business days · Free</ChildContent>
    </FodSelectionCard>
    <FodSelectionCard Value="@("express")" IsRecommended="true">
        <Header><strong>Express</strong></Header>
        <ChildContent>2–3 business days · $5.99</ChildContent>
    </FodSelectionCard>
</FodSelectionGroup>

@code {
    private string? _shipping;   // null → "express" will be pre-selected on load
}

Radio Position

The radio indicator can appear at the end (default) or the start of the card header.

End (default)

Start

razor
@* End (default) *@
<FodSelectionCard Value="@("a")" RadioPosition="CardRadioPosition.End">
    <Header><strong>Option A</strong></Header>
    <ChildContent>Radio on the right.</ChildContent>
</FodSelectionCard>

@* Start *@
<FodSelectionCard Value="@("a")" RadioPosition="CardRadioPosition.Start">
    <Header><strong>Option A</strong></Header>
    <ChildContent>Radio on the left.</ChildContent>
</FodSelectionCard>

Info Banner

Use the InfoBanner slot to display a contextual notice above the card grid.

All plans include a 30-day free trial. No credit card required.
razor
<FodSelectionGroup @bind-Value="_plan">
    <InfoBanner>
        <FodIcon Icon="IconName.CircleInfo" Color="IconColor.Brand"/>
        <span>All plans include a 30-day free trial.</span>
    </InfoBanner>
    <FodSelectionCard Value="@("starter")" IsRecommended="true">
        <Header><strong>Starter</strong></Header>
        <ChildContent>Free forever for small teams.</ChildContent>
    </FodSelectionCard>
    <FodSelectionCard Value="@("growth")">
        <Header><strong>Growth</strong></Header>
        <ChildContent>$29/month for growing teams.</ChildContent>
    </FodSelectionCard>
</FodSelectionGroup>

Enum Value Type

FodSelectionGroup is generic — use any value type including enums.

Enum binding

Selected: Low
razor
<FodSelectionGroup @bind-Value="_priority">
    <FodSelectionCard Value="Priority.Low">
        <Header><strong>Low</strong></Header>
        <ChildContent>Processed within 5 business days.</ChildContent>
    </FodSelectionCard>
    <FodSelectionCard Value="Priority.Normal" IsRecommended="true">
        <Header><strong>Normal</strong></Header>
        <ChildContent>Processed within 2 business days.</ChildContent>
    </FodSelectionCard>
    <FodSelectionCard Value="Priority.High">
        <Header><strong>High</strong></Header>
        <ChildContent>Processed within 4 hours.</ChildContent>
    </FodSelectionCard>
</FodSelectionGroup>

@code {
    public enum Priority { Low, Normal, High }
    private Priority _priority;
}

Custom Grid Layout

Override column spans per breakpoint to control how many cards appear per row.

Two columns on desktop (Md=6, Lg=6)

razor
<FodSelectionGroup @bind-Value="_value">
    <FodSelectionCard Value="@("a")" Xs="Span.Col12" Md="Span.Col6" Lg="Span.Col6">
        <Header><strong>Option A</strong></Header>
        <ChildContent>Half-width on desktop.</ChildContent>
    </FodSelectionCard>
    <FodSelectionCard Value="@("b")" Xs="Span.Col12" Md="Span.Col6" Lg="Span.Col6">
        <Header><strong>Option B</strong></Header>
        <ChildContent>Half-width on desktop.</ChildContent>
    </FodSelectionCard>
</FodSelectionGroup>

Required

Set Required="true" to mark the group as required (sets aria-required on the radiogroup).

razor
<FodSelectionGroup @bind-Value="_value" AriaLabel="Required selection" Required="true">
    <FodSelectionCard Value="@("yes")">
        <Header><strong>Yes</strong></Header>
        <ChildContent>I accept the terms.</ChildContent>
    </FodSelectionCard>
    <FodSelectionCard Value="@("no")">
        <Header><strong>No</strong></Header>
        <ChildContent>I do not accept.</ChildContent>
    </FodSelectionCard>
</FodSelectionGroup>

Accessibility and Best Practices

FodSelectionGroup and FodSelectionCard are built for full keyboard and screen reader support.

  • role="radiogroup" is set on FodSelectionGroup and role="radio" on each card.
  • aria-checked="true/false" reflects the selection state on each card.
  • AriaLabel on the group provides an accessible name for the radiogroup.
  • aria-required="true" is set when Required is true.
  • Roving tabindex ensures only one card is in the tab sequence at a time.
  • Tab focuses the selected card (or first/recommended when nothing is selected).
  • Arrow Down / Right moves to the next card and selects it (wraps to first).
  • Arrow Up / Left moves to the previous card and selects it (wraps to last).
  • Enter / Space selects the focused card.

API Reference

FodSelectionGroup and FodSelectionCard parameters.

FodSelectionGroup

No properties defined.

FodSelectionCard

No properties defined.

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please reload the page.