Component
Segmented control
Pick one value from a short set — an index, a market view, a timeframe. It looks like a row of buttons and behaves like a radio group, and it takes part in forms like a native input.
- 2.1.1 Keyboard
- 1.3.1 Info and relationships
- 1.4.1 Use of colour
- 2.4.7 Focus visible
- 3.3.2 Labels or instructions
- 4.1.2 Name, role, value
Examples
Index
NIFTY
BANK NIFTY
FINNIFTY
SENSEX
<div class="row">
<span id="index-label" class="muted">Index</span>
<tai-segmented aria-labelledby="index-label" value="NIFTY" id="index-picker">
<tai-option value="NIFTY">NIFTY</tai-option>
<tai-option value="BANKNIFTY">BANK NIFTY</tai-option>
<tai-option value="FINNIFTY">FINNIFTY</tai-option>
<tai-option value="SENSEX" disabled>SENSEX</tai-option>
</tai-segmented>
</div>
<div class="log" role="log" aria-label="Index picker events" id="log-index"></div>
<form id="view-form" class="stretch">
<fieldset style="border:0;padding:0;margin:0 0 1rem">
<legend class="label" style="font-weight:500;margin-bottom:.4rem">Market view <span class="muted">(required)</span></legend>
<tai-segmented name="view" required label="Market view">
<tai-option value="bullish">Bullish</tai-option>
<tai-option value="bearish">Bearish</tai-option>
<tai-option value="neutral">Non-directional</tai-option>
</tai-segmented>
</fieldset>
<div class="row">
<button class="btn btn--primary" type="submit">Suggest strategies</button>
<button class="btn" type="reset">Reset</button>
</div>
<p class="hint" id="form-result" role="status"></p>
</form>API
| Name | Kind | Description |
|---|---|---|
value | attribute / property | Selected option's value. Reflects. Setting it doesn't fire events. |
label | attribute | Accessible name (or use aria-labelledby pointing at a visible label). |
name | attribute | Form field name; the value is submitted with the form. |
required | attribute | The form won't submit until something is chosen (valueMissing). |
disabled | attribute | Disables the whole group (also follows a disabled <fieldset>). |
form, validity, checkValidity(), reportValidity() | properties / methods | Form-associated custom element API via ElementInternals. |
change | event | The user chose an option — like a native radio. |
tai-change | event | Same moment, with detail: { value, previousValue }. Use this one from React. |
tai-option → value, disabled | child attributes | Each choice. The value defaults to its text. |
check | part (on tai-option) | The check mark shown on the selected option. |
Keyboard
| Key | Action |
|---|---|
| Tab | Into the group lands on the selected option (or the first, if none). One Tab stop for the whole group. |
| → ↓ / ← ↑ | Select the next / previous option, wrapping and skipping disabled ones. |
| Space | Selects the focused option if it isn't already. |
Accessibility notes
Why radio semantics for something that looks like buttons
Three toggle buttons would each be a Tab stop and say “pressed / not pressed”, which doesn't tell a screen reader user that exactly one can be on. A radio group says “1 of 4, selected” and moves with arrow keys. In the app, the Trading Desk index picker already had role="radio" but no arrow keys — every option was a Tab stop, which is the half-done version of this.
- Form-associated through
ElementInternals:name/valueare submitted, form reset restores the initial value,requiredblocks submission with the browser's own validation message. - Selection isn't colour alone: the chosen option is raised, outlined and shows a check mark.
- Forced colours: the selected option gets a system
Highlightoutline.
Usage guidance
Do
- Use for 2–5 short, mutually exclusive options.
- Give the group a visible label and point
aria-labelledbyat it.
Don't
- Use it to switch between panels of content (that's tabs).
- Use it for actions (“Run”, “Stop”) — those are buttons.
- Let it wrap onto many lines; use a select instead.
Using it in React
Listen to ontai-change: React's own onChange is wired to its synthetic event system for native inputs, not to custom elements.
<tai-segmented label="Index" value={symbol} ontai-change={e => setSymbol(e.detail.value)}>
{INDICES.map(s => <tai-option key={s} value={s}>{s}</tai-option>)}
</tai-segmented>