Skip to content

Commit 8c00f40

Browse files
committed
docs(Switch): restructure page to match component docs spec
Reorder sections to Usage → Anatomy → Examples → Recipes → Accessibility → FAQ, move Group and Indeterminate live demos out of Recipes into a new Examples section, correct the hidden input description to match the source (native checkbox input with role="switch"), split the data attribute table to reflect that Track and Thumb emit data-state but not data-disabled, and add a FAQ section.
1 parent efdd195 commit 8c00f40

File tree

1 file changed

+69
-61
lines changed
  • apps/docs/src/pages/components/forms

1 file changed

+69
-61
lines changed

apps/docs/src/pages/components/forms/switch.md

Lines changed: 69 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,12 @@ related:
1818

1919
# Switch
2020

21-
A headless switch component with dual-mode support: standalone boolean binding or group multi-selection with tri-state.
21+
A switch for on/off state or multi-selection groups with tri-state support.
2222

2323
<DocsPageFeatures :frontmatter />
2424

2525
## Usage
2626

27-
The Switch component supports two modes:
28-
29-
- **Standalone mode**: Use `v-model` on `Switch.Root` for simple boolean state
30-
- **Group mode**: Wrap in `Switch.Group` for multi-selection with array v-model
31-
3227
::: example
3328
/components/switch/basic
3429

@@ -94,51 +89,40 @@ A standalone boolean switch with label and slide animation.
9489
</template>
9590
```
9691

97-
## Accessibility
98-
99-
The Switch.Root component renders as a button and handles all ARIA attributes automatically:
100-
101-
- `role="switch"` for proper semantics
102-
- `aria-checked` reflects state (`true`, `false`, or `"mixed"`)
103-
- `aria-disabled` when switch is disabled
104-
- `aria-label` from the `label` prop
105-
- `tabindex="0"` for keyboard focus (removed when disabled)
106-
- Space key toggles the switch
107-
108-
For custom implementations, use `renderless` mode and bind the `attrs` slot prop to your element:
92+
## Examples
10993

110-
```vue
111-
<template>
112-
<Switch.Root v-slot="{ attrs }" renderless>
113-
<div v-bind="attrs">
114-
<!-- Custom switch visual -->
115-
</div>
116-
</Switch.Root>
117-
</template>
118-
```
94+
::: example
95+
/components/switch/group
11996

120-
## Recipes
97+
### Switch Group
12198

122-
### Group Mode
99+
Multi-select switch group managing an array of connectivity options (WiFi, Bluetooth, Location).
123100

124-
Wrap switches in `Switch.Group` for multi-selection with array-based v-model:
101+
:::
125102

126103
::: example
127-
/components/switch/group
104+
/components/switch/indeterminate
128105

129-
### Switch Group
106+
### Select-All Switch
130107

131-
Multi-select switch group managing an array of connectivity options (WiFi, Bluetooth, Location).
108+
A "select all" switch with indeterminate state over nested permission toggles.
132109

133110
:::
134111

112+
The `SelectAll` component:
113+
- Binds to the group's `isAllSelected` and `isMixed` state
114+
- Calls `toggleAll` on click
115+
- Does NOT register as a group item
116+
- Sets `aria-checked="mixed"` and `data-state="indeterminate"` when partially selected
117+
118+
## Recipes
119+
135120
### Form Integration
136121

137-
When the `name` prop is provided on `Switch.Root`, a hidden native checkbox is automatically rendered for form submission:
122+
Pass the `name` prop on `Switch.Root` and a hidden native `<input type="checkbox">` is rendered automatically — no `Switch.HiddenInput` placement is required. The input is visually hidden, `inert`, and `tabindex="-1"`, so it only participates in `FormData` submission:
138123

139124
```vue
140125
<template>
141-
<!-- Auto-renders hidden input for form submission -->
142126
<Switch.Root name="notifications" value="on">
143127
<Switch.Track>
144128
<Switch.Thumb />
@@ -147,56 +131,80 @@ When the `name` prop is provided on `Switch.Root`, a hidden native checkbox is a
147131
</template>
148132
```
149133

150-
For custom form integration, use `Switch.HiddenInput` explicitly:
134+
Place `Switch.HiddenInput` explicitly only when you need to override the auto-rendered name, value, or form association:
151135

152136
```vue
153137
<template>
154-
<Switch.Root>
138+
<Switch.Root name="notifications">
155139
<Switch.Track>
156140
<Switch.Thumb />
157141
</Switch.Track>
158142
159-
<Switch.HiddenInput name="custom" value="override" />
143+
<Switch.HiddenInput name="notifications_override" value="custom" />
160144
</Switch.Root>
161145
</template>
162146
```
163147

164-
### Indeterminate State
165-
166-
Use `Switch.SelectAll` within a group for "select all" patterns. It automatically reflects the group's aggregate state and toggles all items on click:
167-
168-
::: example
169-
/components/switch/indeterminate
170-
171-
### Select-All Switch
172-
173-
A "select all" switch with indeterminate state over nested permission toggles.
174-
175-
:::
176-
177-
The `SelectAll` component:
178-
- Binds to the group's `isAllSelected` and `isMixed` state
179-
- Calls `toggleAll` on click
180-
- Does NOT register as a group item
181-
- Sets `aria-checked="mixed"` and `data-state="indeterminate"` when partially selected
182-
183148
### Styling with Data Attributes
184149

185-
Switch components expose `data-state` attributes for CSS styling:
150+
Switch subcomponents expose data attributes for CSS styling without conditional classes. `Switch.Root` and `Switch.SelectAll` emit both `data-state` and `data-disabled`, while `Switch.Track` and `Switch.Thumb` emit only `data-state` (they inherit disabled styling from the Root ancestor):
186151

187152
| Attribute | Values | Components |
188153
|-----------|--------|------------|
189-
| `data-state` | `checked`, `unchecked`, `indeterminate` | Root, Track, Thumb |
190-
| `data-disabled` | `true` | Root |
154+
| `data-state` | `checked`, `unchecked`, `indeterminate` | `Root`, `SelectAll`, `Track`, `Thumb` |
155+
| `data-disabled` | `true` | `Root`, `SelectAll` |
191156

192157
```vue
193158
<template>
194-
<Switch.Root class="...">
159+
<Switch.Root class="data-[disabled]:opacity-50">
195160
<Switch.Track class="bg-gray-300 data-[state=checked]:bg-primary">
196161
<Switch.Thumb class="translate-x-0.5 data-[state=checked]:translate-x-5.5" />
197162
</Switch.Track>
198163
</Switch.Root>
199164
</template>
200165
```
201166

167+
## Accessibility
168+
169+
The Switch.Root component renders as a button and handles all ARIA attributes automatically:
170+
171+
- `role="switch"` for proper semantics
172+
- `aria-checked` reflects state (`true`, `false`, or `"mixed"`)
173+
- `aria-disabled` when switch is disabled
174+
- `aria-label` from the `label` prop
175+
- `tabindex="0"` for keyboard focus (removed when disabled)
176+
- Space key toggles the switch
177+
178+
For custom implementations, use `renderless` mode and bind the `attrs` slot prop to your element:
179+
180+
```vue
181+
<template>
182+
<Switch.Root v-slot="{ attrs }" renderless>
183+
<div v-bind="attrs">
184+
<!-- Custom switch visual -->
185+
</div>
186+
</Switch.Root>
187+
</template>
188+
```
189+
190+
::: faq
191+
192+
??? When should I use Switch vs Checkbox?
193+
194+
Use `Switch` for settings that take immediate effect, like toggling a feature on or off (WiFi, notifications, dark mode). Use `Checkbox` for selections that are committed later — form submissions, multi-select lists, and "I agree" confirmations. The ARIA roles (`switch` vs `checkbox`) communicate this intent to assistive technology.
195+
196+
??? Why does my form submission miss the switch value?
197+
198+
`Switch.Root` only renders the hidden native input when a `name` prop is set. Without `name`, the switch is purely visual and won't appear in `FormData`. Add `name="myField"` (and optionally `value`) to participate in form submission.
199+
200+
??? How do I animate the thumb sliding?
201+
202+
Apply a CSS `transition` to `Switch.Thumb` (or `Switch.Track`) and use the `data-[state=checked]:` variant to change its transform. For example, `class="transition-transform data-[state=checked]:translate-x-5"` slides the thumb when toggled. No JavaScript event handling is needed — the data attribute flip drives the animation.
203+
204+
??? Can I use Switch.Root without the Track and Thumb subcomponents?
205+
206+
Yes. `Switch.Track` and `Switch.Thumb` are purely cosmetic — they read switch state from context to render the rail and knob. You can omit them entirely and render your own visual using the `attrs` slot prop on `Switch.Root`, or use `renderless` mode for full control over the rendered element.
207+
208+
:::
209+
202210
<DocsApi />

0 commit comments

Comments
 (0)