Segmented Control
A radio-group-style toggle bar that renders slotted arc-option elements as a row of mutually exclusive buttons with an active highlight.
<arc-segmented-control> Overview
SegmentedControl provides a compact, horizontal set of mutually exclusive options rendered as a pill-shaped button group. It reads <arc-option> children from its default slot and mirrors them as styled buttons inside a bordered container with rounded corners. The currently selected option receives an accent-primary background with a subtle glow, while unselected options appear in muted text that brightens on hover.
The component uses a radiogroup ARIA role with individual radio roles on each option button, following the WAI-ARIA radio group pattern. Keyboard navigation supports arrow keys for cycling through options (with wrapping), Home/End for jumping to the first or last option, and Enter/Space for confirming a selection. Focus management automatically moves focus to the newly selected button after keyboard navigation.
SegmentedControl auto-selects the first option when no initial value is provided, ensuring the control always has a valid selection. It fires a single arc-change event with the selected value whenever the user makes a new choice, making it straightforward to wire into form state or reactive frameworks.
Guidelines
When to use
- Use SegmentedControl for 2-5 options where the user must pick exactly one
- Keep option labels short — ideally one or two words — to prevent overflow
- Provide a `value` attribute if you need to pre-select an option other than the first
- Listen to `arc-change` to react to selection changes in your application logic
- Place the control within a form context or a settings panel where space is limited
When not to use
- Do not use for more than 5 options — use Select or RadioGroup instead for longer lists
- Do not nest interactive elements inside `<arc-option>` children — labels should be plain text
- Do not use SegmentedControl for navigation between views — use Tabs instead
- Do not rely solely on the glow color to indicate selection — the component also uses aria-checked for accessibility
- Avoid using it for binary toggles where a Toggle switch would be more semantically appropriate
Features
- Renders slotted `<arc-option>` elements as styled toggle buttons in a horizontal pill container
- Active option highlighted with accent-primary background, contrasting text, and glow shadow
- Full keyboard navigation: arrow keys cycle options with wrapping, Home/End jump to edges, Enter/Space confirm
- ARIA radiogroup pattern with `role="radio"` and `aria-checked` on each option button
- Auto-selects the first option when no `value` attribute is provided
- Hover state brightens text and adds a subtle background on non-active options
- Disabled state at 40% opacity with pointer events blocked on the entire control
- Respects `prefers-reduced-motion` by disabling transitions
Preview
Usage
This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.
<arc-segmented-control value="monthly">
<arc-option value="daily">Daily</arc-option>
<arc-option value="weekly">Weekly</arc-option>
<arc-option value="monthly">Monthly</arc-option>
</arc-segmented-control> import { SegmentedControl, Option } from '@arclux/arc-ui-react';
export default function Example() {
return (
<SegmentedControl value="monthly">
<Option value="daily">Daily</Option>
<Option value="weekly">Weekly</Option>
<Option value="monthly">Monthly</Option>
</SegmentedControl>
);
} <script setup>
import { SegmentedControl, Option } from '@arclux/arc-ui-vue';
</script>
<template>
<SegmentedControl value="monthly">
<Option value="daily">Daily</Option>
<Option value="weekly">Weekly</Option>
<Option value="monthly">Monthly</Option>
</SegmentedControl>
</template> <script>
import { SegmentedControl, Option } from '@arclux/arc-ui-svelte';
</script>
<SegmentedControl value="monthly">
<Option value="daily">Daily</Option>
<Option value="weekly">Weekly</Option>
<Option value="monthly">Monthly</Option>
</SegmentedControl> import { Component } from '@angular/core';
import { SegmentedControl, Option } from '@arclux/arc-ui-angular';
@Component({
imports: [SegmentedControl, Option],
template: `
<arc-segmented-control value="monthly">
<arc-option value="daily">Daily</arc-option>
<arc-option value="weekly">Weekly</arc-option>
<arc-option value="monthly">Monthly</arc-option>
</arc-segmented-control>
`,
})
export class MyComponent {} import { SegmentedControl, Option } from '@arclux/arc-ui-solid';
export default function Example() {
return (
<SegmentedControl value="monthly">
<Option value="daily">Daily</Option>
<Option value="weekly">Weekly</Option>
<Option value="monthly">Monthly</Option>
</SegmentedControl>
);
} import { SegmentedControl, Option } from '@arclux/arc-ui-preact';
export default function Example() {
return (
<SegmentedControl value="monthly">
<Option value="daily">Daily</Option>
<Option value="weekly">Weekly</Option>
<Option value="monthly">Monthly</Option>
</SegmentedControl>
);
} API
-
valuestring'' - The value of the currently selected option. Reflected as an attribute and auto-set to the first selectable option if empty.
-
namestring'' - The form field name submitted with the selected value. Required for native form integration — without it, the selection will not appear in FormData.
-
disabledbooleanfalse - Disables the entire control, reducing opacity to 40% and blocking pointer events.
-
formAssociatedbooleantrue -
propertiesobject{ // flag(), unlike `disabled`. The exclusion in props.js is specifically // about form-associated *platform* semantics: a `disabled` content // attribute that is merely present makes the element actually disabled // per the HTML spec, and formDisabledCallback assigns the property back, // so no converter can win. Neither of these is platform-mapped — // `required` is enforced by _computeValidity() below and `readonly` by // each component's own interaction handlers — so the stock converter buys // nothing here and costs the usual bug: `required="false"` read as true, // blocking submission of a form the author meant to leave optional. // Finding #48's shape, across all 26 form controls at once. required: flag(false), readonly: flag(false), } - Lit merges static properties up the prototype chain, so every consumer
gets these without declaring them.
requiredparticipates in constraint validation below;readonlyreflects for styling and is enforced by each component's interaction handlers (the mixin can't know which gestures mutate state). -
autoValidatesbooleantrue - Components that run their own constraint-validation logic (pattern checks, range checks) opt out of the automatic required sync by overriding this to false, and own the whole validity flag set instead.
-
form -
validity -
validationMessage -
requiredbooleanfalse -
readonlybooleanfalse
Methods
-
checkValidity()boolean - Whether the control currently satisfies its constraints, per the native
constraint-validation API. Fires
invalidon the element when it does not, and reports nothing to the user. -
reportValidity()boolean - As checkValidity(), but also shows the browser's validation message against the control when it fails.
Events
-
arc-changedetail: { value: string } - Fired when the selected segment changes
Option
<arc-option> Individual option element slotted into the segmented control. The `value` attribute identifies the option and the text content becomes the label.
-
label - Expose text content as label
-
valuestring'' - The value identifier for this option, used to match against the parent control value.
-
disabledbooleanfalse - When true, dims this option and prevents it from being selected.
-
selectedbooleanfalse
See Also
- Tabs Tabbed content navigation with keyboard support and ARIA roles.
- Radio Group Single-select option group with arrow-key navigation and ARIA radiogroup semantics. Ideal for pricing tiers, settings panels, and any context where exactly one choice must be made from a visible set of options.
- Chip A toggleable pill-shaped element for filters, tags, or multi-select options, with a selected state highlighted in accent-primary.