ark-ui

Builds accessible UI components with Ark UI headless primitives for React, Vue, Solid, and Svelte. Use when creating custom-styled components with robust state management and accessibility built-in.

16 stars

Best use case

ark-ui is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Builds accessible UI components with Ark UI headless primitives for React, Vue, Solid, and Svelte. Use when creating custom-styled components with robust state management and accessibility built-in.

Teams using ark-ui should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/ark-ui/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/frontend/ark-ui/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/ark-ui/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How ark-ui Compares

Feature / Agentark-uiStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Builds accessible UI components with Ark UI headless primitives for React, Vue, Solid, and Svelte. Use when creating custom-styled components with robust state management and accessibility built-in.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Ark UI

Headless component library with 45+ accessible components powered by state machines.

## Quick Start

```bash
npm install @ark-ui/react
```

```tsx
import { Accordion } from '@ark-ui/react'

function App() {
  return (
    <Accordion.Root defaultValue={['react']}>
      <Accordion.Item value="react">
        <Accordion.ItemTrigger>
          What is React?
          <Accordion.ItemIndicator>
            <ChevronDownIcon />
          </Accordion.ItemIndicator>
        </Accordion.ItemTrigger>
        <Accordion.ItemContent>
          React is a JavaScript library for building user interfaces.
        </Accordion.ItemContent>
      </Accordion.Item>
    </Accordion.Root>
  )
}
```

## Core Concepts

### Anatomy Pattern

Ark UI uses compound components with dot notation:

```tsx
<Component.Root>
  <Component.Trigger />
  <Component.Content>
    <Component.Item />
  </Component.Content>
</Component.Root>
```

### Data Attributes for Styling

Components expose state via data attributes:

```css
/* Base styles */
[data-scope="accordion"][data-part="trigger"] {
  display: flex;
  justify-content: space-between;
}

/* State-based styles */
[data-state="open"] {
  background-color: #f0f0f0;
}

[data-state="closed"] {
  background-color: white;
}

[data-disabled] {
  opacity: 0.5;
}

[data-focus] {
  outline: 2px solid blue;
}
```

### With Tailwind CSS

```tsx
<Accordion.ItemTrigger className="
  flex justify-between w-full px-4 py-2
  data-[state=open]:bg-gray-100
  data-[focus]:ring-2 data-[focus]:ring-blue-500
  data-[disabled]:opacity-50
">
  Trigger Text
</Accordion.ItemTrigger>
```

## Components

### Accordion

```tsx
import { Accordion } from '@ark-ui/react'
import { ChevronDownIcon } from 'lucide-react'

function AccordionDemo() {
  return (
    <Accordion.Root defaultValue={['item-1']} multiple>
      {items.map((item) => (
        <Accordion.Item key={item.value} value={item.value}>
          <Accordion.ItemTrigger>
            {item.title}
            <Accordion.ItemIndicator>
              <ChevronDownIcon />
            </Accordion.ItemIndicator>
          </Accordion.ItemTrigger>
          <Accordion.ItemContent>
            {item.content}
          </Accordion.ItemContent>
        </Accordion.Item>
      ))}
    </Accordion.Root>
  )
}
```

### Dialog

```tsx
import { Dialog, Portal } from '@ark-ui/react'

function DialogDemo() {
  return (
    <Dialog.Root>
      <Dialog.Trigger>Open Dialog</Dialog.Trigger>
      <Portal>
        <Dialog.Backdrop className="fixed inset-0 bg-black/50" />
        <Dialog.Positioner className="fixed inset-0 flex items-center justify-center">
          <Dialog.Content className="bg-white rounded-lg p-6 max-w-md w-full">
            <Dialog.Title className="text-lg font-semibold">
              Dialog Title
            </Dialog.Title>
            <Dialog.Description className="mt-2 text-gray-600">
              Dialog description text here.
            </Dialog.Description>
            <div className="mt-4 flex justify-end gap-2">
              <Dialog.CloseTrigger className="px-4 py-2 text-gray-600">
                Cancel
              </Dialog.CloseTrigger>
              <button className="px-4 py-2 bg-blue-500 text-white rounded">
                Confirm
              </button>
            </div>
          </Dialog.Content>
        </Dialog.Positioner>
      </Portal>
    </Dialog.Root>
  )
}
```

### Menu

```tsx
import { Menu, Portal } from '@ark-ui/react'

function MenuDemo() {
  return (
    <Menu.Root>
      <Menu.Trigger className="px-4 py-2 bg-gray-100 rounded">
        Open Menu
      </Menu.Trigger>
      <Portal>
        <Menu.Positioner>
          <Menu.Content className="bg-white shadow-lg rounded-lg p-1 min-w-[160px]">
            <Menu.Item value="edit" className="px-3 py-2 hover:bg-gray-100 rounded cursor-pointer">
              Edit
            </Menu.Item>
            <Menu.Item value="duplicate" className="px-3 py-2 hover:bg-gray-100 rounded cursor-pointer">
              Duplicate
            </Menu.Item>
            <Menu.Separator className="h-px bg-gray-200 my-1" />
            <Menu.Item value="delete" className="px-3 py-2 text-red-600 hover:bg-red-50 rounded cursor-pointer">
              Delete
            </Menu.Item>
          </Menu.Content>
        </Menu.Positioner>
      </Portal>
    </Menu.Root>
  )
}
```

### Select

```tsx
import { Select, Portal } from '@ark-ui/react'
import { CheckIcon, ChevronDownIcon } from 'lucide-react'

const items = [
  { label: 'React', value: 'react' },
  { label: 'Vue', value: 'vue' },
  { label: 'Svelte', value: 'svelte' },
]

function SelectDemo() {
  return (
    <Select.Root items={items}>
      <Select.Label>Framework</Select.Label>
      <Select.Control>
        <Select.Trigger className="flex items-center justify-between w-full px-4 py-2 border rounded">
          <Select.ValueText placeholder="Select a framework" />
          <Select.Indicator>
            <ChevronDownIcon />
          </Select.Indicator>
        </Select.Trigger>
      </Select.Control>
      <Portal>
        <Select.Positioner>
          <Select.Content className="bg-white shadow-lg rounded-lg p-1">
            {items.map((item) => (
              <Select.Item
                key={item.value}
                item={item}
                className="flex items-center justify-between px-3 py-2 hover:bg-gray-100 rounded cursor-pointer"
              >
                <Select.ItemText>{item.label}</Select.ItemText>
                <Select.ItemIndicator>
                  <CheckIcon />
                </Select.ItemIndicator>
              </Select.Item>
            ))}
          </Select.Content>
        </Select.Positioner>
      </Portal>
    </Select.Root>
  )
}
```

### Combobox

```tsx
import { Combobox, Portal } from '@ark-ui/react'
import { useState } from 'react'

const allItems = [
  { label: 'React', value: 'react' },
  { label: 'Vue', value: 'vue' },
  { label: 'Svelte', value: 'svelte' },
  { label: 'Solid', value: 'solid' },
]

function ComboboxDemo() {
  const [items, setItems] = useState(allItems)

  const handleInputChange = ({ inputValue }) => {
    const filtered = allItems.filter((item) =>
      item.label.toLowerCase().includes(inputValue.toLowerCase())
    )
    setItems(filtered)
  }

  return (
    <Combobox.Root items={items} onInputValueChange={handleInputChange}>
      <Combobox.Label>Framework</Combobox.Label>
      <Combobox.Control>
        <Combobox.Input className="w-full px-4 py-2 border rounded" placeholder="Search..." />
        <Combobox.Trigger>
          <ChevronDownIcon />
        </Combobox.Trigger>
      </Combobox.Control>
      <Portal>
        <Combobox.Positioner>
          <Combobox.Content className="bg-white shadow-lg rounded-lg p-1">
            {items.map((item) => (
              <Combobox.Item
                key={item.value}
                item={item}
                className="px-3 py-2 hover:bg-gray-100 rounded cursor-pointer"
              >
                <Combobox.ItemText>{item.label}</Combobox.ItemText>
                <Combobox.ItemIndicator>
                  <CheckIcon />
                </Combobox.ItemIndicator>
              </Combobox.Item>
            ))}
          </Combobox.Content>
        </Combobox.Positioner>
      </Portal>
    </Combobox.Root>
  )
}
```

### Tabs

```tsx
import { Tabs } from '@ark-ui/react'

function TabsDemo() {
  return (
    <Tabs.Root defaultValue="react">
      <Tabs.List className="flex gap-1 border-b">
        <Tabs.Trigger
          value="react"
          className="px-4 py-2 data-[selected]:border-b-2 data-[selected]:border-blue-500"
        >
          React
        </Tabs.Trigger>
        <Tabs.Trigger
          value="vue"
          className="px-4 py-2 data-[selected]:border-b-2 data-[selected]:border-blue-500"
        >
          Vue
        </Tabs.Trigger>
        <Tabs.Trigger
          value="svelte"
          className="px-4 py-2 data-[selected]:border-b-2 data-[selected]:border-blue-500"
        >
          Svelte
        </Tabs.Trigger>
        <Tabs.Indicator className="bg-blue-500 h-0.5" />
      </Tabs.List>
      <Tabs.Content value="react" className="p-4">React content</Tabs.Content>
      <Tabs.Content value="vue" className="p-4">Vue content</Tabs.Content>
      <Tabs.Content value="svelte" className="p-4">Svelte content</Tabs.Content>
    </Tabs.Root>
  )
}
```

### Switch

```tsx
import { Switch } from '@ark-ui/react'

function SwitchDemo() {
  return (
    <Switch.Root>
      <Switch.Control className="
        relative w-11 h-6 bg-gray-200 rounded-full
        data-[state=checked]:bg-blue-500
        transition-colors
      ">
        <Switch.Thumb className="
          block w-5 h-5 bg-white rounded-full shadow
          translate-x-0.5 data-[state=checked]:translate-x-5
          transition-transform
        " />
      </Switch.Control>
      <Switch.Label className="ml-2">Enable notifications</Switch.Label>
      <Switch.HiddenInput />
    </Switch.Root>
  )
}
```

### Checkbox

```tsx
import { Checkbox } from '@ark-ui/react'
import { CheckIcon, MinusIcon } from 'lucide-react'

function CheckboxDemo() {
  return (
    <Checkbox.Root>
      <Checkbox.Control className="
        w-5 h-5 border-2 rounded
        data-[state=checked]:bg-blue-500 data-[state=checked]:border-blue-500
        data-[state=indeterminate]:bg-blue-500 data-[state=indeterminate]:border-blue-500
      ">
        <Checkbox.Indicator>
          <CheckIcon className="w-4 h-4 text-white" />
        </Checkbox.Indicator>
        <Checkbox.Indicator indeterminate>
          <MinusIcon className="w-4 h-4 text-white" />
        </Checkbox.Indicator>
      </Checkbox.Control>
      <Checkbox.Label className="ml-2">Accept terms</Checkbox.Label>
      <Checkbox.HiddenInput />
    </Checkbox.Root>
  )
}
```

### Slider

```tsx
import { Slider } from '@ark-ui/react'

function SliderDemo() {
  return (
    <Slider.Root defaultValue={[50]} min={0} max={100}>
      <Slider.Label>Volume</Slider.Label>
      <Slider.ValueText />
      <Slider.Control className="relative flex items-center w-full h-5">
        <Slider.Track className="w-full h-2 bg-gray-200 rounded-full">
          <Slider.Range className="h-full bg-blue-500 rounded-full" />
        </Slider.Track>
        <Slider.Thumb
          index={0}
          className="w-5 h-5 bg-white border-2 border-blue-500 rounded-full shadow"
        />
      </Slider.Control>
    </Slider.Root>
  )
}

// Range slider
<Slider.Root defaultValue={[25, 75]}>
  <Slider.Control>
    <Slider.Track>
      <Slider.Range />
    </Slider.Track>
    <Slider.Thumb index={0} />
    <Slider.Thumb index={1} />
  </Slider.Control>
</Slider.Root>
```

### Tooltip

```tsx
import { Tooltip, Portal } from '@ark-ui/react'

function TooltipDemo() {
  return (
    <Tooltip.Root>
      <Tooltip.Trigger className="px-4 py-2 bg-gray-100 rounded">
        Hover me
      </Tooltip.Trigger>
      <Portal>
        <Tooltip.Positioner>
          <Tooltip.Content className="bg-gray-900 text-white px-3 py-2 rounded text-sm">
            <Tooltip.Arrow>
              <Tooltip.ArrowTip className="border-t-gray-900" />
            </Tooltip.Arrow>
            Tooltip content
          </Tooltip.Content>
        </Tooltip.Positioner>
      </Portal>
    </Tooltip.Root>
  )
}
```

### Popover

```tsx
import { Popover, Portal } from '@ark-ui/react'

function PopoverDemo() {
  return (
    <Popover.Root>
      <Popover.Trigger className="px-4 py-2 bg-blue-500 text-white rounded">
        Open Popover
      </Popover.Trigger>
      <Portal>
        <Popover.Positioner>
          <Popover.Content className="bg-white shadow-lg rounded-lg p-4 max-w-sm">
            <Popover.Arrow>
              <Popover.ArrowTip />
            </Popover.Arrow>
            <Popover.Title className="font-semibold">Popover Title</Popover.Title>
            <Popover.Description className="mt-2 text-gray-600">
              This is the popover content.
            </Popover.Description>
            <Popover.CloseTrigger className="absolute top-2 right-2">
              <XIcon />
            </Popover.CloseTrigger>
          </Popover.Content>
        </Popover.Positioner>
      </Portal>
    </Popover.Root>
  )
}
```

### DatePicker

```tsx
import { DatePicker, Portal } from '@ark-ui/react'

function DatePickerDemo() {
  return (
    <DatePicker.Root>
      <DatePicker.Label>Date</DatePicker.Label>
      <DatePicker.Control>
        <DatePicker.Input className="px-4 py-2 border rounded" />
        <DatePicker.Trigger>
          <CalendarIcon />
        </DatePicker.Trigger>
      </DatePicker.Control>
      <Portal>
        <DatePicker.Positioner>
          <DatePicker.Content className="bg-white shadow-lg rounded-lg p-4">
            <DatePicker.View view="day">
              <DatePicker.Context>
                {(api) => (
                  <>
                    <DatePicker.ViewControl>
                      <DatePicker.PrevTrigger>
                        <ChevronLeftIcon />
                      </DatePicker.PrevTrigger>
                      <DatePicker.ViewTrigger>
                        <DatePicker.RangeText />
                      </DatePicker.ViewTrigger>
                      <DatePicker.NextTrigger>
                        <ChevronRightIcon />
                      </DatePicker.NextTrigger>
                    </DatePicker.ViewControl>
                    <DatePicker.Table>
                      <DatePicker.TableHead>
                        <DatePicker.TableRow>
                          {api.weekDays.map((day, i) => (
                            <DatePicker.TableHeader key={i}>
                              {day.narrow}
                            </DatePicker.TableHeader>
                          ))}
                        </DatePicker.TableRow>
                      </DatePicker.TableHead>
                      <DatePicker.TableBody>
                        {api.weeks.map((week, i) => (
                          <DatePicker.TableRow key={i}>
                            {week.map((day, j) => (
                              <DatePicker.TableCell key={j} value={day}>
                                <DatePicker.TableCellTrigger>
                                  {day.day}
                                </DatePicker.TableCellTrigger>
                              </DatePicker.TableCell>
                            ))}
                          </DatePicker.TableRow>
                        ))}
                      </DatePicker.TableBody>
                    </DatePicker.Table>
                  </>
                )}
              </DatePicker.Context>
            </DatePicker.View>
          </DatePicker.Content>
        </DatePicker.Positioner>
      </Portal>
    </DatePicker.Root>
  )
}
```

### Toast

```tsx
import { Toaster, createToaster } from '@ark-ui/react'

const toaster = createToaster({
  placement: 'bottom-end',
  overlap: true,
  gap: 16,
})

function ToastDemo() {
  return (
    <>
      <button
        onClick={() => {
          toaster.create({
            title: 'Success',
            description: 'Your changes have been saved.',
            type: 'success',
          })
        }}
      >
        Show Toast
      </button>

      <Toaster toaster={toaster}>
        {(toast) => (
          <Toast.Root key={toast.id}>
            <Toast.Title>{toast.title}</Toast.Title>
            <Toast.Description>{toast.description}</Toast.Description>
            <Toast.CloseTrigger>Close</Toast.CloseTrigger>
          </Toast.Root>
        )}
      </Toaster>
    </>
  )
}
```

## Controlled vs Uncontrolled

```tsx
// Uncontrolled (internal state)
<Accordion.Root defaultValue={['item-1']}>
  {/* ... */}
</Accordion.Root>

// Controlled (external state)
const [value, setValue] = useState(['item-1'])

<Accordion.Root value={value} onValueChange={(details) => setValue(details.value)}>
  {/* ... */}
</Accordion.Root>
```

## API Patterns

### onValueChange

```tsx
<Select.Root
  onValueChange={(details) => {
    console.log(details.value)       // Selected value(s)
    console.log(details.items)       // Selected item(s)
  }}
>
```

### onOpenChange

```tsx
<Dialog.Root
  onOpenChange={(details) => {
    console.log(details.open)        // boolean
  }}
>
```

## Best Practices

1. **Use Portal** - Wrap overlays in Portal for proper stacking
2. **Style with data attributes** - Use `data-[state=open]` patterns
3. **Provide accessible labels** - Use Label components
4. **Handle keyboard** - All components have built-in keyboard support
5. **Use HiddenInput** - For form integration with checkboxes/switches

## Reference Files

- [references/components.md](references/components.md) - Complete component list
- [references/patterns.md](references/patterns.md) - Common patterns

Related Skills

bgo

10
from diegosouzapw/awesome-omni-skill

Automates the complete Blender build-go workflow, from building and packaging your extension/add-on to removing old versions, installing, enabling, and launching Blender for quick testing and iteration.

Coding & Development

pcf-tooling

16
from diegosouzapw/awesome-omni-skill

Get Microsoft Power Platform CLI tooling for Power Apps Component Framework Triggers on: **/*.{ts,tsx,js,json,xml,pcfproj,csproj}

pcf-code-components

16
from diegosouzapw/awesome-omni-skill

Understanding code components structure and implementation Triggers on: **/*.{ts,tsx,js,json,xml,pcfproj,csproj}

pcf-canvas-apps

16
from diegosouzapw/awesome-omni-skill

Code components for canvas apps implementation, security, and configuration Triggers on: **/*.{ts,tsx,js,json,xml,pcfproj,csproj}

nextjs15-react19-vercelai-tailwind-cursorrules-prompt-file-cursorrules

16
from diegosouzapw/awesome-omni-skill

Apply for nextjs15-react19-vercelai-tailwind-cursorrules-prompt-file. --- description: Best practices for using Tailwind CSS in Next.js 15 and React 19 applications, including responsive design, custom configurations, and performance optimization. globs: app/**/*

nextjs

16
from diegosouzapw/awesome-omni-skill

Next.js framework best practices including App Router, data fetching, and performance optimization.

nextjs-supabase-auth

16
from diegosouzapw/awesome-omni-skill

Expert integration of Supabase Auth with Next.js App Router Use when: supabase auth next, authentication next.js, login supabase, auth middleware, protected route.

Next.js App Router & Server Components

16
from diegosouzapw/awesome-omni-skill

Build Next.js 15 applications using App Router, Server Components, Client Components, Server Actions, and streaming. Apply when creating pages, handling data fetching, implementing routes, or optimizing performance.

moai-domain-frontend

16
from diegosouzapw/awesome-omni-skill

Frontend development specialist covering React 19, Next.js 16, Vue 3.5, and modern UI/UX patterns with component architecture. Use when building web UIs, implementing components, optimizing frontend performance, or integrating state management.

marp-pitch-creator

16
from diegosouzapw/awesome-omni-skill

Create high-quality pitch decks using Marp and Tailwind CSS

marimo

16
from diegosouzapw/awesome-omni-skill

Guide for creating and working with marimo notebooks, the reactive Python notebook that stores as pure .py files. This skill should be used when creating, editing, running, or deploying marimo notebooks.

manim-scroll

16
from diegosouzapw/awesome-omni-skill

Build and integrate scroll-driven Manim animations with pre-rendered assets, manifest generation, and the web runtime. Use when users ask about Manim scroll playback, render pipelines, native text animation, or integrating the runtime.