# SelectableCardGroup

> Selection owner for a set of SelectableCard options, with native fieldset semantics, controlled or uncontrolled state, and single or multiple selection.

- Status: beta
- Group: form
- Page: https://www.digitaltableteur.com/design-system/components/selectable-card-group
- Contract: https://www.digitaltableteur.com/contracts/v1/SelectableCardGroup.contract.json

## When to use

Use SelectableCardGroup when each choice needs more explanation than a plain radio or checkbox label. The group owns the question, selection state, validation and layout while SelectableCard children own option content.

## When not to use

- Do not mix SelectableCard with unrelated interactive children inside the group.
- Do not pass value without updating it from onValueChange; use defaultValue for uncontrolled state.
- Do not use rich cards when plain RadioGroup or CheckboxGroup labels communicate the choices clearly.

## Look-alikes

- CheckboxGroup: Do not use rich cards when plain RadioGroup or CheckboxGroup labels communicate the choices clearly.
- RadioGroup: Do not use rich cards when plain RadioGroup or CheckboxGroup labels communicate the choices clearly.

## Props

| Prop | Type | Default | Description | Required |
| --- | --- | --- | --- | --- |
| `type` | `"single" \| "multiple"` | none | Selection model: one exclusive value or several independent values. | No |
| `legend` | `string` | none | Visible fieldset legend and accessible group name. | Yes |
| `name` | `string` | none | Shared native radio name; generated when omitted. | No |
| `value` | `string \| string[]` | none | Controlled selection value matching the selection type. | No |
| `defaultValue` | `string \| string[]` | none | Initial selection for uncontrolled use. | No |
| `onValueChange` | `(value: string \| string[]) => void` | none | Receives the next selection after an option is activated. | No |
| `orientation` | `"vertical" \| "horizontal"` | none | Visual stack direction for the options. | No |
| `disabled` | `boolean` | none | Disables every option in the fieldset. | No |
| `error` | `string` | none | Validation message announced below the group. | No |
| `helperText` | `string` | none | Supporting guidance suppressed while an error is present. | No |
| `className` | `string` | none | Additional class name merged onto the fieldset. | No |
| `children` | `ReactNode` | none | SelectableCard options owned by this group. | Yes |

## Variants

- Available types: `single`, `multiple`.
- Available orientations: `vertical`, `horizontal`.

## Invalid combinations

- MUST NOT: `value` and `defaultValue` set together. `value` makes the component controlled and `defaultValue` uncontrolled; pass one, not both.

## Keyboard

| Key | Behaviour |
| --- | --- |
| Tab | Moves focus into the option set. |
| Arrow keys | Move between native radios in a single-select set. |
| Space | Selects or toggles the focused option. |

## Rules

Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.

| Level | Rule | How it is checked |
| --- | --- | --- |
| MUST NOT | `value` and `defaultValue` set together. `value` makes the component controlled and `defaultValue` uncontrolled; pass one, not both. | Checked by the MCP tool validate_component_usage. |
| MUST | ARIA: The fieldset has a visible legend. | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | ARIA: Helper and error messages are referenced by aria-describedby. | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | ARIA: An error sets aria-invalid and is announced through HelperText. | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | Required stories produce no axe violations at the configured severity. | Automated by `npm run test:stories:matrix:ci`. WCAG 2.5.8. |
| MUST | The accessibility tree (role, name, state) matches the committed snapshot for every required story. | Automated by `npm run test:stories:matrix:ci`. WCAG 1.3.1, 4.1.2. |
| MUST | The component remains legible and operable under a real browser forced-colors: active pass. | Automated by `npm run test:stories:hc`. |
| MUST | Every documented keyboard interaction works as specified. | Not yet verified: no evidence record. WCAG 2.1.1, 2.1.2. |
| MUST | A human completed an end-to-end accessibility review of this component. | Manual review, last on 2026-07-16. |
| SHOULD | Use a concise legend that states the decision being made. | Not stated in the contract. |
| SHOULD | Match value and defaultValue to type: strings for single selection and string arrays for multiple selection. | Not stated in the contract. |

## Install and import

- Install: `npm install @digitaltableteur/react` (version 0.9.1, restricted access on npm).
- Import: `import { SelectableCardGroup } from "@digitaltableteur/react";`
- Entry point: `@digitaltableteur/react/forms` exports it too.
- Styles: import `@digitaltableteur/react/style.css` once at the app boundary.
- Also available as the custom element `<dt-selectable-card-group>`; see the custom elements manifest.

## Links

- Machine-readable contract (Design System Contract 1.0): https://www.digitaltableteur.com/contracts/v1/SelectableCardGroup.contract.json
- Markdown version of this page: https://www.digitaltableteur.com/design-system/components/selectable-card-group.md
- Component index as JSON: https://www.digitaltableteur.com/design-system/components.json
- All components: https://www.digitaltableteur.com/design-system/components
- Source: The implementation lives in a private repository. This page, its Markdown twin and the contract are generated from it at build time and are the public source of truth.
