Susegad UI
Register
Theme
Palette

Components

Radio group

A real fieldset of native radios: arrow keys, one choice per name, required and submitting all stay the browser's, with or without JavaScript.

quiet warm playful

Open the live demo npx susegad add radio

Stands on: Core, Core: components, Engine, Tokens

The prompt

the prompt

One choice from a few, the way a paper form circles an answer.

A real <fieldset> of native radios. The browser keeps doing what it always has: the arrow keys move and choose, one name allows one choice, required stops the form, the chosen value is sent. The register only changes how the circles are drawn.

<link rel="stylesheet" href="susegad/components/radio/radio.css">
<script type="module" src="susegad/components/radio/radio.js"></script>

<sg-radio-group orientation="row">
  <fieldset>
    <legend>Guests</legend>
    <label><input type="radio" name="guests" value="2" checked> 2</label>
    <label><input type="radio" name="guests" value="4"> 4</label>
  </fieldset>
</sg-radio-group>
AttributeValuesWhat it does
orientationcolumn (default), rowLays the choices out down or across.
seedany wordThe hand-drawn rings. Defaults to the legend.

The prompt

Make a radio group web component that wraps a real <fieldset> with a <legend> and native radios in labels, so it works with JavaScript off and keeps the browser's arrow keys, grouping, required and submission. Style each radio with appearance: none as a hairline circle with a CSS dot that scales in when chosen, aligned to the first line of its label with the lh unit. Give it three registers. Quiet: exactly that. Warm: over each radio, in the same grid cell and aria-hidden, draw a pencil circle whose ends cross, and for the chosen one an ink dot and an inked ring drawn round it the way a pen circles an answer on a paper form: a leaning oval a quarter bigger than a plain ring, well past the radio's edge, whose radius wavers with seeded noise, started at a seeded angle and carried past its start, spreading outward as it goes so the overlap shows at 1x. When the choice moves, draw the new ring in. Playful: a bold inked circle for each, and for the chosen one a kolam flower in place of the circle: four round petal loops drawn round the pulli, filling the circle with a notch between each pair, the pulli left as a hole of paper, blooming with a small turn. Show disabled radios dashed in every register. In forced-colours mode bring back the native radios; under reduced motion, no movement.

Words to code

When you sayTechniqueWhat happens
a real <fieldset> with a <legend>native firstThe group is announced by its legend ("group, Guests"). The browser's arrow keys, Tab stop on the chosen radio, skipping disabled ones and required all work with no script.
the way a pen circles an answernoisehandRing(seed, opts) walks 1 + overlap turns of a circle, adding seeded Perlin noise to the radius and starting at a seeded angle. spread ramps the radius outward over the last third of a turn, so the overrun runs outside the start instead of on top of it.
a leaning oval a quarter biggergeometryThe chosen ring is r: 10.4 (the old ring was 8.6), squash: 0.8 and tilt: -14, carried 0.3 turns past its start with a spread of 1.5. It spills past the 20-unit box; the SVG has overflow: visible. The browser check measures it at 1.23 times the radio's width.
draw the new ring ina real changeThe group counts real change events; the ring's stroke-dashoffset animates only when a new radio has just been chosen, never on first paint.
a kolam flower in place of the circlemotifFour cubic petals from the centre out to 8.4 units and back, round at the tip, filled in ink at 88%; the notches between them give the flower its outline at 20 px. The pulli is a 1.7-unit circle in the surface colour, and the plain ring is hidden while the flower shows.
dashed in every registerstateinput:disabled ~ .sg-radio-art dashes the pencil and the ink circle; the native disabled state keeps it out of the arrow keys.
bring back the native radiosforced coloursappearance: auto and the drawing hidden under forced-colors: active.

Accessibility

  • Keyboard: Tab lands on the chosen radio (or the first, if none); the arrow keys move and choose and skip disabled radios; Tab leaves the group. All native.
  • A screen reader hears the legend, then each radio's label and state.
  • Circles, rings and dots are 3:1 or more on every surface in every palette and theme (tested).
  • The drawing is aria-hidden and never takes a click. Forced colours: the system radios. Reduced motion: no movement.

Credit

The kolam dot is a pulli with petal loops, from the dot-grid kolam drawn at thresholds across South India. Tier: shared practice with local forms, used here as a small ornament.

<sg-radio-group> wraps a <fieldset> of native radios. The fieldset, the legend and the radios do all the work, with or without JavaScript.

Use

<link rel="stylesheet" href="susegad/components/radio/radio.css">
<script type="module" src="susegad/components/radio/radio.js"></script>

<sg-radio-group>
  <fieldset>
    <legend>Arriving by</legend>
    <label><input type="radio" name="arrive" value="car" required> Car</label>
    <label><input type="radio" name="arrive" value="train" required> Train to Thivim</label>
    <label><input type="radio" name="arrive" value="boat" disabled> Boat (closed in the monsoon)</label>
  </fieldset>
</sg-radio-group>

Always give the group a <legend>: it is the question the radios answer, and what a screen reader says first.

Attributes and properties

ValuesDefault
orientationcolumn, rowcolumn
seedany stringthe legend
registerquiet, warm, playfulinherited
value (property)the chosen radio's value, or ''; setting it chooses and fires change
radios (property)the native radios, in order

A server can swap the options (htmx); the drawings follow.

Registers

LookMotion
quiet (and no JS)hairline circles, a crisp dotthe dot scales in under 200ms
warmpencil circles whose ends cross; the choice gets an ink dot and is circled in ink, a leaning oval a quarter bigger that runs past its start, as a pen circles an answerthe ring is drawn in when the choice moves
playfulbold inked circles; the choice becomes a four-petalled kolam flower in place of its circlethe kolam blooms with a small turn

Reduced motion: no movement. Forced colours: the system radios.

Accessibility

  • Keyboard: native. Tab to the chosen radio, arrows to move and choose, disabled radios skipped.
  • Required: put required on the radios; the browser stops the form and says why.
  • Contrast: circles, rings and dots are 3:1 or more on every surface (WCAG 1.4.11), tested for every palette and theme.

Budget

Behaviour 5.8 KB of 12 KB (radio.js + radio.core.js); skins 0.3, 2.5 and 1.4 KB.