Skip to content

SelectionCardGroup ​

Experimental Alpha

This library is a work-in-progress. We are releasing it early to gather feedback, but it is not ready for production.

A card-based selection UI for single (radio) or multiple (checkbox) selection. Ideal for onboarding flows, preferences, pricing tiers, and feature opt-ins.

Examples ​

Vue
Lit
React
Live Preview

Radio Selection (Single)

Select one option from a group of cards

πŸ’»
Technology
🎨
Art & Design
🎡
Music

Checkbox Selection (Multiple)

Select multiple options from a group of cards

πŸ“Š
Analytics
Track user behavior
πŸ””
Notifications
Push & email alerts
πŸ“€
Export
Download your data

Theme Variants

Different color themes for various contexts

Default (Primary/Blue)

Option A
Option B

Success

Option A
Option B

Warning

Option A
Option B

Error

Option A
Option B

Monochrome

Option A
Option B

Disabled State

Entire group can be disabled

Option A
Option B

Pricing Tiers Example

Common use case for plan selection

Free
$0
Perfect for getting started
Pro
$29
For growing teams
Premium
Custom
For large organizations
View Vue Code
<template>
  <section>
    <div class="mbe4">
      <h2>Radio Selection (Single)</h2>
      <p class="mbs2 mbe3">Select one option from a group of cards</p>
    </div>
    <VueSelectionCardGroup
      type="radio"
      name="interests"
      legend="Select your primary interest"
      class="mbe4"
      @selection-change="handleChange"
    >
      <VueSelectionCard value="tech" label="Technology">
        <div style="text-align: center; padding: 1rem;">
          <div style="font-size: 2rem;">πŸ’»</div>
          <div style="font-weight: 600;">Technology</div>
        </div>
      </VueSelectionCard>
      <VueSelectionCard value="art" label="Art & Design">
        <div style="text-align: center; padding: 1rem;">
          <div style="font-size: 2rem;">🎨</div>
          <div style="font-weight: 600;">Art & Design</div>
        </div>
      </VueSelectionCard>
      <VueSelectionCard value="music" label="Music">
        <div style="text-align: center; padding: 1rem;">
          <div style="font-size: 2rem;">🎡</div>
          <div style="font-weight: 600;">Music</div>
        </div>
      </VueSelectionCard>
    </VueSelectionCardGroup>

    <div class="mbe4">
      <h2>Checkbox Selection (Multiple)</h2>
      <p class="mbs2 mbe3">Select multiple options from a group of cards</p>
    </div>
    <VueSelectionCardGroup
      type="checkbox"
      name="features"
      legend="Select features to enable"
      class="mbe4"
      @selection-change="handleChange"
    >
      <VueSelectionCard value="analytics" label="Analytics">
        <div style="text-align: center; padding: 1rem;">
          <div style="font-size: 2rem;">πŸ“Š</div>
          <div style="font-weight: 600;">Analytics</div>
          <div style="font-size: 0.875rem; color: #666;">Track user behavior</div>
        </div>
      </VueSelectionCard>
      <VueSelectionCard value="notifications" label="Notifications">
        <div style="text-align: center; padding: 1rem;">
          <div style="font-size: 2rem;">πŸ””</div>
          <div style="font-weight: 600;">Notifications</div>
          <div style="font-size: 0.875rem; color: #666;">Push & email alerts</div>
        </div>
      </VueSelectionCard>
      <VueSelectionCard value="export" label="Export">
        <div style="text-align: center; padding: 1rem;">
          <div style="font-size: 2rem;">πŸ“€</div>
          <div style="font-weight: 600;">Export</div>
          <div style="font-size: 0.875rem; color: #666;">Download your data</div>
        </div>
      </VueSelectionCard>
    </VueSelectionCardGroup>

    <div class="mbe4">
      <h2>Theme Variants</h2>
      <p class="mbs2 mbe3">Different color themes for various contexts</p>
    </div>
    <div style="display: flex; flex-direction: column; gap: 1.5rem;" class="mbe4">
      <div>
        <h3 style="margin-bottom: 0.5rem; font-size: 0.875rem;">Default (Primary/Blue)</h3>
        <VueSelectionCardGroup
          type="radio"
          name="theme-default"
          legend="Default theme"
          legend-hidden
        >
          <VueSelectionCard value="a" label="Option A">
            <div style="padding: 1rem; text-align: center;">Option A</div>
          </VueSelectionCard>
          <VueSelectionCard value="b" label="Option B">
            <div style="padding: 1rem; text-align: center;">Option B</div>
          </VueSelectionCard>
        </VueSelectionCardGroup>
      </div>
      <div>
        <h3 style="margin-bottom: 0.5rem; font-size: 0.875rem;">Success</h3>
        <VueSelectionCardGroup
          type="radio"
          name="theme-success"
          legend="Success theme"
          legend-hidden
          theme="success"
        >
          <VueSelectionCard value="a" label="Option A">
            <div style="padding: 1rem; text-align: center;">Option A</div>
          </VueSelectionCard>
          <VueSelectionCard value="b" label="Option B">
            <div style="padding: 1rem; text-align: center;">Option B</div>
          </VueSelectionCard>
        </VueSelectionCardGroup>
      </div>
      <div>
        <h3 style="margin-bottom: 0.5rem; font-size: 0.875rem;">Warning</h3>
        <VueSelectionCardGroup
          type="radio"
          name="theme-warning"
          legend="Warning theme"
          legend-hidden
          theme="warning"
        >
          <VueSelectionCard value="a" label="Option A">
            <div style="padding: 1rem; text-align: center;">Option A</div>
          </VueSelectionCard>
          <VueSelectionCard value="b" label="Option B">
            <div style="padding: 1rem; text-align: center;">Option B</div>
          </VueSelectionCard>
        </VueSelectionCardGroup>
      </div>
      <div>
        <h3 style="margin-bottom: 0.5rem; font-size: 0.875rem;">Error</h3>
        <VueSelectionCardGroup
          type="radio"
          name="theme-error"
          legend="Error theme"
          legend-hidden
          theme="error"
        >
          <VueSelectionCard value="a" label="Option A">
            <div style="padding: 1rem; text-align: center;">Option A</div>
          </VueSelectionCard>
          <VueSelectionCard value="b" label="Option B">
            <div style="padding: 1rem; text-align: center;">Option B</div>
          </VueSelectionCard>
        </VueSelectionCardGroup>
      </div>
      <div>
        <h3 style="margin-bottom: 0.5rem; font-size: 0.875rem;">Monochrome</h3>
        <VueSelectionCardGroup
          type="radio"
          name="theme-mono"
          legend="Monochrome theme"
          legend-hidden
          theme="monochrome"
        >
          <VueSelectionCard value="a" label="Option A">
            <div style="padding: 1rem; text-align: center;">Option A</div>
          </VueSelectionCard>
          <VueSelectionCard value="b" label="Option B">
            <div style="padding: 1rem; text-align: center;">Option B</div>
          </VueSelectionCard>
        </VueSelectionCardGroup>
      </div>
    </div>

    <div class="mbe4">
      <h2>Disabled State</h2>
      <p class="mbs2 mbe3">Entire group can be disabled</p>
    </div>
    <VueSelectionCardGroup
      type="radio"
      name="disabled-example"
      legend="Disabled group"
      :disabled="true"
      class="mbe4"
    >
      <VueSelectionCard value="a" label="Option A">
        <div style="padding: 1rem; text-align: center;">Option A</div>
      </VueSelectionCard>
      <VueSelectionCard value="b" label="Option B">
        <div style="padding: 1rem; text-align: center;">Option B</div>
      </VueSelectionCard>
    </VueSelectionCardGroup>

    <div class="mbe4">
      <h2>Pricing Tiers Example</h2>
      <p class="mbs2 mbe3">Common use case for plan selection</p>
    </div>
    <VueSelectionCardGroup
      type="radio"
      name="pricing"
      legend="Choose your plan"
      class="mbe4"
      @selection-change="handleChange"
    >
      <VueSelectionCard value="free" label="Free Plan">
        <div style="padding: 1rem; text-align: center;">
          <div style="font-size: 1.5rem; font-weight: 700;">Free</div>
          <div style="font-size: 2rem; font-weight: 700; margin: 0.5rem 0;">$0</div>
          <div style="color: #666; font-size: 0.875rem;">Perfect for getting started</div>
        </div>
      </VueSelectionCard>
      <VueSelectionCard value="pro" label="Pro Plan">
        <div style="padding: 1rem; text-align: center;">
          <div style="font-size: 1.5rem; font-weight: 700;">Pro</div>
          <div style="font-size: 2rem; font-weight: 700; margin: 0.5rem 0;">$29</div>
          <div style="color: #666; font-size: 0.875rem;">For growing teams</div>
        </div>
      </VueSelectionCard>
      <VueSelectionCard value="premium" label="Premium Plan">
        <div style="padding: 1rem; text-align: center;">
          <div style="font-size: 1.5rem; font-weight: 700;">Premium</div>
          <div style="font-size: 1.5rem; font-weight: 700; margin: 0.5rem 0;">Custom</div>
          <div style="color: #666; font-size: 0.875rem;">For large organizations</div>
        </div>
      </VueSelectionCard>
    </VueSelectionCardGroup>
  </section>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import { VueSelectionCardGroup } from "agnosticui-core/selection-card-group/vue";
import { VueSelectionCard } from "agnosticui-core/selection-card/vue";

export default defineComponent({
  name: "SelectionCardGroupExamples",
  components: {
    VueSelectionCardGroup,
    VueSelectionCard,
  },
  methods: {
    handleChange(detail: { value: string; checked: boolean; selectedValues: string[] }) {
      console.log("Selection changed:", detail);
    },
  },
});
</script>

<style>
/* Responsive: stack cards on mobile */
@media (max-width: 640px) {
  ag-selection-card-group::part(ag-selection-card-group-content) {
    grid-template-columns: 1fr;
  }
}
</style>
Live Preview
View Lit / Web Component Code
import { LitElement, html } from 'lit';
import 'agnosticui-core/selection-card-group';
import 'agnosticui-core/selection-card';

export class SelectionCardGroupLitExamples extends LitElement {
  // Render in light DOM to access global utility classes
  createRenderRoot() {
    return this;
  }

  handleChange(e) {
    console.log('Selection changed:', e.detail);
  }

  render() {
    return html`
      <section>
        <!-- Radio Selection -->
        <div class="mbe4">
          <h2>Radio Selection (Single)</h2>
          <p class="mbs2 mbe3">Select one option from a group of cards</p>
        </div>
        <ag-selection-card-group
          type="radio"
          name="interests"
          legend="Select your primary interest"
          class="mbe4"
          @selection-change=${this.handleChange}
        >
          <ag-selection-card value="tech" label="Technology">
            <div style="text-align: center; padding: 1rem;">
              <div style="font-size: 2rem;">πŸ’»</div>
              <div style="font-weight: 600;">Technology</div>
            </div>
          </ag-selection-card>
          <ag-selection-card value="art" label="Art & Design">
            <div style="text-align: center; padding: 1rem;">
              <div style="font-size: 2rem;">🎨</div>
              <div style="font-weight: 600;">Art & Design</div>
            </div>
          </ag-selection-card>
          <ag-selection-card value="music" label="Music">
            <div style="text-align: center; padding: 1rem;">
              <div style="font-size: 2rem;">🎡</div>
              <div style="font-weight: 600;">Music</div>
            </div>
          </ag-selection-card>
        </ag-selection-card-group>

        <!-- Checkbox Selection -->
        <div class="mbe4">
          <h2>Checkbox Selection (Multiple)</h2>
          <p class="mbs2 mbe3">Select multiple options from a group of cards</p>
        </div>
        <ag-selection-card-group
          type="checkbox"
          name="features"
          legend="Select features to enable"
          class="mbe4"
          @selection-change=${this.handleChange}
        >
          <ag-selection-card value="analytics" label="Analytics">
            <div style="text-align: center; padding: 1rem;">
              <div style="font-size: 2rem;">πŸ“Š</div>
              <div style="font-weight: 600;">Analytics</div>
              <div style="font-size: 0.875rem; color: #666;">Track user behavior</div>
            </div>
          </ag-selection-card>
          <ag-selection-card value="notifications" label="Notifications">
            <div style="text-align: center; padding: 1rem;">
              <div style="font-size: 2rem;">πŸ””</div>
              <div style="font-weight: 600;">Notifications</div>
              <div style="font-size: 0.875rem; color: #666;">Push & email alerts</div>
            </div>
          </ag-selection-card>
          <ag-selection-card value="export" label="Export">
            <div style="text-align: center; padding: 1rem;">
              <div style="font-size: 2rem;">πŸ“€</div>
              <div style="font-weight: 600;">Export</div>
              <div style="font-size: 0.875rem; color: #666;">Download your data</div>
            </div>
          </ag-selection-card>
        </ag-selection-card-group>

        <!-- Theme Variants -->
        <div class="mbe4">
          <h2>Theme Variants</h2>
          <p class="mbs2 mbe3">Different color themes for various contexts</p>
        </div>
        <div style="display: flex; flex-direction: column; gap: 1.5rem;" class="mbe4">
          <div>
            <h3 style="margin-bottom: 0.5rem; font-size: 0.875rem;">Default (Primary/Blue)</h3>
            <ag-selection-card-group
              type="radio"
              name="theme-default"
              legend="Default theme"
              legend-hidden
            >
              <ag-selection-card value="a" label="Option A">
                <div style="padding: 1rem; text-align: center;">Option A</div>
              </ag-selection-card>
              <ag-selection-card value="b" label="Option B">
                <div style="padding: 1rem; text-align: center;">Option B</div>
              </ag-selection-card>
            </ag-selection-card-group>
          </div>
          <div>
            <h3 style="margin-bottom: 0.5rem; font-size: 0.875rem;">Success</h3>
            <ag-selection-card-group
              type="radio"
              name="theme-success"
              legend="Success theme"
              legend-hidden
              theme="success"
            >
              <ag-selection-card value="a" label="Option A">
                <div style="padding: 1rem; text-align: center;">Option A</div>
              </ag-selection-card>
              <ag-selection-card value="b" label="Option B">
                <div style="padding: 1rem; text-align: center;">Option B</div>
              </ag-selection-card>
            </ag-selection-card-group>
          </div>
          <div>
            <h3 style="margin-bottom: 0.5rem; font-size: 0.875rem;">Warning</h3>
            <ag-selection-card-group
              type="radio"
              name="theme-warning"
              legend="Warning theme"
              legend-hidden
              theme="warning"
            >
              <ag-selection-card value="a" label="Option A">
                <div style="padding: 1rem; text-align: center;">Option A</div>
              </ag-selection-card>
              <ag-selection-card value="b" label="Option B">
                <div style="padding: 1rem; text-align: center;">Option B</div>
              </ag-selection-card>
            </ag-selection-card-group>
          </div>
          <div>
            <h3 style="margin-bottom: 0.5rem; font-size: 0.875rem;">Error</h3>
            <ag-selection-card-group
              type="radio"
              name="theme-error"
              legend="Error theme"
              legend-hidden
              theme="error"
            >
              <ag-selection-card value="a" label="Option A">
                <div style="padding: 1rem; text-align: center;">Option A</div>
              </ag-selection-card>
              <ag-selection-card value="b" label="Option B">
                <div style="padding: 1rem; text-align: center;">Option B</div>
              </ag-selection-card>
            </ag-selection-card-group>
          </div>
          <div>
            <h3 style="margin-bottom: 0.5rem; font-size: 0.875rem;">Monochrome</h3>
            <ag-selection-card-group
              type="radio"
              name="theme-mono"
              legend="Monochrome theme"
              legend-hidden
              theme="monochrome"
            >
              <ag-selection-card value="a" label="Option A">
                <div style="padding: 1rem; text-align: center;">Option A</div>
              </ag-selection-card>
              <ag-selection-card value="b" label="Option B">
                <div style="padding: 1rem; text-align: center;">Option B</div>
              </ag-selection-card>
            </ag-selection-card-group>
          </div>
        </div>

        <!-- Disabled State -->
        <div class="mbe4">
          <h2>Disabled State</h2>
          <p class="mbs2 mbe3">Entire group can be disabled</p>
        </div>
        <ag-selection-card-group
          type="radio"
          name="disabled-example"
          legend="Disabled group"
          disabled
          class="mbe4"
        >
          <ag-selection-card value="a" label="Option A">
            <div style="padding: 1rem; text-align: center;">Option A</div>
          </ag-selection-card>
          <ag-selection-card value="b" label="Option B">
            <div style="padding: 1rem; text-align: center;">Option B</div>
          </ag-selection-card>
        </ag-selection-card-group>

        <!-- Pricing Tiers Example -->
        <div class="mbe4">
          <h2>Pricing Tiers Example</h2>
          <p class="mbs2 mbe3">Common use case for plan selection</p>
        </div>
        <ag-selection-card-group
          type="radio"
          name="pricing"
          legend="Choose your plan"
          class="mbe4"
          @selection-change=${this.handleChange}
        >
          <ag-selection-card value="free" label="Free Plan">
            <div style="padding: 1rem; text-align: center;">
              <div style="font-size: 1.5rem; font-weight: 700;">Free</div>
              <div style="font-size: 2rem; font-weight: 700; margin: 0.5rem 0;">$0</div>
              <div style="color: #666; font-size: 0.875rem;">Perfect for getting started</div>
            </div>
          </ag-selection-card>
          <ag-selection-card value="pro" label="Pro Plan">
            <div style="padding: 1rem; text-align: center;">
              <div style="font-size: 1.5rem; font-weight: 700;">Pro</div>
              <div style="font-size: 2rem; font-weight: 700; margin: 0.5rem 0;">$29</div>
              <div style="color: #666; font-size: 0.875rem;">For growing teams</div>
            </div>
          </ag-selection-card>
          <ag-selection-card value="premium" label="Premium Plan">
            <div style="padding: 1rem; text-align: center;">
              <div style="font-size: 1.5rem; font-weight: 700;">Premium</div>
              <div style="font-size: 1.5rem; font-weight: 700; margin: 0.5rem 0;">Custom</div>
              <div style="color: #666; font-size: 0.875rem;">For large organizations</div>
            </div>
          </ag-selection-card>
        </ag-selection-card-group>
      </section>
    `;
  }
}

// Register the custom element
customElements.define('selection-card-group-lit-examples', SelectionCardGroupLitExamples);

// Add responsive styles
const style = document.createElement('style');
style.textContent = `
  @media (max-width: 640px) {
    ag-selection-card-group::part(ag-selection-card-group-content) {
      grid-template-columns: 1fr;
    }
  }
`;
document.head.appendChild(style);

Interactive Preview: Click the "Open in StackBlitz" button below to see this example running live in an interactive playground.

View React Code
import { ReactSelectionCardGroup } from "agnosticui-core/selection-card-group/react";
import { ReactSelectionCard } from "agnosticui-core/selection-card/react";

export default function SelectionCardGroupExamples() {
  const handleChange = (e) => {
    console.log("Selection changed:", e.detail);
  };

  return (
    <section>
      {/* Radio Selection */}
      <div className="mbe4">
        <h2>Radio Selection (Single)</h2>
        <p className="mbs2 mbe3">Select one option from a group of cards</p>
      </div>
      <ReactSelectionCardGroup
        type="radio"
        name="interests"
        legend="Select your primary interest"
        className="mbe4"
        onSelectionChange={handleChange}
      >
        <ReactSelectionCard value="tech" label="Technology">
          <div style={{ textAlign: "center", padding: "1rem" }}>
            <div style={{ fontSize: "2rem" }}>πŸ’»</div>
            <div style={{ fontWeight: 600 }}>Technology</div>
          </div>
        </ReactSelectionCard>
        <ReactSelectionCard value="art" label="Art & Design">
          <div style={{ textAlign: "center", padding: "1rem" }}>
            <div style={{ fontSize: "2rem" }}>🎨</div>
            <div style={{ fontWeight: 600 }}>Art & Design</div>
          </div>
        </ReactSelectionCard>
        <ReactSelectionCard value="music" label="Music">
          <div style={{ textAlign: "center", padding: "1rem" }}>
            <div style={{ fontSize: "2rem" }}>🎡</div>
            <div style={{ fontWeight: 600 }}>Music</div>
          </div>
        </ReactSelectionCard>
      </ReactSelectionCardGroup>

      {/* Checkbox Selection */}
      <div className="mbe4">
        <h2>Checkbox Selection (Multiple)</h2>
        <p className="mbs2 mbe3">Select multiple options from a group of cards</p>
      </div>
      <ReactSelectionCardGroup
        type="checkbox"
        name="features"
        legend="Select features to enable"
        className="mbe4"
        onSelectionChange={handleChange}
      >
        <ReactSelectionCard value="analytics" label="Analytics">
          <div style={{ textAlign: "center", padding: "1rem" }}>
            <div style={{ fontSize: "2rem" }}>πŸ“Š</div>
            <div style={{ fontWeight: 600 }}>Analytics</div>
            <div style={{ fontSize: "0.875rem", color: "#666" }}>Track user behavior</div>
          </div>
        </ReactSelectionCard>
        <ReactSelectionCard value="notifications" label="Notifications">
          <div style={{ textAlign: "center", padding: "1rem" }}>
            <div style={{ fontSize: "2rem" }}>πŸ””</div>
            <div style={{ fontWeight: 600 }}>Notifications</div>
            <div style={{ fontSize: "0.875rem", color: "#666" }}>Push & email alerts</div>
          </div>
        </ReactSelectionCard>
        <ReactSelectionCard value="export" label="Export">
          <div style={{ textAlign: "center", padding: "1rem" }}>
            <div style={{ fontSize: "2rem" }}>πŸ“€</div>
            <div style={{ fontWeight: 600 }}>Export</div>
            <div style={{ fontSize: "0.875rem", color: "#666" }}>Download your data</div>
          </div>
        </ReactSelectionCard>
      </ReactSelectionCardGroup>

      {/* Theme Variants */}
      <div className="mbe4">
        <h2>Theme Variants</h2>
        <p className="mbs2 mbe3">Different color themes for various contexts</p>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: "1.5rem" }} className="mbe4">
        <div>
          <h3 style={{ marginBottom: "0.5rem", fontSize: "0.875rem" }}>Default (Primary/Blue)</h3>
          <ReactSelectionCardGroup
            type="radio"
            name="theme-default"
            legend="Default theme"
            legendHidden
          >
            <ReactSelectionCard value="a" label="Option A">
              <div style={{ padding: "1rem", textAlign: "center" }}>Option A</div>
            </ReactSelectionCard>
            <ReactSelectionCard value="b" label="Option B">
              <div style={{ padding: "1rem", textAlign: "center" }}>Option B</div>
            </ReactSelectionCard>
          </ReactSelectionCardGroup>
        </div>
        <div>
          <h3 style={{ marginBottom: "0.5rem", fontSize: "0.875rem" }}>Success</h3>
          <ReactSelectionCardGroup
            type="radio"
            name="theme-success"
            legend="Success theme"
            legendHidden
            theme="success"
          >
            <ReactSelectionCard value="a" label="Option A">
              <div style={{ padding: "1rem", textAlign: "center" }}>Option A</div>
            </ReactSelectionCard>
            <ReactSelectionCard value="b" label="Option B">
              <div style={{ padding: "1rem", textAlign: "center" }}>Option B</div>
            </ReactSelectionCard>
          </ReactSelectionCardGroup>
        </div>
        <div>
          <h3 style={{ marginBottom: "0.5rem", fontSize: "0.875rem" }}>Monochrome</h3>
          <ReactSelectionCardGroup
            type="radio"
            name="theme-mono"
            legend="Monochrome theme"
            legendHidden
            theme="monochrome"
          >
            <ReactSelectionCard value="a" label="Option A">
              <div style={{ padding: "1rem", textAlign: "center" }}>Option A</div>
            </ReactSelectionCard>
            <ReactSelectionCard value="b" label="Option B">
              <div style={{ padding: "1rem", textAlign: "center" }}>Option B</div>
            </ReactSelectionCard>
          </ReactSelectionCardGroup>
        </div>
      </div>

      {/* Disabled State */}
      <div className="mbe4">
        <h2>Disabled State</h2>
        <p className="mbs2 mbe3">Entire group can be disabled</p>
      </div>
      <ReactSelectionCardGroup
        type="radio"
        name="disabled-example"
        legend="Disabled group"
        disabled
        className="mbe4"
      >
        <ReactSelectionCard value="a" label="Option A">
          <div style={{ padding: "1rem", textAlign: "center" }}>Option A</div>
        </ReactSelectionCard>
        <ReactSelectionCard value="b" label="Option B">
          <div style={{ padding: "1rem", textAlign: "center" }}>Option B</div>
        </ReactSelectionCard>
      </ReactSelectionCardGroup>

      {/* Pricing Tiers Example */}
      <div className="mbe4">
        <h2>Pricing Tiers Example</h2>
        <p className="mbs2 mbe3">Common use case for plan selection</p>
      </div>
      <ReactSelectionCardGroup
        type="radio"
        name="pricing"
        legend="Choose your plan"
        className="mbe4"
        onSelectionChange={handleChange}
      >
        <ReactSelectionCard value="free" label="Free Plan">
          <div style={{ padding: "1rem", textAlign: "center" }}>
            <div style={{ fontSize: "1.5rem", fontWeight: 700 }}>Free</div>
            <div style={{ fontSize: "2rem", fontWeight: 700, margin: "0.5rem 0" }}>$0</div>
            <div style={{ color: "#666", fontSize: "0.875rem" }}>Perfect for getting started</div>
          </div>
        </ReactSelectionCard>
        <ReactSelectionCard value="pro" label="Pro Plan">
          <div style={{ padding: "1rem", textAlign: "center" }}>
            <div style={{ fontSize: "1.5rem", fontWeight: 700 }}>Pro</div>
            <div style={{ fontSize: "2rem", fontWeight: 700, margin: "0.5rem 0" }}>$29</div>
            <div style={{ color: "#666", fontSize: "0.875rem" }}>For growing teams</div>
          </div>
        </ReactSelectionCard>
        <ReactSelectionCard value="premium" label="Premium Plan">
          <div style={{ padding: "1rem", textAlign: "center" }}>
            <div style={{ fontSize: "1.5rem", fontWeight: 700 }}>Premium</div>
            <div style={{ fontSize: "1.5rem", fontWeight: 700, margin: "0.5rem 0" }}>Custom</div>
            <div style={{ color: "#666", fontSize: "0.875rem" }}>For large organizations</div>
          </div>
        </ReactSelectionCard>
      </ReactSelectionCardGroup>
    </section>
  );
}
Open in StackBlitz

Usage ​

TIP

The framework examples below import AgnosticUI as an npm package. Alternatively, you can use the CLI for complete control, AI/LLM visibility, and full code ownership:

bash
npx ag init --framework FRAMEWORK # react, vue, lit, svelte, etc.
npx ag add SelectionCardGroup
npx ag add SelectionCard

The CLI copies source code directly into your project, giving you full visibility and control. After running npx ag add, you'll receive exact import instructions.

Vue
vue
<template>
  <VueSelectionCardGroup
    type="radio"
    name="interests"
    legend="Select your interests"
    @selection-change="handleChange"
  >
    <VueSelectionCard value="tech" label="Technology">
      <span>Technology</span>
    </VueSelectionCard>
    <VueSelectionCard value="art" label="Art & Design">
      <span>Art & Design</span>
    </VueSelectionCard>
  </VueSelectionCardGroup>
</template>

<script setup>
import { VueSelectionCardGroup } from 'agnosticui-core/selection-card-group/vue';
import { VueSelectionCard } from 'agnosticui-core/selection-card/vue';

const handleChange = (e) => {
  console.log('Selected:', e.detail.selectedValues);
};
</script>
React
tsx
import { ReactSelectionCardGroup } from "agnosticui-core/selection-card-group/react";
import { ReactSelectionCard } from "agnosticui-core/selection-card/react";

export default function Example() {
  const handleChange = (e) => {
    console.log('Selected:', e.detail.selectedValues);
  };

  return (
    <ReactSelectionCardGroup
      type="radio"
      name="interests"
      legend="Select your interests"
      onSelectionChange={handleChange}
    >
      <ReactSelectionCard value="tech" label="Technology">
        <span>Technology</span>
      </ReactSelectionCard>
      <ReactSelectionCard value="art" label="Art & Design">
        <span>Art & Design</span>
      </ReactSelectionCard>
    </ReactSelectionCardGroup>
  );
}
Lit (Web Components)
html
<script type="module">
  import "agnosticui-core/selection-card-group";
  import "agnosticui-core/selection-card";

  const group = document.querySelector('ag-selection-card-group');
  group.addEventListener('selection-change', (e) => {
    console.log('Selected:', e.detail.selectedValues);
  });
</script>

<ag-selection-card-group type="radio" name="interests" legend="Select your interests">
  <ag-selection-card value="tech" label="Technology">
    <span>Technology</span>
  </ag-selection-card>
  <ag-selection-card value="art" label="Art & Design">
    <span>Art & Design</span>
  </ag-selection-card>
</ag-selection-card-group>

Props ​

SelectionCardGroup ​

PropTypeDefaultDescription
type'radio' | 'checkbox''radio'Selection mode
namestring''Input name attribute (required)
legendstring''Accessible group label
legend-hiddenbooleanfalseVisually hide legend
theme'' | 'success' | 'info' | 'warning' | 'error' | 'monochrome'''Theme variant (empty = primary/blue)
valuestring''Controlled value (radio)
valuesstring[][]Controlled values (checkbox)
disabledbooleanfalseDisable all cards

SelectionCard ​

PropTypeDefaultDescription
valuestring''Unique value (required)
labelstring''Accessible label (required)
checkedbooleanfalseSelection state (managed by group)
disabledbooleanfalseDisable this card

Events ​

selection-change ​

Fired when selection changes.

typescript
interface SelectionChangeEventDetail {
  value: string;        // Value that triggered the change
  checked: boolean;     // Whether the card is now selected
  selectedValues: string[]; // All currently selected values
}

Themes ​

The theme prop controls the color scheme for selected cards:

ThemeDescription
'' (default)Primary blue - uses --ag-primary-* tokens
successGreen - uses --ag-success-* tokens
infoCyan/blue - uses --ag-info-* tokens
warningYellow/orange - uses --ag-warning-* tokens
errorRed - uses --ag-danger-* tokens
monochromeBlack/white - uses --ag-black and inverted tokens
html
<!-- Success theme -->
<ag-selection-card-group type="radio" name="status" theme="success">
  ...
</ag-selection-card-group>

Styling ​

CSS Custom Properties ​

css
/* Card sizing & spacing */
--ag-selection-card-padding: var(--ag-space-4);
--ag-selection-card-gap: var(--ag-space-3);
--ag-selection-card-border-radius: var(--ag-radius-md);
--ag-selection-card-indicator-size: var(--ag-space-5);

/* Card colors - unselected */
--ag-selection-card-background: var(--ag-background-primary);
--ag-selection-card-border-color: var(--ag-border);

/* Card colors - selected */
--ag-selection-card-selected-background: var(--ag-primary-background);
--ag-selection-card-selected-border-color: var(--ag-primary);
--ag-selection-card-indicator-color: var(--ag-primary);

/* Group layout */
--ag-selection-card-group-gap: var(--ag-space-4);

CSS Parts ​

SelectionCardGroup ​

PartDescription
ag-selection-card-group-fieldsetThe fieldset element
ag-selection-card-group-legendThe legend element
ag-selection-card-group-contentThe content wrapper (grid)

SelectionCard ​

PartDescription
ag-selection-card-containerThe outer clickable label
ag-selection-card-controlThe hidden input element
ag-selection-card-indicatorThe selection indicator (filled circle for radio, checkmark for checkbox)
ag-selection-card-contentThe slotted content wrapper

CSS Parts Example ​

css
/* Custom indicator size and shape */
ag-selection-card::part(ag-selection-card-indicator) {
  width: 1.5rem;
  height: 1.5rem;
}

/* Custom container styling */
ag-selection-card::part(ag-selection-card-container) {
  border-radius: var(--ag-radius-lg);
}

/* Responsive: stack cards on mobile */
@media (max-width: 640px) {
  ag-selection-card-group::part(ag-selection-card-group-content) {
    grid-template-columns: 1fr;
  }
}

Accessibility ​

  • Uses <fieldset> and <legend> for proper group semantics
  • Each card has a required label prop for screen readers
  • Keyboard navigation:
    • Arrow keys: Navigate between cards (selects in radio mode)
    • Space/Enter: Toggle selection
    • Home/End: Jump to first/last card
  • Focus ring visible on keyboard navigation
  • role="radiogroup" for radio mode, role="group" for checkbox mode