# leptos_aria > Headless accessible components for Leptos, ported from React Aria with the Radix/shadcn primitive surface completed. No styles ship; state is exposed as `data-*` attributes for Tailwind v4. A shadcn-style CLI copies styled wrapper components into your project. Install the library, install the CLI, add components: ``` cargo add leptos_aria cargo install leptos_aria_cli leptos-aria add button ``` ## Docs - [Tailwind v4 guide](https://ifiokjr.github.io/leptos_aria/tailwind.html): styling states with `data-[state=open]` variants and `@custom-variant` blocks, plus the full data-attribute contract - [CLI guide](https://ifiokjr.github.io/leptos_aria/cli.html): `init`, `add`, `diff`, `build`, `leptos-aria.json`, theming, and hosting your own registry - [Components](https://ifiokjr.github.io/leptos_aria/components.html): every styled registry component with install command, usage, and source - [Registry index](https://ifiokjr.github.io/leptos_aria/r/registry.json): the shadcn-schema registry the CLI reads; each item is at `r/.json` - [Porting roadmap](https://github.com/ifiokjr/leptos_aria/blob/main/PORTING.md): every upstream hook, utility, and component with its crate - [API docs](https://docs.rs/leptos_aria): the facade crate on docs.rs - [Full documentation](https://ifiokjr.github.io/leptos_aria/llms-full.txt): every document above in one file ## The data-attribute contract Every interactive component exposes its state so styling stays external: - `data-state`: `open`/`closed` (Modal, Popover, Tooltip, AlertDialog, HoverCard, ContextMenu, Accordion), `checked`/`unchecked` (Switch), `on`/`off` (ToggleGroup), `selected` (Calendar, Tabs, ListBox), `loaded`/`fallback` (Avatar) - `data-disabled`, `data-dragging`, `data-is-dragging`, `data-current`, `data-orientation`, `data-action`, `data-focused`, `data-expanded` Tailwind v4's built-in `data-[...]` and `aria-*` variants target these directly with no plugin. ## Component surface Interactions: usePress, useFocus, useFocusWithin, useHover, useFocusable, useFocusRing, useKeyboard, useLongPress, useMove, useDrag/useDrop, useScroll, useDismiss, useModal, FocusManager, useAutofocus. Components: Button, AnchorButton, CustomButton, ToggleButton, ToggleGroup, Switch, Form, TextField, TextArea, SearchField, NumberField, OneTimePasswordField, Checkbox, CheckboxGroup, RadioGroup, Slider, ComboBox, SelectBox, ListBox, Menu, MenuTrigger, ContextMenu, Menubar, Tabs, TagGroup, Toolbar, Tooltip, Popover, Modal, AlertDialog, Dialog, HoverCard, Calendar, DatePicker, DateField, DateRangePicker, TimeField, FileTrigger, Grid, Table, Tree, Meter, ProgressBar, Toast, DropZone, DraggableItem, Link, Breadcrumb, Heading, Text, VisuallyHidden, Disclosure, Accordion, Pagination, ScrollArea, Separator, AspectRatio, Avatar, ColorField, ColorArea, ColorSlider, ColorWheel, ColorSwatch. ## Registry CLI A shadcn/ui-style CLI. `leptos-aria init` writes `leptos-aria.json`, the theme CSS variables, and the `cn` helper. `leptos-aria add ` copies a Tailwind-styled component into `src/components/ui` with its dependencies. `list`, `view`, and `diff` read the registry, and `build` turns your own `registry.json` into a hostable registry. Items are served at `https://ifiokjr.github.io/leptos_aria/r/.json` in shadcn's registry-item schema. --- # Full documentation ## readme # leptos_aria > Accessible by default. Styled by you. `leptos_aria` ports the [React Aria](https://react-spectrum.adobe.com/react-aria/) ecosystem to [Leptos](https://leptos.dev). It brings React Aria's keyboard, focus, and screen-reader behaviour and ships no CSS: every state is exposed as a `data-*` attribute, so Tailwind v4 styles it with the classes you already write. [Website](https://ifiokjr.github.io/leptos_aria/) · [Tailwind guide](https://ifiokjr.github.io/leptos_aria/tailwind.html) · [Porting roadmap](PORTING.md) · [API docs](https://docs.rs/leptos_aria) ## Install ```bash cargo add leptos_aria ``` The `leptos_aria` crate re-exports every component, hook, and utility. Each component family also ships as its own `leptos_aria_*` crate if you want a smaller dependency. On a nightly toolchain, enable the `nightly` feature — `cargo add leptos_aria --features nightly` — to turn on `leptos`'s nightly-only signal and renderer optimizations. The feature is additive and a no-op on stable compilers. ## Style with Tailwind v4 Components are headless. Pass classes with each component's `class` prop and target state with Tailwind's built-in `data-[...]` and `aria-*` variants: ```rust view! { "Is it accessible?" "By default." } ``` See [docs/tailwind.md](docs/tailwind.md) for the full attribute contract and `@custom-variant` recipes. ## Copy styled components with the CLI ```bash cargo install leptos_aria_cli # or `cargo binstall leptos_aria_cli` for a prebuilt binary leptos-aria init # leptos-aria.json, theme CSS variables, the `cn` helper leptos-aria add button dialog # copy components into src/components/ui leptos-aria diff # compare your copies with the registry ``` The same workflow as shadcn/ui: copied components are yours to edit, with Tailwind classes on top and the headless crates underneath. Browse the [components](https://ifiokjr.github.io/leptos_aria/components.html) or read the [CLI guide](docs/cli.md), which also covers theming and hosting your own registry. The registry's source is the [`registry`](registry) crate, compiled and tested in CI so every component builds exactly as you receive it. ## Contributing [`devenv`](https://devenv.sh/) provides a reproducible development environment. Follow its [getting started guide](https://devenv.sh/getting-started/), then [install direnv](https://devenv.sh/automatic-shell-activation/) and allow this repository's `.envrc`: ```bash direnv allow . ``` Editor configuration for VS Code and Helix is available with `setup:vscode` and `setup:helix`. These scripts are available inside the devenv shell: ```bash build:all # build every crate for the wasm32 target fix:all # run dprint fmt and cargo clippy --fix lint:all # run dprint check and cargo clippy test:all # run the native and wasm32 browser test suites test:wasm # run the browser test suite only test:e2e # build the site and run Playwright accessibility and keyboard tests site:build # render the website and its live previews into target/site ``` Browser tests run through `wasm-bindgen-test-runner` with a local Chrome driven by `chromedriver`; both come from the devenv shell. The `wasm-bindgen` dependency is pinned to the `wasm-bindgen-cli` version in the devenv lock so the runner and the crate always match. Playwright tests run against the real production website and compiled WASM examples, with no internal mocks. See [e2e/readme.md](e2e/readme.md) for coverage, focused runs, and failure traces. ### Releases The [release workflow](.github/workflows/release-pr.yml) runs `monochange run release` to prepare versions and changelogs. The hosted `CommitRelease` and `OpenReleaseRequest` steps authenticate with GitHub Actions OIDC, so the monochange GitHub App creates the verified release commits and manages the release pull request as `monochange[bot]`. Preserve that bot authorship when merging release pull requests. Use `monochange preview` for local release planning; package publication runs separately behind the protected `publisher` environment. ### The website The site at [ifiokjr.github.io/leptos_aria](https://ifiokjr.github.io/leptos_aria/) is built by the `site` binary in [`crates/leptos_aria_docs`](crates/leptos_aria_docs). It renders [`website/layout.html`](website/layout.html) around the landing page and the repository's own markdown (this readme, [docs/tailwind.md](docs/tailwind.md), and [PORTING.md](PORTING.md)), so the site, `llms-full.txt`, and the docs never drift. Static assets, including the logo, live in [`website/public`](website/public). Each component page frames a live preview of its usage example. [`crates/leptos_aria_preview`](crates/leptos_aria_preview) compiles every registry component's `//!` usage example into a small client-side app, which trunk builds into `preview/` with the registry's own Tailwind theme. The frame loads lazily, so the wasm is only fetched on component pages, when the preview scrolls into view. ## License [Unlicense](license): public domain. ## Tailwind v4 guide # Tailwind CSS v4 Every component is headless: no styles ship, and state is exposed as `data-*` attributes so Tailwind's arbitrary variants style it directly. No plugin, no configuration, no dependency — add the crate, style with the classes you already write. ## Style with arbitrary variants Tailwind v4 supports `data-[...]` variants out of the box. Every component takes a `class` prop, added after its built-in `leptos-aria-*` class on the element it renders. Pass a string, or a closure for classes that change: ```rust use leptos::prelude::*; use leptos_aria::Accordion; use leptos_aria::AccordionItem; use leptos_aria::AccordionPanel; use leptos_aria::AccordionTrigger; view! { "Section" "Content" } ``` ## Or name the variants once A short `@custom-variant` block in your CSS turns every state into a one-word variant: ```css @import "tailwindcss"; @custom-variant open (&:where([data-state="open"], [data-state="checked"], [data-state="on"])); @custom-variant closed (&:where([data-state="closed"], [data-state="unchecked"], [data-state="off"])); @custom-variant disabled- (*:disabled, &:where([data-disabled="true"])); ``` ```html