Skip to main content
Zag’s API is intentionally low-level and unstyled. Unlike higher-level libraries, it provides only behavior, accessibility, and interactions — you bring your own styles. This gives you complete freedom over your design system. Each machine documents its DOM structure and the attributes it exposes so you know exactly what to target.

Styling a component part

Every Zag component is made up of named parts. For example, the dialog component has these parts: backdrop, positioner, content, title, description, and close-trigger. Each part element receives a data-part attribute automatically when you spread the props returned by the machine’s connect function. Here is what the dialog’s rendered HTML looks like:
<div data-part="backdrop"></div>
<div data-part="positioner">
  <div data-part="content">
    <h2 data-part="title">Dialog Title</h2>
    <p data-part="description">Dialog Description</p>
    <button data-part="close-trigger">Close</button>
  </div>
</div>
Use the CSS attribute selector to target each part:
[data-part="backdrop"] {
  background-color: #000;
  opacity: 0.5;
}

[data-part="content"] {
  padding: 24px;
  border-radius: 6px;
  background: white;
}

[data-part="close-trigger"] {
  position: absolute;
  top: 16px;
  right: 16px;
}

Styling a state

When a component part can be in multiple states, Zag automatically attaches data-* attributes reflecting the current state. You do not need to add these manually — they are applied when you spread the props from connect. For example, an accordion trigger exposes:
  • data-disabled — when the trigger is disabled
  • data-expanded — when the trigger is in its expanded state
[data-part="trigger"][data-expanded] {
  background-color: #f0f0f0;
  font-weight: bold;
}

[data-part="trigger"][data-disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}
This same pattern applies across every component in the library.

Common state attributes

Different components expose different state attributes depending on their behavior. Here are the most commonly used ones:
AttributeWhen it is present
data-disabledThe element or its ancestor is disabled
data-expandedThe element is in an open/expanded state
data-highlightedThe element is currently highlighted (e.g. hovered in a listbox)
data-selectedThe element is selected
data-checkedThe element is checked
data-invalidThe element has a validation error
data-readonlyThe element is read-only
data-placeholder-shownA placeholder value is currently displayed
data-orientationThe orientation ("horizontal" or "vertical")

Scoping styles to a component

To avoid style collisions when using multiple components on the same page, scope your selectors to a root element using the data-scope attribute:
[data-scope="accordion"] [data-part="trigger"] {
  /* styles only apply to accordion triggers */
}

[data-scope="dialog"] [data-part="trigger"] {
  /* styles only apply to dialog triggers */
}

CSS variables and theming

Because Zag components are fully unstyled, CSS custom properties are a natural fit for theming. Define your design tokens at a high level and reference them inside your part selectors:
:root {
  --color-primary: #6366f1;
  --radius-md: 6px;
  --space-4: 16px;
}

[data-part="content"] {
  border-radius: var(--radius-md);
  padding: var(--space-4);
}

[data-part="trigger"][data-expanded] {
  color: var(--color-primary);
}

Framework integration example

When you use a Zag machine in a framework, the data-part and state attributes are applied for you via the spread props pattern:
import { useMachine, normalizeProps } from "@zag-js/react"
import * as accordion from "@zag-js/accordion"
import { useId } from "react"

function Accordion() {
  const service = useMachine(accordion.machine, { id: useId() })
  const api = accordion.connect(service, normalizeProps)

  return (
    <div {...api.getRootProps()}>
      <div {...api.getItemProps({ value: "item-1" })}>
        <h3>
          {/* data-part="trigger" and data-expanded are applied automatically */}
          <button {...api.getTriggerProps({ value: "item-1" })}>
            Section 1
          </button>
        </h3>
        <div {...api.getContentProps({ value: "item-1" })}>
          Content for section 1
        </div>
      </div>
    </div>
  )
}
Zag was designed to encapsulate logic, accessibility, and interactions while giving you full control over styling. You own every pixel.

Build docs developers (and LLMs) love