Skip to content
igris-labsPublic

Repository files navigation

Logo

Akaza UI

Vue-native headless UI primitives - accessible, unstyled, and built for Vue.

GitHub stars License

Akaza UI

Unlike libraries ported from React (Radix, Reka), Akaza UI is designed from scratch for Vue 3. It uses v-model, named scoped slots, and the ui prop as first-class primitives - no sub-component trees, no asChild, no fighting the framework.

Why Akaza UI?

Others Akaza UI
<DialogRoot><DialogTrigger><DialogPortal><DialogOverlay><DialogContent> <Dialog v-model="isOpen"> with named slots
React-adapted DX Vue-native: v-model, scoped slots, inject/provide
Styled or opinionated defaults Fully unstyled - data-akaza-* attributes and class hooks only

Core Philosophy

  • Slot-based, not sub-component-based - named scoped slots (#trigger, #header, #body, #footer) keep templates flat and readable
  • v-model-native - all stateful components bind via standard v-model
  • ui prop for structural styling - pass class strings per part (ui.overlay, ui.content, ui.header) instead of wrapping sub-components
  • Items-based API for lists - RadioGroup, Menu, Tabs, Select, Listbox, CheckboxGroup, and ToggleGroup accept data arrays; per-item rendering uses named slots
  • Accessible by default - WAI-ARIA roles, keyboard navigation, and focus management built in

Installation

pnpm add akaza-ui
# or
npm install akaza-ui

Vue

Import the CSS in your app entry point:

// main.ts
import "akaza-ui/dist/akaza-ui.css";

If you use Tailwind CSS, declare the akaza-reset layer before Tailwind so utility classes can override component base styles:

/* main.css */
@layer akaza-reset;
@import "tailwindcss";

Nuxt

Use the built-in Nuxt module — it registers all components as auto-imports and injects the CSS automatically:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ["akaza-ui/nuxt"],
});

No manual CSS import or component registration needed.

Components

Component Description
Accordion Single or multi-open collapsible item list
AlertDialog Confirmation dialog with alertdialog semantics
Avatar Image with fallback slot on load error
Button Accessible button with disabled, loading, async click, and as support
Calendar Locale-aware single, multiple, and range date grid
Checkbox Tri-state checkbox (true, false, 'indeterminate')
CheckboxGroup Multi-select checkbox list with an items-based API
Collapsible Single show/hide region with animated height
Combobox Editable input plus listbox selection, filtering, and creation
ContextMenu Right-click menu positioned at the pointer
DataTable Typed semantic table with custom cells, sorting, filtering, and selection
DateField Locale-ordered segmented date entry with optional Calendar
DateRangeField Segmented start/end entry with range validation and native forms
DateRangePicker Date range entry plus collision-aware Calendar popup
Dialog Modal dialog with focus trap, Escape, backdrop
Drawer Side panel with slide-in animation (top, right, bottom, left)
Editable Inline text editing with preview, draft, submit, and cancel states
Field Label, description, and error wiring for controls
Fieldset Native fieldset and legend wrapper for related controls
Form Submit wrapper exposing FormData and native validity state
Input Text input with Field integration and invalid state attrs
Listbox Standalone single/multiple selection with filtering and virtualization
Menu Dropdown menu with items-based API, submenus, checkbox/radio items
Menubar Horizontal menu strip with cross-menu keyboard navigation
Meter Read-only gauge for bounded numeric values
NavigationMenu Site navigation with hover/focus flyout panels
NumberField Spinbutton number input with increment/decrement and scrub support
OverlayProvider Programmatic overlay mounting with useOverlay
Pagination Page navigation with compact ranges, ellipses, controls, and links
PinInput Multi-cell OTP/PIN input with paste distribution and form support
Popover Click-triggered floating panel with auto-positioning
Progress Progressbar with determinate and indeterminate support
RadioGroup Accessible radio group with roving tabindex and custom item rendering
Rating Rating input with fractional values, hover preview, and form support
Select Listbox select with keyboard navigation, typeahead, and form submission
Separator Horizontal or vertical divider, decorative or semantic
Sidebar Responsive sidebar with collapse, resizing, and mobile dialog behavior
AppLayout Application shell with region slots, scrolling, sticky bars, and overlay sides
Slider Slider with pointer drag, keyboard step, and ARIA value metadata
Stepper Multi-step progress and navigation with linear rules and panels
Switch Binary toggle with WAI-ARIA switch role
Tabs Accessible tab set with animated indicator, items-based API
TagsInput Tokenized text input with keyboard editing, validation, and paste handling
Toast Queued status/alert notifications with actions and direction-aware motion
Toggle On/off button with aria-pressed
ToggleGroup Single or multiple pressed values with roving focus
Toolbar Command strip with roving focus across controls
Tooltip Hover/focus tooltip with auto-positioning and delay
Tree Hierarchical selection with lazy loading, propagation, and virtualization
HoverPreviewCard Hover/focus rich preview for links and entities

Usage

Dialog

<script setup lang="ts">
import { ref } from "vue";
import { Dialog } from "akaza-ui";

const isOpen = ref(false);
</script>

<template>
  <Dialog
    v-model="isOpen"
    :ui="{
      overlay: 'fixed inset-0 bg-black/50 backdrop-blur-sm',
      content:
        'fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl shadow-xl w-full max-w-md',
      header: 'flex items-center justify-between p-6 border-b',
      body: 'p-6',
      footer: 'flex justify-end gap-2 p-6 border-t',
    }"
  >
    <template #trigger="{ toggle }">
      <button @click="toggle">Open</button>
    </template>

    <template #header="{ close, titleId }">
      <h2 :id="titleId">Confirm</h2>
      <button @click="close">✕</button>
    </template>

    <template #body="{ descriptionId }">
      <p :id="descriptionId">Are you sure you want to continue?</p>
    </template>

    <template #footer="{ close }">
      <button @click="close">Cancel</button>
      <button @click="close">Confirm</button>
    </template>
  </Dialog>
</template>

Menu

<script setup lang="ts">
import { Menu } from "akaza-ui";

const items = [
  { label: "Profile", onSelect: () => {} },
  { label: "Settings", onSelect: () => {} },
  { type: "separator" },
  { label: "Sign out", onSelect: () => {} },
];
</script>

<template>
  <Menu :items="items">
    <template #trigger="{ toggle }">
      <button @click="toggle">Options</button>
    </template>
  </Menu>
</template>

Tabs

<script setup lang="ts">
import { ref } from "vue";
import { Tabs } from "akaza-ui";

const activeTab = ref("overview");
const items = [
  { value: "overview", label: "Overview" },
  { value: "settings", label: "Settings" },
];
</script>

<template>
  <Tabs v-model="activeTab" :items="items" aria-label="Main navigation">
    <template #panel-overview>Overview content</template>
    <template #panel-settings>Settings content</template>
  </Tabs>
</template>

Styling

Akaza UI is fully unstyled. Relevant structural elements expose:

  • data-akaza-state - primary state (open, closed, checked, disabled, loading)
  • granular data-akaza-* attrs - orthogonal state (invalid, focused, dirty, filled)
  • data-akaza-side - positioning side for floating elements (top, bottom, left, right)
  • semantic akaza-* classes - stable CSS selectors for each documented part

Style via the ui prop (inject classes per structural part):

<Dialog :ui="{
  overlay: 'fixed inset-0 bg-black/50 backdrop-blur-sm',
  content: 'fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded-xl shadow-xl w-full max-w-md',
  header: 'flex items-center justify-between p-6 border-b',
  body: 'p-6',
  footer: 'flex justify-end gap-2 p-6 border-t',
}">

Or target via CSS:

[data-akaza-state="open"] { … }
.akaza-dialog-overlay { … }
.akaza-dialog-content { … }

Requirements

  • Vue >=3.5.0

Repo Structure

packages/
  akaza-ui/       — publishable library (akaza-ui on npm)
    src/
      components/  — Vue component primitives
      composables/ — internal + public composables (useOverlay)
      utils/       — focusTrap, focusable helpers
    tests/         — unit, Nuxt integration, axe, and cross-browser interaction tests
docs/             — documentation site (Nuxt + Nuxt UI)

Status

Early development. API is not stable. Breaking changes may occur between minor versions.

License

MIT

Releases

Packages

Contributors

Languages