Styling
Learn how to style Ark UI components.
Overview
Ark UI is a headless component library that works with any styling solution. It provides functional styles for elements like popovers for positioning, while leaving presentation styles up to you. Some components also expose CSS variables that can be used for styling or animations.
Tip: Looking for a ready-to-use solution? Checkout Park UI for a collection of pre-designed styles based on Ark UI components.
Data Attributes
Ark UI components use data-scope and data-part attributes to target specific elements within a component.
Interactive components often include data-* attributes to indicate their state. For example, here's what an open
accordion item looks like:
<div data-scope="accordion" data-part="item" data-state="open"></div>
For more details on each component's data attributes, refer to their respective documentation.
Styling with CSS
When styling components with CSS, you can target the data attributes assigned to each component part for easy customization.
Styling a Part
To style a specific component part, target its data-scope and data-part attributes:
[data-scope='accordion'][data-part='item'] {
border-bottom: 1px solid #e5e5e5;
}
Styling a State
To style a component based on its state, use the data-state attribute:
[data-scope='accordion'][data-part='item'][data-state='open'] {
background-color: #f5f5f5;
}
Tip: If you prefer using classes instead of data attributes, utilize the
classorclassNameprop to add custom classes to Ark UI components.
Class Names
If you prefer using classes instead of data attributes, utilize class or className prop to add custom classes to Ark
UI components.
Pass a class:
<Accordion.Root>
<Accordion.Item className="AccordionItem">{/* … */}</Accordion.Item>
</Accordion.Root>
Then use in styles:
.AccordionItem {
border-bottom: 1px solid #e5e5e5;
&[data-state='open'] {
background-color: #f5f5f5;
}
}
Z-Index
Ark UI does not prescribe a global z-index scale. You decide the base z-index value for overlays, and Ark exposes Zag's
--layer-index variable to keep stacked overlays in the right order.
--layer-index is a zero-based index for open dismissible layers. Use it as an offset from your base z-index, not as
the full z-index value.
Use one shared base z-index for all dismissible overlays. This lets Zag order any combination of nested layers correctly, regardless of component type.
For example, this should stack correctly:
- A dialog opened from a popover
- A menu opened inside a dialog
- A select opened inside a drawer
- A nested dialog opened from another dialog
If each component type uses a different base z-index, those base values can fight the layer order. Keep the base the
same, then let --layer-index do the stacking.
For dialogs and drawers, apply it to the content. Zag syncs the content's computed z-index to the backdrop and
positioner as --z-index, so set the z-index property for those parts:
[data-scope='dialog'][data-part='content'] {
z-index: calc(1000 + var(--layer-index, 0));
}
[data-scope='dialog'][data-part='positioner'] {
z-index: var(--z-index, 1000);
}
[data-scope='dialog'][data-part='backdrop'] {
z-index: calc(var(--z-index, 1000) - 1);
}
For popper-style components, apply it to the floating content. Zag syncs the content's computed z-index to the
positioner as --z-index, and the positioner already uses z-index: var(--z-index):
[data-scope='popover'][data-part='content'],
[data-scope='menu'][data-part='content'],
[data-scope='select'][data-part='content'],
[data-scope='combobox'][data-part='content'],
[data-scope='date-picker'][data-part='content'] {
z-index: calc(1000 + var(--layer-index, 0));
}
If your design system exposes z-index tokens, prefer one shared overlay token for dismissible layers:
[data-scope='dialog'][data-part='content'] {
z-index: calc(var(--z-index-overlay) + var(--layer-index, 0));
}
[data-scope='dialog'][data-part='positioner'] {
z-index: var(--z-index, var(--z-index-overlay));
}
[data-scope='dialog'][data-part='backdrop'] {
z-index: calc(var(--z-index, var(--z-index-overlay)) - 1);
}
[data-scope='popover'][data-part='content'] {
z-index: calc(var(--z-index-overlay) + var(--layer-index, 0));
}
Caution: Avoid using --layer-index as the entire z-index:
[data-scope='dialog'][data-part='positioner'] {
z-index: var(--layer-index);
}
This produces values like 0, 1, and 2, which can appear below headers, sidebars, or other app chrome.
Zag also exposes data-nested, data-has-nested, and --nested-layer-count for styling layers that contain nested
layers.
[data-scope='dialog'][data-part='content'][data-has-nested='dialog'] {
scale: 0.98;
}
Use portals for overlays that need to escape parent stacking contexts. CSS properties like transform, filter,
opacity, contain, isolation, and perspective can create stacking contexts that trap inline overlays.
Styling with Panda CSS
Panda CSS is a best-in-class CSS-in-JS framework that integrates seamlessly with Ark UI, providing an efficient styling solution.
Styling a part
Panda offers various ways to write styles, but in the context of Ark UI, we recommend using the defineSlotRecipe
function to style a component with its different parts and variants.
Important: When importing anatomy objects, we recommend using the
@ark-ui/<framework>/anatomyentrypoint (e.g.,@ark-ui/react/anatomy) instead of the main package export to avoid potential build and import errors.
import { accordionAnatomy } from '@ark-ui/react/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'
export const accordionStyles = defineSlotRecipe({
className: 'accordion',
slots: accordionAnatomy.keys(),
base: {
item: {
borderBottom: '1px solid #e5e5e5',
},
},
defaultVariants: {},
variants: {},
})
Styling a state
To style a component based on its state, you can use built in conditions in Panda CSS.
import { accordionAnatomy } from '@ark-ui/react/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'
export const accordionStyles = defineSlotRecipe({
className: 'accordion',
slots: accordionAnatomy.keys(),
base: {
item: {
borderBottom: '1px solid {colors.gray.300}',
_open: {
backgroundColor: 'gray.100',
},
},
},
defaultVariants: {},
variants: {},
})
Styling with Tailwind CSS
Tailwind CSS is a utility-first CSS framework providing a flexible way to style your components.
Styling a Part
To style a part, apply classes directly to the parts using either class or className, depending on the JavaScript
framework.
<Accordion.Root>
<Accordion.Item className="border-b border-gray-300">{/* … */}</Accordion.Item>
</Accordion.Root>
Styling a State
Leverage Tailwind CSS's variant selector to style a component based on its data-state attribute.
<Accordion.Root>
<Accordion.Item className="border-b border-gray-300 data-[state=open]:bg-gray-100">{/* … */}</Accordion.Item>
</Accordion.Root>