+ );
+}
diff --git a/frontend/src/v1/components/ui/toggles/README.md b/frontend/src/v1/components/ui/toggles/README.md
new file mode 100644
index 0000000000..5aa49c017c
--- /dev/null
+++ b/frontend/src/v1/components/ui/toggles/README.md
@@ -0,0 +1,181 @@
+# Toggles
+
+The one way v1 shows a checkbox, a switch, a radio group and a slider
+(P1-T05, UN-4300). Import everything from `@v1/components/ui/toggles`:
+
+```tsx
+import {
+ Checkbox,
+ Radio,
+ RadioGroup,
+ Slider,
+ Switch,
+} from "@v1/components/ui/toggles";
+```
+
+| Component | Use it for | Size |
+| --- | --- | --- |
+| `Checkbox` | An option that is on or off and takes effect on save; "select all" (mixed) | 16 × 16 box |
+| `Switch` | A setting that takes effect at once | 20 × 36, 16 thumb |
+| `RadioGroup` + `Radio` | One choice from a few options, all visible | 14 radio |
+| `Slider` | A number, or a range of two, within known bounds | 6 track, 16 thumb |
+
+## Rules
+
+- **Sizes come from the dev-spec board (§ 5).** Checkbox `ui:size-4`, radio
+ `ui:size-3.5`, switch `ui:h-5 ui:w-9` with a `ui:size-4` thumb, slider
+ track `ui:h-1.5` and thumb `ui:size-4`. Do not resize them with classes;
+ `className` sets the outermost element (the row of a `Checkbox`, `Switch`
+ or `Radio`; the whole field of a `RadioGroup` or `Slider`), never the
+ control. Each control sits in a 20px box, the label's line height, so it
+ centres on the label's first line.
+- **One focus treatment.** Every focusable part uses
+ `ui:focus-visible:focus-ring`. Disabled follows board § 6: opacity .5 and
+ no pointer events, on the control and its label (a disabled `RadioGroup`
+ dims its heading and every item label too).
+- **Mixed is controlled only.** Pass `checked="indeterminate"`; the box then
+ shows a minus. `defaultChecked` is `boolean` here on purpose: the icon is
+ chosen from `checked`, so an uncontrolled mixed start would show a tick
+ while reporting mixed. The icon is chosen in JS, never hidden with CSS.
+- **Labels and descriptions.** Pass `label` and `description` rather than
+ placing text beside a control: the label is tied by `htmlFor`, the
+ description by `aria-describedby`, and both use the v1 type roles. A
+ control with no visible label needs `aria-label`. A `RadioGroup` needs a
+ name: `label` (used as `aria-labelledby`) or `aria-label`. Every slider
+ thumb needs a name: see `Slider` below. A `label` or `description` of
+ `false`, `""`, `null` or `undefined` counts as absent.
+- **The checkbox icon is the Indicator itself.** v0's global rule forces any
+ `svg.lucide` whose parent is a `span`, `p`, `label`, `li`, `a`, `td` or
+ `th` to `1em` (see the v1 README, Styling), and it beats `ui:size-*`. So
+ the Radix `Indicator` renders `asChild` onto the lucide svg, whose parent
+ is then the `button`. Never wrap a lucide icon in a `span` in this folder.
+- **The row is private.** Label and description are drawn by `ControlRow`
+ with the same text classes as `Label` and `HelperText` in the P1-T03 form
+ components. Do not import `ControlRow`; it is not exported.
+- **Not inside `Field` yet (P1-T03 / P1-T06).** `Field` is a vertical grid
+ whose required `label` renders above the control as `