# rind-ui
> Headless React components: behaviour, accessibility and state, styled through data-* attributes.
Use rind-ui for React UI that must work with keyboard and screen readers when you own the styles. Every component ships a test driver (Testing Library and Playwright), so an agent can verify what it builds.
## Quick start
```sh
pnpm add @rind-ui/react # React 19+
pnpm add -D @rind-ui/testing # drivers, for tests
```
```tsx
import { Switch } from "@rind-ui/react"
Dark mode
```
```css
[data-scope="switch"][data-part="control"][data-state="checked"] { background: black; }
[data-focus-visible] { outline: 2px solid hotpink; }
```
```ts
import { getSwitch } from "@rind-ui/testing"
const dark = getSwitch(screen, { name: "Dark mode" })
await dark.toggle()
expect(await dark.isChecked()).toBe(true)
```
Rules: compose the documented parts; never re-implement focus, keyboard or ARIA behaviour; give every control and group an accessible name; style `data-*` attributes (focus with `[data-focus-visible]`). Each component page below lists its parts, props, attributes, keyboard and driver.
## Guides
- [Announcements](https://rind.watermelon-studio.it/docs/announcements.md): Some changes have no focus move to carry them — "3 results", "Copied", a streamed answer. Components announce them through the environment's `Announcer`; they never render their own live region.
- [Autosize](https://rind.watermelon-studio.it/docs/autosize.md): A textarea that takes the height of its content, used by Text Field's `autosize` and later by Prompt Input and Editable. Native first, measured only where the browser cannot do it.
- [Collections](https://rind.watermelon-studio.it/docs/collections.md): Listbox, Select, Combobox, Menu, Tabs, Tree View and every other component that shows a set of items share one collection layer in `@rind-ui/core`: a key-based model of the items, selection, navigation, typeahead and the props of the collection parts. A component spreads these pieces into its machine; it never re-implements them.
- [Colour model](https://rind.watermelon-studio.it/docs/color-model.md): The [Color Picker](https://raw.githubusercontent.com/WatermelonBros/rind-ui/main/docs/components/color-picker.md) and every one of its parts share one model, in `@rind-ui/core`: a colour is plain data — `{ space, channels, alpha }` in `rgb`, `hsl` or `hsb` — and every operation is a function, so a colour survives `structuredClone`, JSON and a server render.
- [control-label](https://rind.watermelon-studio.it/docs/control-label.md): The shared Label and Description of a labelable control (Checkbox, Switch, group items, radios).
- [Implementing field-control](https://rind.watermelon-studio.it/docs/field-control.md): How a form control works inside a Field. Every rind-ui form control (Text Field, Checkbox, Select, …) does this, and your own control can too: the Field then labels, describes, validates and disables it like a built-in one.
- [Focus and input modality](https://rind.watermelon-studio.it/docs/focus-and-modality.md): Every focusable part carries `data-focus` while focused and `data-focus-visible` only when the focus came from the keyboard or from assistive technology, so a theme draws the focus ring for keyboard and screen-reader users and never after a mouse click.
- [hidden-form-input](https://rind.watermelon-studio.it/docs/hidden-form-input.md): The shared behaviour that lets a control that is not a native form field (Checkbox, Switch, [Checkbox Group](https://raw.githubusercontent.com/WatermelonBros/rind-ui/main/docs/components/checkbox-group.md), [Radio Group](https://raw.githubusercontent.com/WatermelonBros/rind-ui/main/docs/components/radio-group.md), later Select) take part in forms. Each control renders one visually hidden native input as the last child of its Root, `aria-hidden` and out of the Tab order.
- [Collapsed content that find-in-page can reach](https://rind.watermelon-studio.it/docs/hidden-until-found.md): Collapsible content can render closed as `hidden="until-found"`: hidden from view and from assistive technology, but the browser's find-in-page (and fragment links) can still match it. When a match is found the browser fires `beforematch`; the component opens with reason `find-in-page` and marks the content `data-reveal="find-in-page"`.
- [Keyboard, composition and right-to-left](https://rind.watermelon-studio.it/docs/keyboard-and-rtl.md): Components read keys as **logical keys** — `NEXT`, `PREV`, `FIRST`, `LAST`, `PAGE_NEXT`, `PAGE_PREV`, `EXPAND`, `COLLAPSE`, `INCREASE`, `DECREASE`, `ACTIVATE`, `DISMISS`, `CONTEXT_MENU` — so direction, orientation and platform are handled once.
- [Labels](https://rind.watermelon-studio.it/docs/labels.md): Every rind-ui label follows the same rules, whether it is the standalone [Label](https://raw.githubusercontent.com/WatermelonBros/rind-ui/main/docs/components/label.md), a control's own Label part (Checkbox, Switch) or a Field's Label. The browser's native `