GrRadio

  • core
  • forms
  • API · 15 props
  • ⌨ keys

A single choice within a group of mutually exclusive options.

import { GrRadio } from '@feugene/granularity/components/GrRadio'

Overview

When to take it

  • the switch stands on its own — an option in a table row, in a card, in a list cell: there is no group around it;
  • the layout is set by the consumer — the options are laid out on a grid of their own rather than in a row or a column;
  • the look of a button — variant="button" gives the appearance of GrButton while keeping the radio role;
  • the label is complex — the slot accepts markup: a price, a badge, an icon.

When to take something else

NeedTake
There are several options and they stand side by sideGrRadioGroup
Several can be selectedGrCheckbox
There are 2–5 options and they switch a viewGrSegmented
There are more than seven optionsGrSelect

Outside GrRadioGroup the keyboard contract of the radiogroup pattern does not work: the arrows between options are walked by the container. A single GrRadio is a deliberate refusal of it for the sake of a layout of your own rather than a simplification.

Variants every tone and view, rendered by the real component

variant

  • radiobox
  • button

Sizes

  • xs
  • sm
  • md
  • lg

States

  • normal
  • disabled
  • invalid

The grid is live: hover and press it. Hover, focus and active states are not drawn here — they belong to the browser, not to a prop.

Playground controls come from the API, not from hand

Stage

Loading…

0 changed· state is in the URL

Examples 3 each with a live demo and its code

  • Plans as cards

    Separate radios with one name make a group of cards: a click anywhere on a card chooses its plan, the radio inside keeps the keyboard, and the description slot puts the note inside the radio’s accessible name — the difference between plans is what the choice is made on.

    Choose a plan
    €12 / month
    €49 / month
    Custom
  • A delivery option with its own field

    Separate radios let content live between the options: the parcel point is chosen right under its option, which an options list cannot do. The select stays in place and is enabled only with its option, so the form does not jump; a disabled option keeps saying why.

    Delivery
  • A period in a card header

    variant="button" changes the drawing, not the semantics: one name, arrow keys between the options. Quiet buttons fit a card header — button-variant="ghost" at rest, a filled neutral one when chosen through selected-button-variant and selected-button-tone.

    Revenue€76,900 +6.1%

API generated from the package · cannot drift

Props

PropTypedefaultDescription
disabledboolean | undefinedundefinedThe radio is unavailable. Unset — taken from the group.
variantGrRadioVariant | undefined"radiobox"The look: `radiobox` — a circle with a dot and a label, `button` — a button in the style of `GrButton`. `radiobox` by default.
modelValuev-modelGrRadioValue | undefinedundefinedThe selected value (`v-model`) of a standalone radio. In a group the group holds the value.
invalidboolean | undefinedfalseThe visual and ARIA state of an error. It adds up with the `invalid` of the group.
requiredboolean | undefinedfalseA choice is required: `aria-required` on the radio.
size"xs" | "sm" | "md" | "lg" | undefinedundefinedThe size on the button scale. Unset — from the group, then `GrConfigProvider`, otherwise `md`.
ariaLabelstring | undefinedundefinedThe accessible name when there is no label in the slot — for example, for an icon-only button.
namestring | undefinedundefinedThe name for a native form: the selected radio submits its value in a hidden field. Unset — from the group.
idstring | undefinedundefinedThe `id` of the element with the `radio` role — for references to it from outside.
formstring | undefinedundefinedThe `form` of the hidden field — when the form the value goes to is not around the radio.
buttonVariantGrButtonVariant | undefined"outline"The weight of an unselected button in `variant="button"`. `outline` by default.
buttonTone
"primary" | "neutral" | "success" | "warning" | "danger" | ""primary" | "neutral" | "success" | "warning" | "danger" | "info" | "slate" | "azure" | undefined
"neutral"The tone of an unselected button in `variant="button"`. `neutral` by default.
selectedButtonVariantGrButtonVariant | undefined"primary"The weight of the selected button in `variant="button"`. `primary` by default.
selectedButtonTone
"primary" | "neutral" | "success" | "warning" | "danger" | ""primary" | "neutral" | "success" | "warning" | "danger" | "info" | "slate" | "azure" | undefined
"primary"The tone of the selected button in `variant="button"`. `primary` by default.

Slots

SlotTypeDescription
defaultanyThe label of the radio instead of the `label` prop.
descriptionanyAn explanation under the label.

Events

EventSignatureDescription
update:modelValue(event: "update:modelValue", value: GrRadioValue): void—

Types

Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.

Import
import type { GrRadioProps, GrRadioEmits, GrRadioConfigurableProps } from '@feugene/granularity/components/GrRadio'

Filedist/types/components/GrRadio/GrRadio.vue.d.ts

GrRadioPropsinterface
interface GrRadioProps {
    value: GrRadioValue;
    modelValue?: GrRadioValue;
    disabled?: boolean;
    invalid?: boolean;
    name?: string;
    required?: boolean;
    form?: string;
    id?: string;
    size?: GrButtonSize;
    variant?: GrRadioVariant;
    buttonVariant?: GrButtonVariant;
    buttonTone?: GrButtonTone;
    selectedButtonVariant?: GrButtonVariant;
    selectedButtonTone?: GrButtonTone;
    ariaLabel?: string;
}
GrRadioEmitsinterface
interface GrRadioEmits {
    (e: 'update:modelValue', value: GrRadioValue): void;
}

Filedist/types/components/GrRadio/config.d.ts

grRadioConfigconst
const grRadioConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrRadio">;

Filedist/types/components/GrRadio/defaults.d.ts

GrRadioConfigurablePropsinterface
interface GrRadioConfigurableProps {
    size: GrButtonSize;
}

Filedist/types/components/GrRadio/grRadioStyles.d.ts

GrRadioVarianttype
type GrRadioVariant = 'radiobox' | 'button';

Filedist/types/components/GrRadio/grRadioGroupContext.d.ts

GrRadioGroupContexttype
type GrRadioGroupContext = {
    modelValue: ComputedRef<GrRadioValue>;
    name: ComputedRef<string | undefined>;
    disabled: ComputedRef<boolean>;
    readonly: ComputedRef<boolean>;
    invalid: ComputedRef<boolean>;
    size: ComputedRef<GrButtonSize>;
    setValue: (value: GrRadioValue) => void;

Filedist/types/components/GrRadio/safelist.d.ts

grRadioSafelistconst
const grRadioSafelist: string[];

Accessibility

APG pattern

radio

Keys

Enter↑ / ←

Usage from the library prose

The keyboard: the group is one Tab stop
KeyWhat it does
Tabenters the group and leaves it; inside the group there is exactly one stop (a roving tabindex)
↓ / →the next option, in a ring
↑ / ←the previous option, in a ring
Home / Endthe first / the last available option
Space, Enterselect the current one (for a standalone switch)

The selection travels with the focus — that is what the radiogroup pattern requires. Disabled options are skipped: they take part neither in the roving tabindex nor in the walk with the arrows.

The state lives in GrRadioGroup (register / rovingValue / moveSelection / selectEdge in the context), and GrRadio asks the group whether it is the focusable one right now. A standalone switch outside a group remains an ordinary Tab stop.

The label and the description
vue
<GrRadio value="pro">
  Pro
  <template #description>
    Charged monthly, can be cancelled at any moment
  </template>
</GrRadio>

The label of a selected option is --gr-fg, of an unselected one --gr-muted-fg: before that any of them was dimmed, and the selected option was not distinguished by text at all.

The #description slot is linked to the switch through aria-describedby — otherwise the description does not exist for a screen reader.

The values

value and modelValue are string | number | boolean (GrRadioValue). Enumerations in real forms are usually an id as a number or a flag. Objects are deliberately not included: the value travels into data-value and into the hidden input of a native form, which means it has to have an unambiguous string representation.

The error and the disabled state

invalid can be set on the switch itself or on the whole group — the states add by “or”. The border of the control is coloured --gr-danger, and aria-invalid goes onto the element.

A disabled switch is dimmed with tokens (--gr-muted / --gr-muted-fg) rather than with transparency: opacity dilutes colours tuned to AA. The button variant takes its disabled look from GrButton itself — there it depends on the variant.

The native form

An input[type="hidden"] is rendered beside the switch — only for the selected and non-disabled element and only when name is set, as a native radio would do. Nothing interactive is nested inside an element with role="radio": the role declares its descendants presentational, and a hidden <input type="radio"> would break the widget for a screen reader (axe: nested-interactive).