first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
# Switch
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs --> - switch.variants — `switchVariants` and `switchThumb…
- [switch.variants](./switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
- switch.variants — `switchVariants` and `switchThumb…
- [switch.variants](./switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
- switch.variants — `switchVariants` and `switchThumb…
- [switch.variants](./switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
- switch.variants — `switchVariants` and `switchThumb…
- [switch.variants](./switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
- [switch.variants](./switch.variants.md) — `switchVariants` and `switchThumbVariants` are the two cva (class-variance-authority) configurations that together powe…
## Purpose
`<Switch>` is the chassis on/off toggle primitive built on `@radix-ui/react-switch`. Radix supplies the a11y semantics (`role="switch"`, `aria-checked` reflecting boolean state, native `Space` activation, `disabled` propagation); the chassis layers theme-driven variant resolution, the sliding-thumb render, and the cva `size` axis that scales both the track and the thumb proportionally.
The thumb cva is exported alongside the track cva so consumers can theme each independently. Refs are forwarded to the Radix Root which renders a styled `<button>`.
Icon-only switches (no visible label sibling) MUST supply an `aria-label` so screen readers can announce what is being toggled. The chassis does NOT inject a fallback because the label is domain-specific — only the consumer knows what is being switched on or off.
## Features
- Three sizes (`sm`, `default`, `lg`) scaling track + thumb proportionally.
- Sliding thumb with `data-[state=checked]:translate-x-*` driving the on/off motion via Tailwind transforms.
- Controlled and uncontrolled use via Radix `checked` / `defaultChecked` / `onCheckedChange`.
- `disabled` blocks pointer + keyboard input and decays opacity.
- `data-state="checked|unchecked"` mirrored on the rendered button.
- Refs forwarded to the Radix Root.
- `motion-reduce:transition-none` collapses both colour + transform transitions for `prefers-reduced-motion: reduce`.
- Theme-driven variant overrides via `useThemedVariants("Switch", switchVariants)`.
## Usage: Basic Example
```tsx
import { Switch } from "@suaveplan/react-ui";
export function AirplaneToggle() {
return (
<div className="flex items-center gap-2">
<Switch id="airplane" />
<label htmlFor="airplane">Airplane mode</label>
</div>
);
}
```
## Usage: Advanced Example
```tsx
import { Switch } from "@suaveplan/react-ui";
import { useState } from "react";
export function NotificationSettings() {
const [enabled, setEnabled] = useState(true);
const [marketing, setMarketing] = useState(false);
return (
<div className="flex flex-col gap-3 p-4 border rounded">
<div className="flex items-center justify-between">
<label htmlFor="notif">Notifications</label>
<Switch
id="notif"
checked={enabled}
onCheckedChange={setEnabled}
/>
</div>
<div className="flex items-center justify-between">
<label htmlFor="marketing">Marketing emails</label>
<Switch
id="marketing"
checked={marketing}
disabled={!enabled}
onCheckedChange={setMarketing}
/>
</div>
</div>
);
}
```
## API Reference
### `<Switch>`
`forwardRef<ComponentRef<typeof SwitchPrimitive.Root>, SwitchProps>` — wraps Radix Root, renders a `<button role="switch">` containing the sliding thumb.
#### `SwitchProps`
Inherits every prop from Radix `SwitchPrimitive.Root` except `asChild`. Adds:
| Prop | Type | Default | Description |
|---|---|---|---|
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | Visual variant scaling track + thumb. |
Common Radix props consumers will use:
| Prop | Type | Default | Description |
|---|---|---|---|
| `checked` | `boolean` | _uncontrolled_ | Controlled state. |
| `defaultChecked` | `boolean` | `false` | Initial state for uncontrolled use. |
| `onCheckedChange` | `(checked: boolean) => void` | _none_ | Fires on state change. |
| `disabled` | `boolean` | `false` | Blocks input + decays opacity. |
| `required` | `boolean` | `false` | Native form-field `required`. |
| `name` | `string` | _none_ | Native form-field name. |
| `value` | `string` | `"on"` | Native form-field value when checked. |
#### `switchVariants` / `switchThumbVariants`
Both bare cva resolvers are exported so external consumers can re-use the variants directly.
## Implementation Notes
- The track and thumb cva configs are kept separate so consumer themes can override either independently — for example a theme that makes the thumb glossy without changing the track shape.
- The thumb's `translate-x-*` per-size value is hard-coded into `switchThumbVariants` rather than computed because cva is a string-templating library, not a runtime calculator. The math is `track-width − thumb-width − padding-x*2`.
- `motion-reduce:transition-none` applies to both the track (colour transition) and the thumb (transform transition) so reduced-motion users see instant state changes.
- Refs target the Radix Root (the `<button>` element) — consumers can `.focus()` the switch programmatically through the forwarded ref.
- The chassis does NOT auto-inject an `aria-label` for icon-only use — see `Purpose`.