Cycle Toggle
Cycle toggles advance through a fixed set of values on each click, such as theme preference, sort order, or any small exclusive choice.
Try it
Usage
Compose RuiCycleToggleItem children inside RuiCycleToggle. Only the active item is visible in the button. Listen for rui-change to react to the new detail.value.
import { RuiCycleToggle, RuiCycleToggleItem } from '@ecopages/radiant-ui/cycle-toggle';
<RuiCycleToggle value="system" label="Theme" variant="ghost" size="sm">
<RuiCycleToggleItem id="system" selected>System</RuiCycleToggleItem>
<RuiCycleToggleItem id="light">Light</RuiCycleToggleItem>
<RuiCycleToggleItem id="dark">Dark</RuiCycleToggleItem>
</RuiCycleToggle>Custom markup
<rui-cycle-toggle> coordinates any light-DOM tree that matches its query contract. The Rui* helpers stamp these targets; they are not required.
import '@ecopages/radiant-ui/cycle-toggle';
<rui-cycle-toggle value="light" label="Theme" variant="ghost" size="sm">
<button type="button" data-cycle-toggle-button class="rui-button rui-cycle-toggle__button rui-button--ghost rui-button--sm">
<span data-cycle-value="system" class="rui-cycle-toggle__item">System</span>
<span data-cycle-value="light" class="rui-cycle-toggle__item">Light</span>
<span data-cycle-value="dark" class="rui-cycle-toggle__item">Dark</span>
</button>
</rui-cycle-toggle>BEM classes are presentation-only. Do not set hidden on items — the host owns visibility.
Sort order
Theming
The cycle button reuses the rui-button action roles, never Tailwind palette steps. variant / size map straight to the button system:
| Part | CSS roles |
|---|---|
Button (.rui-cycle-toggle__button) | rui-button chrome, primary filled, on-surface ghost, error destructive |
Item (.rui-cycle-toggle__item) | space-inline gap |
Button geometry (--size-control-*, --radius-control) is inherited from rui-button so the cycle control aligns with form rows. Override at the theme layer, not in component CSS.
API
RuiCycleToggle is a custom element (<rui-cycle-toggle>); the view composes the inner rui-button.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
value | string | '' | Active item id. |
variant | filled · outline · destructive · ghost · link | filled | Button variant passed to the inner button. |
size | none · sm · md · lg | md | Button size passed to the inner button. |
label | string | '' | Accessible name prefix for the cycle button. |
disabled | boolean | false | Disables the cycle button. |
Light-DOM contract
| Target | Required | Host writes | Author owns |
|---|---|---|---|
button[data-cycle-toggle-button] | yes | aria-label, disabled, rui-button--* classes | the button element |
[data-cycle-value] | yes (per item) | hidden | data-cycle-value, aria-disabled |
Do not set hidden on items. Nested hosts: none.
Events
| Event | Detail | Description |
|---|---|---|
rui-change | { value: string } | Emitted after value advances; detail.value is the new id. |
Methods
| Method | Description |
|---|---|
resync() | Re-apply button labeling and visible item from the current value. |
View helpers
| Component | Target stamped | Notes |
|---|---|---|
RuiCycleToggle | <rui-cycle-toggle> + RuiCycleToggleButton | Wraps items in the button. |
RuiCycleToggleItem | [data-cycle-value] | Pass id; maps to data-cycle-value. |
RuiCycleToggleButton | button[data-cycle-toggle-button] | Composes rui-button variant/size classes. |
CSS classes
Public BEM classes (documented via @cssclass):
| Class | Description |
|---|---|
.rui-cycle-toggle__button | Cycle toggle button root (adds rui-button rui-button--*). |
.rui-cycle-toggle__item | Cycle option row (icon + label). |
Theme roles
| Part | CSS variables consumed |
|---|---|
| Button chrome | --primary, --on-primary, --size-control-*, --radius-control (via rui-button) |
| Item | --space-inline |
Accessibility
- The host
labelsetsaria-labelon the cycle button. - Item children can include visible text, icons, or screen-reader-only labels.
- Space and Enter activate the native button.