An accordion is a vertically stacked set of interactive headings containing a title, content snippet, or thumbnail representing a section of content. ## Resources [Latest version: v1.35.2](https://www.npmjs.com/package/@zag-js/accordion) [Logic Visualizer](https://zag-visualizer.vercel.app/accordion) [Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/accordion) **Features** - Full keyboard navigation - Supports single and multiple expanded items - Supports collapsible items - Supports horizontal and vertical orientation ## Installation Install the accordion package: ```bash npm install @zag-js/accordion @zag-js/solid # or yarn add @zag-js/accordion @zag-js/solid ``` ## Anatomy Check the accordion anatomy and part names. > Each part includes a `data-part` attribute to help identify them in the DOM. ## Usage Import the accordion package: ```jsx import * as accordion from "@zag-js/accordion" ``` The accordion package exports two key functions: - `machine` - State machine logic. - `connect` - Maps machine state to JSX props and event handlers. > Pass a unique `id` to `useMachine` so generated element ids stay predictable. Then use the framework integration helpers: ```jsx import * as accordion from "@zag-js/accordion" import { normalizeProps, useMachine } from "@zag-js/solid" import { createMemo, createUniqueId, For } from "solid-js" const data = [ { title: "Watercraft", content: "Sample accordion content" }, { title: "Automobiles", content: "Sample accordion content" }, { title: "Aircraft", content: "Sample accordion content" }, ] function Accordion() { const service = useMachine(accordion.machine, { id: createUniqueId() }) const api = createMemo(() => accordion.connect(service, normalizeProps)) return (
{(item) => (

{item.content}
)}
) } ``` You may have noticed we wrapped each accordion trigger within an `h3`. This is recommended by the [WAI-ARIA](https://www.w3.org/TR/wai-aria-practices-1.1/#wai-aria-roles-states-and-properties) design pattern to ensure the accordion has the appropriate hierarchy on the page. ### Opening multiple items Set `multiple` to `true` to allow more than one expanded item at a time. ```jsx {2} const service = useMachine(accordion.machine, { multiple: true, }) ``` ### Setting the initial value Set `defaultValue` to define expanded items on first render. ```jsx // multiple mode const service = useMachine(accordion.machine, { multiple: true, defaultValue: ["home", "about"], }) // single mode const service = useMachine(accordion.machine, { defaultValue: ["home"], }) ``` ### Controlled accordions Use `value` and `onValueChange` to control expanded items externally. ```tsx import { createSignal } from "solid-js" export function ControlledAccordion() { const [value, setValue] = createSignal(["home"]) const service = useMachine(accordion.machine, { get value() { return value() }, onValueChange(details) { setValue(details.value) }, }) return ( // ... ) } ``` ### Making items collapsible Set `collapsible` to `true` to allow closing an expanded item by clicking it again. > Note: If `multiple` is `true`, we internally set `collapsible` to be `true`. ```jsx {2} const service = useMachine(accordion.machine, { collapsible: true, }) ``` ### Listening for value changes When the accordion value changes, the `onValueChange` callback is invoked. ```jsx {2-5} const service = useMachine(accordion.machine, { onValueChange(details) { // details => { value: string[] } console.log("selected accordion:", details.value) }, }) ``` ### Listening for focus changes Use `onFocusChange` to react when keyboard focus moves between item triggers. ```jsx const service = useMachine(accordion.machine, { onFocusChange(details) { // details => { value: string | null } console.log("focused item:", details.value) }, }) ``` ### Horizontal orientation Set `orientation` to `horizontal` when rendering items side by side. ```jsx {2} const service = useMachine(accordion.machine, { orientation: "horizontal", }) ``` ### Disabling an accordion item To disable a specific accordion item, pass the `disabled: true` property to the `getItemProps`, `getItemTriggerProps` and `getItemContentProps`. When an accordion item is disabled, it is skipped from keyboard navigation and can't be interacted with. ```jsx //...

Content
//... ``` You can also disable the entire accordion by setting `disabled` on the machine. ```jsx {2} const service = useMachine(accordion.machine, { disabled: true, }) ``` ## Styling guide Each part includes a `data-part` attribute you can target in CSS. ### Open and closed state When an accordion item expands or collapses, `data-state` is set to `open` or `closed` on the item, trigger, and content elements. ```css [data-part="item"][data-state="open|closed"] { /* styles for the item is open or closed state */ } [data-part="item-trigger"][data-state="open|closed"] { /* styles for the item is open or closed state */ } [data-part="item-content"][data-state="open|closed"] { /* styles for the item is open or closed state */ } ``` ### Focused state When an accordion item's trigger is focused, a `data-focus` attribute is set on the item and content. ```css [data-part="item"][data-focus] { /* styles for the item's focus state */ } [data-part="item-trigger"]:focus { /* styles for the trigger's focus state */ } [data-part="item-content"][data-focus] { /* styles for the content's focus state */ } ``` ## Creating a component Create your accordion component by abstracting the machine into your own component. ### Usage ```tsx import { Accordion } from "./your-accordion" function Demo() { return ( ) } ``` ### Implementation Use the `splitProps` utility to separate the machine's props from the component's props. ```tsx import * as accordion from "@zag-js/accordion" import { normalizeProps, useMachine } from "@zag-js/solid" import { mergeProps, splitProps, createMemo, createUniqueId, For, JSX, } from "solid-js" interface Item { value: string title: JSX.Element content: JSX.Element } export interface AccordionProps extends Omit { items: Item[] } export function Accordion(props: AccordionProps) { const [machineProps, localProps] = splitProps(props, accordion.props) const context = mergeProps(machineProps, { id: createUniqueId() }) const service = useMachine(accordion.machine, context) const api = createMemo(() => accordion.connect(service, normalizeProps)) return (
{(item) => (

{item.content}
)}
) } ``` ## Methods and Properties The accordion's `api` exposes the following methods and properties: ### Machine Context The accordion machine exposes the following context properties: **`ids`** Type: `Partial<{ root: string; item: (value: string) => string; itemContent: (value: string) => string; itemTrigger: (value: string) => string; }>` Description: The ids of the elements in the accordion. Useful for composition. **`multiple`** Type: `boolean` Description: Whether multiple accordion items can be expanded at the same time. **`collapsible`** Type: `boolean` Description: Whether an accordion item can be closed after it has been expanded. **`value`** Type: `string[]` Description: The controlled value of the expanded accordion items. **`defaultValue`** Type: `string[]` Description: The initial value of the expanded accordion items. Use when you don't need to control the value of the accordion. **`disabled`** Type: `boolean` Description: Whether the accordion items are disabled **`onValueChange`** Type: `(details: ValueChangeDetails) => void` Description: The callback fired when the state of expanded/collapsed accordion items changes. **`onFocusChange`** Type: `(details: FocusChangeDetails) => void` Description: The callback fired when the focused accordion item changes. **`orientation`** Type: `"horizontal" | "vertical"` Description: The orientation of the accordion items. **`dir`** Type: `"ltr" | "rtl"` Description: The document's text/writing direction. **`id`** Type: `string` Description: The unique identifier of the machine. **`getRootNode`** Type: `() => ShadowRoot | Node | Document` Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron. ### Machine API The accordion `api` exposes the following methods: **`focusedValue`** Type: `string` Description: The value of the focused accordion item. **`value`** Type: `string[]` Description: The value of the accordion **`setValue`** Type: `(value: string[]) => void` Description: Sets the value of the accordion **`getItemState`** Type: `(props: ItemProps) => ItemState` Description: Returns the state of an accordion item. ### Data Attributes **`Root`** **`data-scope`**: accordion **`data-part`**: root **`data-orientation`**: The orientation of the accordion **`Item`** **`data-scope`**: accordion **`data-part`**: item **`data-state`**: "open" | "closed" **`data-focus`**: Present when focused **`data-disabled`**: Present when disabled **`data-orientation`**: The orientation of the item **`ItemContent`** **`data-scope`**: accordion **`data-part`**: item-content **`data-state`**: "open" | "closed" **`data-disabled`**: Present when disabled **`data-focus`**: Present when focused **`data-orientation`**: The orientation of the item **`ItemIndicator`** **`data-scope`**: accordion **`data-part`**: item-indicator **`data-state`**: "open" | "closed" **`data-disabled`**: Present when disabled **`data-focus`**: Present when focused **`data-orientation`**: The orientation of the item **`ItemTrigger`** **`data-scope`**: accordion **`data-part`**: item-trigger **`data-controls`**: **`data-orientation`**: The orientation of the item **`data-state`**: "open" | "closed" ## Accessibility ### Keyboard Interactions **`Space`** Description: When focus is on an trigger of a collapsed item, the item is expanded **`Enter`** Description: When focus is on an trigger of a collapsed section, expands the section. **`Tab`** Description: Moves focus to the next focusable element **`Shift + Tab`** Description: Moves focus to the previous focusable element **`ArrowDown`** Description: Moves focus to the next trigger **`ArrowUp`** Description: Moves focus to the previous trigger. **`Home`** Description: When focus is on an trigger, moves focus to the first trigger. **`End`** Description: When focus is on an trigger, moves focus to the last trigger. An angle slider is a circular dial that allows users to select an angle, typically in degrees, within a 360° range. It provides an intuitive way to control rotations or orientations, offering accessibility features. ## Resources [Latest version: v1.35.2](https://www.npmjs.com/package/@zag-js/angle-slider) [Logic Visualizer](https://zag-visualizer.vercel.app/angle-slider) [Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/angle-slider) **Features** - Fully managed keyboard navigation - Supports touch or click on the track to update value - Supports right-to-left direction ## Installation Install the angle slider package: ```bash npm install @zag-js/angle-slider @zag-js/solid # or yarn add @zag-js/angle-slider @zag-js/solid ``` ## Anatomy To set up the angle slider correctly, you'll need to understand its anatomy and how we name its parts. > Each part includes a `data-part` attribute to help identify them in the DOM. ## Usage Import the angle-slider package: ```jsx import * as angleSlider from "@zag-js/angle-slider" ``` The angle slider package exports two key functions: - `machine` - State machine logic. - `connect` - Maps machine state to JSX props and event handlers. > Pass a unique `id` to `useMachine` so generated element ids stay predictable. Then use the framework integration helpers: ```jsx import * as angleSlider from "@zag-js/angle-slider" import { normalizeProps, useMachine } from "@zag-js/solid" import { createMemo, createUniqueId, Index } from "solid-js" export function AngleSlider() { const service = useMachine(angleSlider.machine, { id: createUniqueId() }) const api = createMemo(() => angleSlider.connect(service, normalizeProps)) return (
{(value) => (
)}
) } ``` ### Setting the initial value Set `defaultValue` to define the initial slider value. ```jsx {2} const service = useMachine(angleSlider.machine, { defaultValue: 45, }) ``` ### Controlled angle slider Use `value` and `onValueChange` to control the value externally. ```tsx import { createSignal } from "solid-js" export function ControlledAngleSlider() { const [value, setValue] = createSignal(45) const service = useMachine(angleSlider.machine, { get value() { return value() }, onValueChange(details) { setValue(details.value) }, }) return ( // ... ) } ``` ### Setting the value's granularity By default, `step` is `1`, so values move in whole-number increments. Set `step` to control granularity. For example, set `step` to `0.01` for two-decimal precision: ```jsx {2} const service = useMachine(angleSlider.machine, { step: 0.01, }) ``` ### Listening for changes When the angle slider value changes, the `onValueChange` and `onValueChangeEnd` callbacks are invoked. ```jsx {2-7} const service = useMachine(angleSlider.machine, { onValueChange(details) { console.log("value:", details.value) console.log("as degree:", details.valueAsDegree) }, onValueChangeEnd(details) { console.log("final value:", details.value) }, }) ``` ### Read-only mode Set `readOnly` to prevent updates while preserving focus and form semantics. ```jsx {2} const service = useMachine(angleSlider.machine, { readOnly: true, }) ``` ### Usage in forms To submit the value with a form: - Set `name` on the machine. - Render the hidden input from `api.getHiddenInputProps()`. ```jsx {2} const service = useMachine(angleSlider.machine, { name: "wind-direction", }) ``` ### Labeling the thumb for assistive tech Use `aria-label` or `aria-labelledby` when you need custom labeling. ```jsx {2} const service = useMachine(angleSlider.machine, { "aria-label": "Wind direction", }) ``` ### Using angle slider marks To show marks or ticks along the angle slider track, use the exposed `api.getMarkerProps()` method to position the angle slider marks at desired angles. ```jsx {7-11} //...
{[0, 45, 90, 135, 180, 225, 270, 315].map((value) => (
))}
{api().value} degrees
//... ``` ## Styling guide Each part includes a `data-part` attribute you can target in CSS. ### Disabled State When the angle slider is disabled, the `data-disabled` attribute is added to the root, label, control, thumb and marker. ```css [data-part="root"][data-disabled] { /* styles for root disabled state */ } [data-part="label"][data-disabled] { /* styles for label disabled state */ } [data-part="control"][data-disabled] { /* styles for control disabled state */ } [data-part="thumb"][data-disabled] { /* styles for thumb disabled state */ } [data-part="range"][data-disabled] { /* styles for thumb disabled state */ } ``` ### Invalid State When the slider is invalid, the `data-invalid` attribute is added to the root, track, range, label, and thumb parts. ```css [data-part="root"][data-invalid] { /* styles for root invalid state */ } [data-part="label"][data-invalid] { /* styles for label invalid state */ } [data-part="control"][data-invalid] { /* styles for control invalid state */ } [data-part="valueText"][data-invalid] { /* styles for output invalid state */ } [data-part="thumb"][data-invalid] { /* styles for thumb invalid state */ } [data-part="marker"][data-invalid] { /* styles for marker invalid state */ } ``` ### Styling the markers ```css [data-part="marker"][data-state="(at|under|over)-value"] { /* styles for when the value exceeds the marker's value */ } ``` ## Methods and Properties ### Machine Context The angle slider machine exposes the following context properties: **`ids`** Type: `Partial<{ root: string; thumb: string; hiddenInput: string; control: string; valueText: string; label: string; }>` Description: The ids of the elements in the machine. Useful for composition. **`step`** Type: `number` Description: The step value for the slider. **`value`** Type: `number` Description: The value of the slider. **`defaultValue`** Type: `number` Description: The initial value of the slider. Use when you don't need to control the value of the slider. **`onValueChange`** Type: `(details: ValueChangeDetails) => void` Description: The callback function for when the value changes. **`onValueChangeEnd`** Type: `(details: ValueChangeDetails) => void` Description: The callback function for when the value changes ends. **`disabled`** Type: `boolean` Description: Whether the slider is disabled. **`readOnly`** Type: `boolean` Description: Whether the slider is read-only. **`invalid`** Type: `boolean` Description: Whether the slider is invalid. **`name`** Type: `string` Description: The name of the slider. Useful for form submission. **`aria-label`** Type: `string` Description: The accessible label for the slider thumb. **`aria-labelledby`** Type: `string` Description: The id of the element that labels the slider thumb. **`dir`** Type: `"ltr" | "rtl"` Description: The document's text/writing direction. **`id`** Type: `string` Description: The unique identifier of the machine. **`getRootNode`** Type: `() => ShadowRoot | Node | Document` Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron. ### Machine API The angle slider `api` exposes the following methods: **`value`** Type: `number` Description: The current value of the angle slider **`valueAsDegree`** Type: `string` Description: The current value as a degree string **`setValue`** Type: `(value: number) => void` Description: Sets the value of the angle slider **`dragging`** Type: `boolean` Description: Whether the slider is being dragged. ### Data Attributes **`Root`** **`data-scope`**: angle-slider **`data-part`**: root **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-readonly`**: Present when read-only **`Label`** **`data-scope`**: angle-slider **`data-part`**: label **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-readonly`**: Present when read-only **`Control`** **`data-scope`**: angle-slider **`data-part`**: control **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-readonly`**: Present when read-only **`Thumb`** **`data-scope`**: angle-slider **`data-part`**: thumb **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-readonly`**: Present when read-only **`Marker`** **`data-scope`**: angle-slider **`data-part`**: marker **`data-value`**: The value of the item **`data-state`**: **`data-disabled`**: Present when disabled ### CSS Variables ### Keyboard Interactions **`ArrowRight`** Description: Increments the angle slider based on defined step **`ArrowLeft`** Description: Decrements the angle slider based on defined step **`ArrowUp`** Description: Decreases the value by the step amount. **`ArrowDown`** Description: Increases the value by the step amount. **`Shift + ArrowUp`** Description: Decreases the value by a larger step **`Shift + ArrowDown`** Description: Increases the value by a larger step **`Home`** Description: Sets the value to 0 degrees. **`End`** Description: Sets the value to 360 degrees. An avatar represents a user profile picture. It displays an image or fallback content in a container. Avatar supports fallback text or elements when the image fails to load or when no image is provided. ## Resources [Latest version: v1.35.2](https://www.npmjs.com/package/@zag-js/avatar) [Logic Visualizer](https://zag-visualizer.vercel.app/avatar) [Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/avatar) ## Installation Install the avatar package: ```bash npm install @zag-js/avatar @zag-js/solid # or yarn add @zag-js/avatar @zag-js/solid ``` ## Anatomy Check the avatar anatomy and part names. > Each part includes a `data-part` attribute to help identify them in the DOM. ## Usage Import the avatar package: ```jsx import * as avatar from "@zag-js/avatar" ``` The avatar package exports two key functions: - `machine` - State machine logic. - `connect` - Maps machine state to JSX props and event handlers. > Pass a unique `id` to `useMachine` so generated element ids stay predictable. Then use the framework integration helpers: ```jsx import * as avatar from "@zag-js/avatar" import { useMachine, normalizeProps } from "@zag-js/solid" function Avatar() { const service = useMachine(avatar.machine, { id: "1" }) const api = createMemo(() => avatar.connect(service, normalizeProps)) return (
PA PA
) } ``` ### Listening for loading status changes When the image loads or fails, `onStatusChange` is invoked. ```jsx {2} const service = useMachine(avatar.machine, { onStatusChange(details) { // details => { status: "error" | "loaded" } }, }) ``` ### Updating the image source programmatically Use `api.setSrc` when the image source changes after mount. ```jsx api.setSrc(nextSrc) ``` ## Styling guide Each avatar part includes a `data-part` attribute you can target in CSS. ```css [data-scope="avatar"][data-part="root"] { /* Styles for the root part */ } [data-scope="avatar"][data-part="image"] { /* Styles for the image part */ } [data-scope="avatar"][data-part="fallback"] { /* Styles for the fallback part */ } ``` ## Creating a component Create your avatar component by abstracting the machine into your own component. ### Usage ### Implementation Use the `splitProps` utility to separate the machine's props from the component's props. ## Methods and Properties ### Machine Context The avatar machine exposes the following context properties: **`onStatusChange`** Type: `(details: StatusChangeDetails) => void` Description: Functional called when the image loading status changes. **`ids`** Type: `Partial<{ root: string; image: string; fallback: string; }>` Description: The ids of the elements in the avatar. Useful for composition. **`id`** Type: `string` Description: The unique identifier of the machine. **`getRootNode`** Type: `() => ShadowRoot | Node | Document` Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron. **`dir`** Type: `"ltr" | "rtl"` Description: The document's text/writing direction. ### Machine API The avatar `api` exposes the following methods: **`loaded`** Type: `boolean` Description: Whether the image is loaded. **`setSrc`** Type: `(src: string) => void` Description: Function to set new src. **`setLoaded`** Type: `VoidFunction` Description: Function to set loaded state. **`setError`** Type: `VoidFunction` Description: Function to set error state. ### Data Attributes **`Image`** **`data-scope`**: avatar **`data-part`**: image **`data-state`**: "visible" | "hidden" **`Fallback`** **`data-scope`**: avatar **`data-part`**: fallback **`data-state`**: "hidden" | "visible" A carousel component that leverages native CSS Scroll Snap for smooth, performant scrolling between slides. ## Resources [Latest version: v1.35.2](https://www.npmjs.com/package/@zag-js/carousel) [Logic Visualizer](https://zag-visualizer.vercel.app/carousel) [Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/carousel) **Features** - Uses native CSS Scroll Snap - Supports horizontal and vertical orientations - Supports slide alignment (`start`, `center`, `end`) - Supports showing multiple slides at a time - Supports looping and auto-playing - Supports custom spacing between slides ## Installation Install the carousel package: ```bash npm install @zag-js/carousel @zag-js/solid # or yarn add @zag-js/carousel @zag-js/solid ``` ## Anatomy Check the carousel anatomy and part names. > Each part includes a `data-part` attribute to help identify them in the DOM. ## Usage Import the carousel package: ```jsx import * as carousel from "@zag-js/carousel" ``` The carousel package exports two key functions: - `machine` - State machine logic. - `connect` - Maps machine state to JSX props and event handlers. > Pass a unique `id` to `useMachine` so generated element ids stay predictable. Then use the framework integration helpers: > **Note:** The carousel requires that you provide a `slideCount` property in > the machine context. This is the total number of slides. ```jsx import * as carousel from "@zag-js/carousel" import { normalizeProps, useMachine } from "@zag-js/solid" import { createMemo, createUniqueId, For } from "solid-js" const items = [ "https://tinyurl.com/5b6ka8jd", "https://tinyurl.com/7rmccdn5", "https://tinyurl.com/59jxz9uu", ] export function Carousel() { const service = useMachine(carousel.machine, { id: createUniqueId(), slideCount: items.length, }) const api = createMemo(() => carousel.connect(service, normalizeProps)) return (
{(image, index) => (
)}
{(_, index) =>
) } ``` ### Vertical orientation Set `orientation` to `vertical` to render a vertical carousel. ```jsx {2} const service = useMachine(carousel.machine, { orientation: "vertical", }) ``` ### Setting the initial slide Set `defaultPage` to define the initial page. > The `defaultPage` corresponds to the scroll snap position index based on the > layout. It does not necessarily correspond to the index of the slide in the > carousel. ```jsx {2} const service = useMachine(carousel.machine, { defaultPage: 2, }) ``` ### Controlling the current page Use `page` and `onPageChange` for controlled navigation. ```jsx const service = useMachine(carousel.machine, { slideCount: 8, page, onPageChange(details) { setPage(details.page) }, }) ``` ### Setting slides per page Set `slidesPerPage` to control how many slides are visible per page. ```jsx {2} const service = useMachine(carousel.machine, { slidesPerPage: 2, }) ``` ### Setting slides per move Set `slidesPerMove` to control how many slides advance on next/previous. ```jsx {2} const service = useMachine(carousel.machine, { slidesPerMove: 2, }) ``` **Considerations** - If the value is `auto`, the carousel will move the number of slides equal to the number of slides per page. - Ensure the `slidesPerMove` is less than or equal to the `slidesPerPage` to avoid skipping slides. - On touch devices, `slidesPerMove` is not enforced during active swiping. The browser's native scrolling and CSS Scroll Snap determine slide movement for optimal performance and UX. ### Looping pages Set `loop` to `true` to wrap around from last to first page. ```jsx {2} const service = useMachine(carousel.machine, { loop: true, }) ``` ### Setting the gap between slides Set `spacing` to control the gap between slides. ```jsx {2} const service = useMachine(carousel.machine, { spacing: "16px", }) ``` ### Setting viewport padding Set `padding` to keep neighboring slides partially visible. ```jsx {2} const service = useMachine(carousel.machine, { padding: "16px", }) ``` ### Variable-width slides Set `autoSize` to `true` when slides have different widths. ```jsx {2} const service = useMachine(carousel.machine, { autoSize: true, }) ``` ### Listening for page changes When the carousel page changes, the `onPageChange` callback is invoked. ```jsx {2-5} const service = useMachine(carousel.machine, { onPageChange(details) { // details => { page: number } console.log("selected page:", details.page) }, }) ``` ### Listening for drag and autoplay status Use status callbacks to react to dragging and autoplay lifecycle changes. ```jsx const service = useMachine(carousel.machine, { onDragStatusChange(details) { console.log(details.type, details.isDragging) }, onAutoplayStatusChange(details) { console.log(details.type, details.isPlaying) }, }) ``` ### Dragging the carousel Set `allowMouseDrag` to `true` to drag the carousel with a mouse. ```jsx {2} const service = useMachine(carousel.machine, { allowMouseDrag: true, }) ``` ### Autoplaying the carousel Set `autoplay` to `true` to start automatic slide movement. ```jsx {2} const service = useMachine(carousel.machine, { autoplay: true, }) ``` Alternatively, you can configure the autoplay interval by setting the `delay` property. ```jsx {2} const service = useMachine(carousel.machine, { autoplay: { delay: 2000 }, }) ``` ### Customizing accessibility messages Use `translations` to customize localized trigger, item, and progress text. ```jsx const service = useMachine(carousel.machine, { slideCount: 5, translations: { nextTrigger: "Next slide", prevTrigger: "Previous slide", indicator: (index) => `Go to slide ${index + 1}`, item: (index, count) => `Slide ${index + 1} of ${count}`, autoplayStart: "Start autoplay", autoplayStop: "Stop autoplay", progressText: ({ page, totalPages }) => `Page ${page + 1} of ${totalPages}`, }, }) ``` ## Styling guide Each part includes a `data-part` attribute you can target in CSS. ```css [data-part="root"] { /* styles for the root part */ } [data-part="item-group"] { /* styles for the item-group part */ } [data-part="item"] { /* styles for the root part */ } [data-part="control"] { /* styles for the control part */ } [data-part="next-trigger"] { /* styles for the next-trigger part */ } [data-part="prev-trigger"] { /* styles for the prev-trigger part */ } [data-part="indicator-group"] { /* styles for the indicator-group part */ } [data-part="indicator"] { /* styles for the indicator part */ } [data-part="autoplay-trigger"] { /* styles for the autoplay-trigger part */ } ``` ### Active state When a carousel's indicator is active, a `data-current` attribute is set on the indicator. ```css [data-part="indicator"][data-current] { /* styles for the indicator's active state */ } ``` ## Methods and Properties The carousel's `api` exposes the following methods and properties: ### Machine Context The carousel machine exposes the following context properties: **`ids`** Type: `Partial<{ root: string; item: (index: number) => string; itemGroup: string; nextTrigger: string; prevTrigger: string; indicatorGroup: string; indicator: (index: number) => string; }>` Description: The ids of the elements in the carousel. Useful for composition. **`translations`** Type: `IntlTranslations` Description: The localized messages to use. **`slidesPerPage`** Type: `number` Description: The number of slides to show at a time. **`autoSize`** Type: `boolean` Description: Whether to enable variable width slides. **`slidesPerMove`** Type: `number | "auto"` Description: The number of slides to scroll at a time. When set to `auto`, the number of slides to scroll is determined by the `slidesPerPage` property. **`autoplay`** Type: `boolean | { delay: number; }` Description: Whether to scroll automatically. The default delay is 4000ms. **`allowMouseDrag`** Type: `boolean` Description: Whether to allow scrolling via dragging with mouse **`loop`** Type: `boolean` Description: Whether the carousel should loop around. **`page`** Type: `number` Description: The controlled page of the carousel. **`defaultPage`** Type: `number` Description: The initial page to scroll to when rendered. Use when you don't need to control the page of the carousel. **`spacing`** Type: `string` Description: The amount of space between items. **`padding`** Type: `string` Description: Defines the extra space added around the scrollable area, enabling nearby items to remain partially in view. **`onPageChange`** Type: `(details: PageChangeDetails) => void` Description: Function called when the page changes. **`inViewThreshold`** Type: `number | number[]` Description: The threshold for determining if an item is in view. **`snapType`** Type: `"proximity" | "mandatory"` Description: The snap type of the item. **`slideCount`** Type: `number` Description: The total number of slides. Useful for SSR to render the initial ating the snap points. **`onDragStatusChange`** Type: `(details: DragStatusDetails) => void` Description: Function called when the drag status changes. **`onAutoplayStatusChange`** Type: `(details: AutoplayStatusDetails) => void` Description: Function called when the autoplay status changes. **`dir`** Type: `"ltr" | "rtl"` Description: The document's text/writing direction. **`id`** Type: `string` Description: The unique identifier of the machine. **`getRootNode`** Type: `() => ShadowRoot | Node | Document` Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron. **`orientation`** Type: `"horizontal" | "vertical"` Description: The orientation of the element. ### Machine API The carousel `api` exposes the following methods: **`page`** Type: `number` Description: The current index of the carousel **`pageSnapPoints`** Type: `number[]` Description: The current snap points of the carousel **`isPlaying`** Type: `boolean` Description: Whether the carousel is auto playing **`isDragging`** Type: `boolean` Description: Whether the carousel is being dragged. This only works when `draggable` is true. **`canScrollNext`** Type: `boolean` Description: Whether the carousel is can scroll to the next view **`canScrollPrev`** Type: `boolean` Description: Whether the carousel is can scroll to the previous view **`scrollToIndex`** Type: `(index: number, instant?: boolean) => void` Description: Function to scroll to a specific item index **`scrollTo`** Type: `(page: number, instant?: boolean) => void` Description: Function to scroll to a specific page **`scrollNext`** Type: `(instant?: boolean) => void` Description: Function to scroll to the next page **`scrollPrev`** Type: `(instant?: boolean) => void` Description: Function to scroll to the previous page **`getProgress`** Type: `() => number` Description: Returns the current scroll progress as a percentage **`getProgressText`** Type: `() => string` Description: Returns the progress text **`play`** Type: `VoidFunction` Description: Function to start/resume autoplay **`pause`** Type: `VoidFunction` Description: Function to pause autoplay **`isInView`** Type: `(index: number) => boolean` Description: Whether the item is in view **`refresh`** Type: `VoidFunction` Description: Function to re-compute the snap points and clamp the page ### Data Attributes **`Root`** **`data-scope`**: carousel **`data-part`**: root **`data-orientation`**: The orientation of the carousel **`ItemGroup`** **`data-scope`**: carousel **`data-part`**: item-group **`data-orientation`**: The orientation of the item **`data-dragging`**: Present when in the dragging state **`Item`** **`data-scope`**: carousel **`data-part`**: item **`data-index`**: The index of the item **`data-inview`**: Present when in viewport **`data-orientation`**: The orientation of the item **`Control`** **`data-scope`**: carousel **`data-part`**: control **`data-orientation`**: The orientation of the control **`PrevTrigger`** **`data-scope`**: carousel **`data-part`**: prev-trigger **`data-orientation`**: The orientation of the prevtrigger **`NextTrigger`** **`data-scope`**: carousel **`data-part`**: next-trigger **`data-orientation`**: The orientation of the nexttrigger **`IndicatorGroup`** **`data-scope`**: carousel **`data-part`**: indicator-group **`data-orientation`**: The orientation of the indicatorgroup **`Indicator`** **`data-scope`**: carousel **`data-part`**: indicator **`data-orientation`**: The orientation of the indicator **`data-index`**: The index of the item **`data-readonly`**: Present when read-only **`data-current`**: Present when current **`AutoplayTrigger`** **`data-scope`**: carousel **`data-part`**: autoplay-trigger **`data-orientation`**: The orientation of the autoplaytrigger **`data-pressed`**: Present when pressed ### CSS Variables A cascade select component allows users to select from hierarchical data through multiple linked levels of dropdown menus. ## Resources [Latest version: v1.35.2](https://www.npmjs.com/package/@zag-js/cascade-select) [Logic Visualizer](https://zag-visualizer.vercel.app/cascade-select) [Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/cascade-select) **Features** - Supports hierarchical data with unlimited depth levels - Full keyboard navigation across all levels with arrow keys - Supports single and multiple selections - Supports both click and hover triggering modes - Supports looping keyboard navigation - Built-in accessibility with ARIA roles and keyboard interactions - Supports disabled items and read-only state - Form integration with hidden input element - Supports right-to-left direction ## Installation Install the cascade select package: ```bash npm install @zag-js/cascade-select @zag-js/solid # or yarn add @zag-js/cascade-select @zag-js/solid ``` ## Anatomy Check the cascade select anatomy and part names. > Each part includes a `data-part` attribute to help identify them in the DOM. ## Usage Import the cascade select package: ```jsx import * as cascadeSelect from "@zag-js/cascade-select" ``` These are the key exports: - `machine` - State machine logic. - `connect` - Maps machine state to JSX props and event handlers. - `collection` - Creates a tree collection from your hierarchical data. ### Create the collection Use the `collection` function to create a tree collection from your hierarchical data. Pass a `rootNode` along with functions to extract each node's value, string label, and children. ```ts import * as cascadeSelect from "@zag-js/cascade-select" interface Node { label: string value: string children?: Node[] } const collection = cascadeSelect.collection({ nodeToValue: (node) => node.value, nodeToString: (node) => node.label, nodeToChildren: (node) => node.children, rootNode: { label: "ROOT", value: "ROOT", children: [ { label: "North America", value: "north-america", children: [ { label: "United States", value: "us", children: [ { label: "New York", value: "ny" }, { label: "California", value: "ca" }, ], }, { label: "Canada", value: "canada" }, ], }, { label: "Africa", value: "africa", children: [ { label: "Nigeria", value: "ng" }, { label: "Kenya", value: "ke" }, ], }, ], }, }) ``` ### Create the cascade select > Pass a unique `id` to `useMachine` so generated element ids stay predictable. Then use the framework integration helpers: ```tsx import * as cascadeSelect from "@zag-js/cascade-select" import { normalizeProps, useMachine } from "@zag-js/solid" import { Index, createMemo, createUniqueId, JSX } from "solid-js" import { Portal } from "solid-js/web" // 1. Create the collection (see above) const collection = cascadeSelect.collection({ // ... }) // 2. Create the recursive tree node interface TreeNodeProps { node: Node indexPath?: number[] value?: string[] api: cascadeSelect.Api } function TreeNode(props: TreeNodeProps): JSX.Element { const indexPath = () => props.indexPath ?? [] const value = () => props.value ?? [] const nodeProps = () => ({ indexPath: indexPath(), value: value(), item: props.node }) const nodeState = () => props.api.getItemState(nodeProps()) const children = () => collection.getNodeChildren(props.node) return ( <>
    {(item, index) => { const itemProps = () => ({ indexPath: [...indexPath(), index], value: [...value(), collection.getNodeValue(item())], item: item(), }) const itemState = () => props.api.getItemState(itemProps()) return (
  • {item().label} {itemState().hasChildren && }
  • ) }}
{nodeState().highlightedChild && collection.isBranchNode(nodeState().highlightedChild) && ( )} ) } // 3. Create the cascade select export function CascadeSelect() { const service = useMachine(cascadeSelect.machine, { id: createUniqueId(), collection, }) const api = createMemo(() => cascadeSelect.connect(service, normalizeProps)) return (
) } ``` ### Setting the initial value Use the `defaultValue` property to set the initial value of the cascade select. > The `value` property must be an array of string paths. Each path is an array > of values from the root to the selected leaf item. ```jsx {4} const service = useMachine(cascadeSelect.machine, { id: useId(), collection, defaultValue: [["north-america", "us", "ny"]], }) ``` ### Controlled selection Use `value` and `onValueChange` for controlled selection state. ```jsx const service = useMachine(cascadeSelect.machine, { id: useId(), collection, value, onValueChange(details) { setValue(details.value) }, }) ``` ### Selecting multiple values To allow selecting multiple values, set the `multiple` property to `true`. ```jsx {4} const service = useMachine(cascadeSelect.machine, { id: useId(), collection, multiple: true, }) ``` ### Hover triggering By default, items are highlighted when clicked. To highlight items on hover instead (like a traditional cascading menu), set the `highlightTrigger` property to `"hover"`. ```jsx {4} const service = useMachine(cascadeSelect.machine, { id: useId(), collection, highlightTrigger: "hover", }) ``` ### Allowing parent selection By default, only leaf nodes (items without children) can be selected. To allow parent (branch) nodes to also be selectable, set `allowParentSelection` to `true`. ```jsx {4} const service = useMachine(cascadeSelect.machine, { id: useId(), collection, allowParentSelection: true, }) ``` ### Close on select By default, the menu closes when you select an item. Set `closeOnSelect` to `false` to keep it open. ```jsx {4} const service = useMachine(cascadeSelect.machine, { id: useId(), collection, closeOnSelect: false, }) ``` ### Looping the keyboard navigation By default, arrow-key navigation stops at the first and last items. Set `loopFocus: true` to loop back around. ```jsx {4} const service = useMachine(cascadeSelect.machine, { id: useId(), collection, loopFocus: true, }) ``` ### Listening for highlight changes When an item is highlighted with the pointer or keyboard, use the `onHighlightChange` to listen for the change and do something with it. ```jsx {3-6} const service = useMachine(cascadeSelect.machine, { id: useId(), onHighlightChange(details) { // details => { highlightedValue: string[], highlightedItems: Item[] } console.log(details) }, }) ``` ### Setting the initial highlighted path Use `defaultHighlightedValue` to set the initially highlighted path. ```jsx const service = useMachine(cascadeSelect.machine, { id: useId(), collection, defaultHighlightedValue: ["north-america", "us"], }) ``` ### Controlled highlighted path Use `highlightedValue` and `onHighlightChange` to control highlighting externally. ```jsx const service = useMachine(cascadeSelect.machine, { id: useId(), collection, highlightedValue, onHighlightChange(details) { setHighlightedValue(details.highlightedValue) }, }) ``` ### Listening for selection changes When an item is selected, use the `onValueChange` property to listen for the change and do something with it. ```jsx {4-7} const service = useMachine(cascadeSelect.machine, { id: useId(), collection, onValueChange(details) { // details => { value: string[][], items: Item[][] } console.log(details) }, }) ``` ### Listening for open and close events Use `onOpenChange` to listen for open and close events. ```jsx {4-7} const service = useMachine(cascadeSelect.machine, { id: useId(), collection, onOpenChange(details) { // details => { open: boolean, value: string[][] } console.log(details) }, }) ``` ### Controlling open state Use `open` and `onOpenChange` for controlled open state, or `defaultOpen` for an uncontrolled initial state. ```jsx const service = useMachine(cascadeSelect.machine, { id: useId(), collection, open, onOpenChange({ open }) { setOpen(open) }, }) ``` ```jsx const service = useMachine(cascadeSelect.machine, { id: useId(), collection, defaultOpen: true, }) ``` ### Positioning submenu panels Use `positioning` to configure placement and collision behavior. ```jsx const service = useMachine(cascadeSelect.machine, { id: useId(), collection, positioning: { placement: "right-start", gutter: 4, }, }) ``` ### Custom scroll behavior Use `scrollToIndexFn` to customize how each level scrolls highlighted items into view. ```jsx const service = useMachine(cascadeSelect.machine, { id: useId(), collection, scrollToIndexFn(details) { // details => { index, depth, immediate? } customScroll(details) }, }) ``` ### Customizing the trigger label Use `formatValue` to control how selected paths are rendered in the trigger. ```jsx const service = useMachine(cascadeSelect.machine, { id: useId(), collection, formatValue(selectedItems) { return selectedItems .map((path) => path.map((item) => item.label).join(" / ")) .join(", ") }, }) ``` ### Usage within a form To use cascade select in a form, pass `name`. A hidden input is rendered with `getHiddenInputProps()`. ```jsx {4} const service = useMachine(cascadeSelect.machine, { id: useId(), collection, name: "location", }) // In your JSX ``` If the hidden input belongs to a different form element, also set `form`. ```jsx const service = useMachine(cascadeSelect.machine, { id: useId(), collection, name: "location", form: "checkout-form", }) ``` ### Validation and read-only state Use `readOnly`, `required`, and `invalid` to control interaction and form state. ```jsx const service = useMachine(cascadeSelect.machine, { id: useId(), collection, readOnly: true, required: true, invalid: false, }) ``` ## Styling guide Each cascade select part includes a `data-part` attribute you can target in CSS. ### Open and closed state When the cascade select is open, the trigger and content is given a `data-state` attribute. ```css [data-part="trigger"][data-state="open|closed"] { /* styles for open or closed state */ } [data-part="content"][data-state="open|closed"] { /* styles for open or closed state */ } ``` ### Selected state Items are given a `data-state` attribute, indicating whether they are selected. ```css [data-part="item"][data-state="checked|unchecked"] { /* styles for selected or unselected state */ } ``` ### Highlighted state When an item is highlighted, via keyboard navigation or pointer, it is given a `data-highlighted` attribute. ```css [data-part="item"][data-highlighted] { /* styles for highlighted state */ } ``` ### Branch items When an item has children (is a branch node), it is given a `data-has-children` attribute. ```css [data-part="item"][data-has-children] { /* styles for items with children (branch nodes) */ } ``` ### Invalid state When the cascade select is invalid, the label and trigger is given a `data-invalid` attribute. ```css [data-part="label"][data-invalid] { /* styles for invalid state */ } [data-part="trigger"][data-invalid] { /* styles for invalid state */ } ``` ### Disabled state When the cascade select is disabled, the trigger and label is given a `data-disabled` attribute. ```css [data-part="trigger"][data-disabled] { /* styles for disabled state */ } [data-part="label"][data-disabled] { /* styles for disabled label state */ } [data-part="item"][data-disabled] { /* styles for disabled item state */ } ``` ### Empty state When no option is selected, the trigger is given a `data-placeholder-shown` attribute. ```css [data-part="trigger"][data-placeholder-shown] { /* styles for empty state */ } ``` ## Methods and Properties ### Machine Context The cascade select machine exposes the following context properties: **`collection`** Type: `TreeCollection` Description: The tree collection data **`ids`** Type: `Partial<{ root: string; label: string; control: string; trigger: string; indicator: string; clearTrigger: string; positioner: string; content: string; hiddenInput: string; list(valuePath: string): string; item(valuePath: string): string; }>` Description: The ids of the cascade-select elements. Useful for composition. **`name`** Type: `string` Description: The name attribute of the underlying input element **`form`** Type: `string` Description: The form attribute of the underlying input element **`value`** Type: `string[][]` Description: The controlled value of the cascade-select **`defaultValue`** Type: `string[][]` Description: The initial value of the cascade-select when rendered. Use when you don't need to control the value. **`highlightedValue`** Type: `string[]` Description: The controlled highlighted value of the cascade-select **`defaultHighlightedValue`** Type: `string[]` Description: The initial highlighted value of the cascade-select when rendered. **`multiple`** Type: `boolean` Description: Whether to allow multiple selections **`open`** Type: `boolean` Description: The controlled open state of the cascade-select **`defaultOpen`** Type: `boolean` Description: The initial open state of the cascade-select when rendered. Use when you don't need to control the open state. **`highlightTrigger`** Type: `"click" | "hover"` Description: What triggers highlighting of items **`closeOnSelect`** Type: `boolean` Description: Whether the cascade-select should close when an item is selected **`loopFocus`** Type: `boolean` Description: Whether the cascade-select should loop focus when navigating with keyboard **`disabled`** Type: `boolean` Description: Whether the cascade-select is disabled **`readOnly`** Type: `boolean` Description: Whether the cascade-select is read-only **`required`** Type: `boolean` Description: Whether the cascade-select is required **`invalid`** Type: `boolean` Description: Whether the cascade-select is invalid **`positioning`** Type: `PositioningOptions` Description: The positioning options for the cascade-select content **`scrollToIndexFn`** Type: `(details: ScrollToIndexDetails) => void` Description: Function to scroll to a specific index in a list **`formatValue`** Type: `(selectedItems: T[][]) => string` Description: Function to format the display value **`onValueChange`** Type: `(details: ValueChangeDetails) => void` Description: Called when the value changes **`onHighlightChange`** Type: `(details: HighlightChangeDetails) => void` Description: Called when the highlighted value changes **`onOpenChange`** Type: `(details: OpenChangeDetails) => void` Description: Called when the open state changes **`allowParentSelection`** Type: `boolean` Description: Whether parent (branch) items can be selectable **`dir`** Type: `"ltr" | "rtl"` Description: The document's text/writing direction. **`id`** Type: `string` Description: The unique identifier of the machine. **`getRootNode`** Type: `() => ShadowRoot | Node | Document` Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron. **`onPointerDownOutside`** Type: `(event: PointerDownOutsideEvent) => void` Description: Function called when the pointer is pressed down outside the component **`onFocusOutside`** Type: `(event: FocusOutsideEvent) => void` Description: Function called when the focus is moved outside the component **`onInteractOutside`** Type: `(event: InteractOutsideEvent) => void` Description: Function called when an interaction happens outside the component ### Machine API The cascade select `api` exposes the following methods: **`collection`** Type: `TreeCollection` Description: The tree collection data **`open`** Type: `boolean` Description: Whether the cascade-select is open **`focused`** Type: `boolean` Description: Whether the cascade-select is focused **`multiple`** Type: `boolean` Description: Whether the cascade-select allows multiple selections **`disabled`** Type: `boolean` Description: Whether the cascade-select is disabled **`highlightedValue`** Type: `string[]` Description: The value of the highlighted item **`highlightedItems`** Type: `V[]` Description: The items along the highlighted path **`selectedItems`** Type: `V[][]` Description: The selected items **`hasSelectedItems`** Type: `boolean` Description: Whether there's a selected option **`empty`** Type: `boolean` Description: Whether the cascade-select value is empty **`value`** Type: `string[][]` Description: The current value of the cascade-select **`valueAsString`** Type: `string` Description: The current value as text **`focus`** Type: `() => void` Description: Function to focus on the select input **`reposition`** Type: `(options?: Partial) => void` Description: Function to set the positioning options of the cascade-select **`setOpen`** Type: `(open: boolean) => void` Description: Function to open the cascade-select **`setHighlightValue`** Type: `(value: string | string[]) => void` Description: Function to set the highlighted value (path or single value to find) **`clearHighlightValue`** Type: `() => void` Description: Function to clear the highlighted value **`selectValue`** Type: `(value: string[]) => void` Description: Function to select a value **`setValue`** Type: `(value: string[][]) => void` Description: Function to set the value **`clearValue`** Type: `(value?: string[]) => void` Description: Function to clear the value **`getItemState`** Type: `(props: ItemProps) => ItemState` Description: Returns the state of a cascade-select item ### Data Attributes **`Root`** **`data-scope`**: cascade-select **`data-part`**: root **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid **`data-state`**: "open" | "closed" **`Label`** **`data-scope`**: cascade-select **`data-part`**: label **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid **`Control`** **`data-scope`**: cascade-select **`data-part`**: control **`data-disabled`**: Present when disabled **`data-focus`**: Present when focused **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid **`data-state`**: "open" | "closed" **`Trigger`** **`data-scope`**: cascade-select **`data-part`**: trigger **`data-state`**: "open" | "closed" **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid **`data-focus`**: Present when focused **`data-placement`**: The placement of the trigger **`data-placeholder-shown`**: Present when placeholder is shown **`ClearTrigger`** **`data-scope`**: cascade-select **`data-part`**: clear-trigger **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid **`Content`** **`data-scope`**: cascade-select **`data-part`**: content **`data-activedescendant`**: The id the active descendant of the content **`data-state`**: "open" | "closed" **`List`** **`data-scope`**: cascade-select **`data-part`**: list **`data-depth`**: The depth of the item **`Indicator`** **`data-scope`**: cascade-select **`data-part`**: indicator **`data-state`**: "open" | "closed" **`data-disabled`**: Present when disabled **`data-readonly`**: Present when read-only **`data-invalid`**: Present when invalid **`Item`** **`data-scope`**: cascade-select **`data-part`**: item **`data-value`**: The value of the item **`data-disabled`**: Present when disabled **`data-highlighted`**: Present when highlighted **`data-selected`**: Present when selected **`data-depth`**: The depth of the item **`data-state`**: "checked" | "unchecked" **`data-type`**: The type of the item **`data-index-path`**: **`ItemText`** **`data-scope`**: cascade-select **`data-part`**: item-text **`data-value`**: The value of the item **`data-highlighted`**: Present when highlighted **`data-state`**: "checked" | "unchecked" **`data-disabled`**: Present when disabled **`ItemIndicator`** **`data-scope`**: cascade-select **`data-part`**: item-indicator **`data-value`**: The value of the item **`data-highlighted`**: Present when highlighted **`data-type`**: The type of the item **`data-state`**: "checked" | "unchecked" **`ValueText`** **`data-scope`**: cascade-select **`data-part`**: value-text **`data-disabled`**: Present when disabled **`data-invalid`**: Present when invalid **`data-focus`**: Present when focused ### CSS Variables ## Accessibility Adheres to the [ListBox WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/listbox). ### Keyboard Interactions **`Space`** Description: When focus is on trigger, opens the cascade select and focuses the first item.
When focus is on the content, selects the highlighted item.
**`Enter`** Description: When focus is on trigger, opens the cascade select and focuses the first item.
When focus is on content, selects the highlighted item.
**`ArrowDown`** Description: When focus is on trigger, opens the cascade select.
When focus is on content, moves focus to the next item in the current level.
**`ArrowUp`** Description: When focus is on trigger, opens the cascade select and focuses the last item.
When focus is on content, moves focus to the previous item in the current level.
**`ArrowRight`** Description: When focus is on a branch item, expands the next level and moves focus into it. **`ArrowLeft`** Description: When focus is on a nested level, collapses it and moves focus back to the parent.
When focus is at the root level, closes the cascade select.
**`Home`** Description: Moves focus to the first item in the current level. **`End`** Description: Moves focus to the last item in the current level. **`Esc`** Description: Closes the cascade select and moves focus to trigger. A checkbox allows users to make a binary choice, i.e. a choice between one of two possible mutually exclusive options. ## Resources [Latest version: v1.35.2](https://www.npmjs.com/package/@zag-js/checkbox) [Logic Visualizer](https://zag-visualizer.vercel.app/checkbox) [Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/checkbox) **Features** - Tri-state checkbox (`indeterminate` state) - Syncs with `disabled` state of fieldset - Syncs with form `reset` events - Can be toggled programmatically ## Installation Install the checkbox package: ```bash npm install @zag-js/checkbox @zag-js/solid # or yarn add @zag-js/checkbox @zag-js/solid ``` ## Anatomy Check the checkbox anatomy and part names. > Each part includes a `data-part` attribute to help identify them in the DOM. ## Usage Import the checkbox package: ```jsx import * as checkbox from "@zag-js/checkbox" ``` The checkbox package exports two key functions: - `machine` - State machine logic. - `connect` - Maps machine state to JSX props and event handlers. Then use the framework integration helpers: ```jsx import * as checkbox from "@zag-js/checkbox" import { normalizeProps, useMachine } from "@zag-js/solid" import { createMemo, createUniqueId } from "solid-js" function Checkbox() { const service = useMachine(checkbox.machine, { id: createUniqueId() }) const api = createMemo(() => checkbox.connect(service, normalizeProps)) return (