# Toggle Group

> A group where one option is selected at a time. A CSS component with two classes and no JavaScript.

The group is radio buttons in disguise. Each option is a `<label>` with an `<input type="radio">` inside, shrunk away visually. You keep the arrow keys, `FormData` and the screen reader saying “2 of 3”, without having to rewrite any of it.

## How to use it

**Plain HTML**

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/src/tokens/tokens.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/src/components/css/toggle-group/toggle-group.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/src/components/css/sr-only/sr-only.css">
<!-- Or everything in one file: https://cdn.jsdelivr.net/npm/@lo-tek/interfaces@0.36.0/dist/lotek.css -->
```

```html
<fieldset class="lt-toggle-group">
<legend class="lt-sr-only">View</legend>


<label class="lt-toggle-group__option">
  <input type="radio" name="view" value="list" checked /> List
</label>
<label class="lt-toggle-group__option">
  <input type="radio" name="view" value="map" /> Map
</label>
<label class="lt-toggle-group__option">
  <input type="radio" name="view" value="calendar" /> Calendar
</label>
</fieldset>
```

**TypeScript**

```ts
import "@lo-tek/interfaces/tokens.css"
import "@lo-tek/interfaces/toggle-group.css"
import "@lo-tek/interfaces/sr-only.css"
```

`lt` is used in two ways, and which one depends on whether you create the element or find it.

**If you create the markup,** spread the attributes in. That is the way in React, Astro and anything else that renders:

```tsx
import { lt } from "@lo-tek/interfaces/react"


<fieldset {...lt.toggleGroup()}>
  <legend {...lt.srOnly()}>View</legend>
  <label className={lt.toggleGroup.option}>
    <input type="radio" name="view" value="list" defaultChecked />
    List
  </label>
</fieldset>
```

The import is from `/react` here, since the block is JSX. From there the keys are `className` and `htmlFor`. In Astro and anything else without React, import `@lo-tek/interfaces` and you get `class` and `for`.

**If the element is already there,** set them on it. That is the way when you enhance HTML a server or a template has sent:

```ts
import { lt } from "@lo-tek/interfaces"


const group = document.querySelector<HTMLFieldSetElement>("#view")
if (!group) throw new Error("element not found")


lt.setAttributes(group, lt.toggleGroup())
```

`lt.toggleGroup.option` gives the class for each option. You read the choice itself as usual:

```ts
group.addEventListener("change", (event) => {
  const selected = (event.target as HTMLInputElement).value
})
```

`<legend>` is required even when it is hidden. Without it, someone listening to the page does not know what the three options choose between.

## When this is not the one you want

| Situation | Component |
| - | - |
| One choice among three to five short options | **Toggle Group** |
| One choice among many, or long options | [Radio](/components/radio.md) in a [Fieldset](/components/fieldset.md) |
| Several choices at once | [Checkbox](/components/checkbox.md) |
| On and off for one setting | [Switch](/components/switch.md) |
| Actions, such as “bold, italic, underline” | Buttons with `aria-pressed` |

## Classes you can style

| Class | What it is |
| - | - |
| `.lt-toggle-group` | The group |
| `.lt-toggle-group__option` | The label around each option |

| Variable | Default |
| - | - |
| `--lt-toggle-group-radius` | `var(--lt-spacing-1)` |
| `--lt-toggle-group-height` | `var(--lt-spacing-10)` |
| `--lt-toggle-group-padding` | `0 var(--lt-spacing-4)` |

The selected surface is set with `:has(input:checked)`, that is from the state of the real radio button. There is no separate class to keep in sync.

See [Customising](/customising.md) for how the variables and the layer work.

## Accessibility

- The radio button is shrunk away, not removed. `display: none` would take it out of the keyboard order, and the arrow keys would then stop working.
- The arrow keys move between the options and select at the same time. Tab leaves the whole group. That is how radio buttons work, and it should not be overridden.
- The options have to be short. If the text gets long, it is really a list of radio buttons.
- Do not let a choice trigger something that cannot be undone. The user lands on every option on the way when moving with the arrow keys.
