Styleframe Logo
Forms

Radio Group

A layout component for arranging a set of radios with shared orientation and spacing. Supports vertical and horizontal layouts and three gap sizes through the recipe system.

Overview

The Radio Group is a layout component that arranges related radios into a single, consistently spaced set. The useRadioGroupRecipe() composable creates a fully configured recipe with orientation and size options — a flex container that stacks radios vertically or lays them out in a wrapping row.

The group handles layout only. Single selection comes from the native input: give every child radio the same name, and the browser enforces one choice per group and wires up arrow-key navigation. Mark the container with role="radiogroup" (or use a <fieldset>) so assistive technology announces the set.

The Radio Group recipe integrates directly with the Radio recipe and generates type-safe utility classes at build time with zero runtime CSS.

Why use the Radio Group recipe?

The Radio Group recipe helps you:

  • Lay out sets consistently: Stack radios vertically or wrap them in a row with a single orientation prop.
  • Control spacing with one axis: The size variant sets the gap between items, so a whole group scales together.
  • Compose, don't couple: The group only handles layout; each child radio keeps its own color, size, and state.
  • Stay type-safe: Full TypeScript support means your editor catches invalid orientation or size values at compile time.

Usage

Register the recipe

Add the Radio Group recipe to a local Styleframe instance alongside the radio recipes. The global styleframe.config.ts provides design tokens and utilities, while the component-level file registers the recipes themselves:

src/components/radio-group.styleframe.ts
import { styleframe } from 'virtual:styleframe';
import {
    useRadioRecipe,
    useRadioFieldRecipe,
    useRadioGroupRecipe,
} from '@styleframe/theme';

const s = styleframe();

const radio = useRadioRecipe(s);
const radioField = useRadioFieldRecipe(s);
const radioGroup = useRadioGroupRecipe(s);

export default s;

Build the component

Import the radioGroup runtime function from the virtual module and wrap your radios in the container. Mark it with role="radiogroup" and give every child radio the same name:

src/components/RadioGroup.tsx
import { radioGroup } from "virtual:styleframe";

interface RadioGroupProps {
    orientation?: "vertical" | "horizontal";
    size?: "sm" | "md" | "lg";
    children?: React.ReactNode;
}

export function RadioGroup({
    orientation = "vertical",
    size = "md",
    children,
}: RadioGroupProps) {
    const classes = radioGroup({ orientation, size });

    return (
        <div className={classes} role="radiogroup">
            {children}
        </div>
    );
}

See it in action

Orientation

The Radio Group supports two orientation variants that control the layout direction.

Vertical

The default orientation. Radios are stacked top-to-bottom in a column — the most common layout for option lists.

Horizontal

Radios are laid out left-to-right in a wrapping row, aligned to the center. Useful for short option sets that fit inline.

Orientation Reference

OrientationDirectionNotes
verticalTop to bottom (column)Default; one option per line
horizontalLeft to right (row, wraps)Center-aligned, wraps to the next line when out of space

Sizes

Three size variants control the gap between radios, letting you tighten or loosen a group to match its surroundings. Set the same size on each child radio so the controls and the spacing scale together.

SizeGap
sm@0.5
md@0.75
lg@1
Good to know: The group's size controls only the gap between items. Pass the same size to each child <Radio> so the circles and the spacing scale as a set.

Accessibility

  • Mark the group. Wrap the radios in an element with role="radiogroup" and an aria-label (or aria-labelledby) describing the set, so assistive technology announces the context.
  • Share a name across children. Single selection and arrow-key navigation come from giving every child radio the same name. The group recipe only handles layout.
  • Prefer a fieldset for forms. When the group is part of a form, a native <fieldset> with a <legend> is the most robust grouping — apply the recipe class to the fieldset.
  • Keep each radio labelled. The group provides layout only; every child still needs its own label (see the Radio recipe).

Customization

Overriding Defaults

The useRadioGroupRecipe() composable accepts an optional second argument to override any part of the recipe configuration. Overrides are deep-merged with the defaults, so you only need to specify the properties you want to change:

src/components/radio-group.styleframe.ts
import { styleframe } from 'virtual:styleframe';
import { useRadioGroupRecipe } from '@styleframe/theme';

const s = styleframe();

const radioGroup = useRadioGroupRecipe(s, {
    defaultVariants: {
        orientation: 'horizontal',
        size: 'lg',
    },
});

export default s;

Filtering Variants

If you only need a subset of the available variants, use the filter option to limit which values are generated. This reduces the output CSS and keeps your component API focused:

src/components/radio-group.styleframe.ts
import { styleframe } from 'virtual:styleframe';
import { useRadioGroupRecipe } from '@styleframe/theme';

const s = styleframe();

// Only generate the vertical orientation
const radioGroup = useRadioGroupRecipe(s, {
    filter: {
        orientation: ['vertical'],
    },
});

export default s;
Good to know: Filtering also removes compound variants and adjusts default variants that reference filtered-out values, so your recipe stays consistent.

API Reference

useRadioGroupRecipe(s, options?)

Creates a radio group recipe — a flex container with orientation and size (gap) variants.

Parameters:

ParameterTypeDescription
sStyleframeThe Styleframe instance
optionsDeepPartial<RecipeConfig>Optional overrides for the recipe configuration
options.baseVariantDeclarationsBlockCustom base styles for the group
options.variantsVariantsCustom variant definitions for the recipe
options.defaultVariantsRecord<keyof Variants, string>Default variant values for the recipe
options.filterRecord<string, string[]>Limit which variant values are generated

Variants:

VariantOptionsDefault
orientationvertical, horizontalvertical
sizesm, md, lgmd

Learn more about recipes →

Best Practices

  • Pair with the Radio recipe: The group handles layout; each child should use the Radio recipe for consistent controls.
  • Share a name: Give every child radio the same name so the set behaves as one mutually-exclusive group.
  • Match sizes: Use the same size on the group and its child radios so spacing and circles scale together.
  • Keep groups scannable: Prefer vertical layout for longer option lists; reserve horizontal for two or three short options.
  • Label the set: Always describe the group's purpose with aria-label or a <legend> so screen readers communicate the relationship between options.

FAQ