Master Switch Cascade
A master switch cascade React component: flip one master switch and the child switches in a grid follow in a distance-based ripple, each cell flashing a ring as it flips; children stay individually toggleable and the master reflects on, off or mixed.
Gather a whole group of settings under a single master switch. When the master flips, the component measures the distance from each child cell's centre to the origin (the master switch, the grid's top-left corner or the grid's centre) and schedules proportional delays, so the child switches flip in a ripple from nearest to farthest, each cell flashing an accent ring at the moment it changes. Children can also be toggled one by one, and the master always reflects all on, all off or partial (shown with a "—" in the thumb). Disabled items stay out of the cascade. The switch itself (track + thumb) is implemented inside the component with no dependency on other components, and the value supports controlled and uncontrolled modes.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add master-switch-cascade.enPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<MasterSwitchCascade/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add https://webberui.com/r/master-switch-cascade.en.jsonOr, once registries are configured in components.json, install it as @webberui/master-switch-cascade.
Usage
import { Package, CreditCard, Truck, MessageSquare } from "lucide-react";
import { MasterSwitchCascade } from "@/components/ui/master-switch-cascade";
const items = [
{ id: "orders", label: "Order updates", icon: <Package /> },
{ id: "payments", label: "Payment reminders", icon: <CreditCard /> },
{ id: "shipping", label: "Shipping alerts", icon: <Truck /> },
{ id: "reviews", label: "Review replies", icon: <MessageSquare /> },
];
export function NotificationSettings() {
const [value, setValue] = React.useState<Record<string, boolean>>({
orders: true,
});
return (
<MasterSwitchCascade
items={items}
value={value}
onChange={setValue}
masterLabel="Enable all"
origin="master"
spread={600}
columns={4}
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | MasterSwitchItem[] | — | The child switches |
value | Record<string, boolean> | — | Controlled value: switch states keyed by item id; when omitted the component manages it internally (uncontrolled) |
defaultValue | Record<string, boolean> | {} | Initial state in uncontrolled mode; missing ids count as off |
onChange | (value: Record<string, boolean>) => void | — | Called whenever a child switch changes; during a cascade it fires once per child as each one flips |
masterLabel | string | "Enable all" | Title of the master row, also the master switch's accessible name (the Chinese version defaults to "全部啟用", "enable all") |
origin | "master" | "top-left" | "center" | "master" | Ripple origin: the master switch's centre, the grid's top-left corner or the grid's centre |
spread | number | 600 | Total cascade span (ms): the farthest child flips after this delay |
columns | number | 4 | Number of grid columns |
accent | string | "#3b82f6" | Accent colour, written to --wb-master-switch-accent |
className | string | — | Forwarded to the outermost container |
MasterSwitchItem
| Field | Type | Default | Description |
|---|---|---|---|
id | string | — | Unique id, also the key in the value object |
label | string | — | Display label, also the switch's accessible name |
icon | ReactNode | — | Icon shown in the cell's top-left corner (lucide-react icons recommended) |
disabled | boolean | false | Disabled: cannot be toggled and does not take part in the master cascade |
How it works
- Delay formula: each cell's delay = its centre's distance to the origin ÷ the farthest distance among all participating cells ×
spread. The farthest distance is measured over every non-disabled cell, so a given cell always gets the same delay and the rhythm stays stable. - Toggle rule: flipping the master while it is all on turns everything off; flipping it while all off or partial turns everything on. Only children whose state actually needs to change are queued and flash a ring.
- Queue handling: delayed child flips are scheduled with a
setTimeoutqueue; flipping the master again clears the queue first and restarts from the current state, and unmounting clears it too. Toggling a cell by hand mid-cascade cancels that cell's pending flip — the manual choice wins. - While a cascade is running, the master shows the target state directly instead of flashing to partial midway.
- The tri-state only counts non-disabled items: a disabled item's state does not affect the master.
- CSS variables:
--wb-master-switch-duration(thumb travel and track colour, default 150ms),--wb-master-switch-ring(ring duration, default 400ms),--wb-master-switch-accent(accent colour, written from theaccentprop).
Accessibility
- Each child is a
type="button"withrole="switch"andaria-checkedfor its state; its accessible name comes from the label inside the cell, and disabled items setdisabled - The master needs to express a partial state, and
role="switch"does not supportaria-checked="mixed", so the master always usesrole="checkbox"witharia-checkedoftrue/false/mixed, named viaaria-labelledbypointing at the master title - The child grid is a
role="group"named by the same master title - Every switch is a native button, so Space and Enter both toggle it, with a focus-visible ring
- When a cascade finishes, a
role="status",aria-live="polite"region announces the result, such as "12 enabled" or "11 turned off" - When the user has reduce motion enabled at the system level, all children flip at once with no ring, and the thumb transition is skipped
Radius & Shadow Tuner
Drag handles to adjust a box's corner radius, shadow and padding with live readouts and the resulting CSS — ideal for demonstrating design tokens in docs.
Permission Wave Grid
An admin flips one switch; SVG wires draw from it to a member grid and avatars unlock wave by wave — showing access granted to everyone at once.