Design Principles

Core principles that guide HareUI's design and API.

HareUI follows the same design principles as HeroUI v3, adapted to Vue's component model and Nuxt UI-style conventions.

Core Principles

1. Semantic Intent Over Visual Style

Use semantic naming (primary, secondary, tertiary) instead of visual descriptions (solid, flat, bordered). Inspired by Uber's Base design system, HareUI keeps the same variant names and values as HeroUI, so the hierarchy is unchanged:

<!-- ✅ Semantic variants communicate hierarchy -->
<Button variant="primary">Save</Button>
<Button variant="secondary">Edit</Button>
<Button variant="tertiary">Cancel</Button>
VariantPurposeUsage
PrimaryMain action to move forward1 per context
SecondaryAlternative actionsMultiple allowed
TertiaryDismissive actions (cancel, skip)Sparingly
DangerDestructive actionsWhen needed

2. Accessibility as Foundation — Reka UI

HareUI is built on Reka UI instead of React Aria Components, so the same WCAG-conscious behavior — focus management, keyboard navigation, screen reader support — comes from Vue-native headless primitives. Components also expose their own state through props and data attributes without any extra wiring:

<script setup lang="ts">
import { ref } from 'vue'
import { Button } from '@hareui/vue'

const isSaving = ref(false)
</script>

<template>
  <!-- pending renders a Spinner, sets aria-busy/data-pending, and
       suppresses the click handler while true -->
  <Button variant="primary" :pending="isSaving" @click="isSaving = true">
    Save
  </Button>
</template>

3. Composition Over Configuration — Slots Over Compound Components

HeroUI exposes compound parts like Card.Header / Card.Title. HareUI replaces compound components with named template slots on a single component — rearrange, customize, or omit parts with standard Vue syntax:

import { Card } from '@heroui/react';

<Card>
  <Card.Header>
    <Card.Title>Settings</Card.Title>
    <Card.Description>Manage your preferences</Card.Description>
  </Card.Header>
  <Card.Content>Body content</Card.Content>
</Card>
<template>
  <Card>
    <template #header>
      <h3>Settings</h3>
      <p>Manage your preferences</p>
    </template>
    Body content
  </Card>
</template>

Most compound parts also have a shorthand prop (title, description, …) for the common case — see each component's Anatomy section.

4. Progressive Disclosure

Start simple, add complexity only when needed. Components work with minimal props and scale up as requirements grow.

<!-- Level 1: Minimal -->
<Button>Click me</Button>

<!-- Level 2: Enhanced -->
<Button variant="primary" size="lg">
  Submit
</Button>

<!-- Level 3: Advanced -->
<Button variant="primary" :pending="isSaving">
  Submit
</Button>

5. Predictable Behavior

Consistent patterns across all components: sizes (sm, md, lg), variants, a class prop, and data-slot attributes. Same API, same behavior, every component:

<Button size="lg" variant="primary" class="custom" />
<Chip size="lg" color="success" class="custom" />
<Avatar size="lg" class="custom" />

6. Type Safety First

Full TypeScript support with IntelliSense, auto-completion, and compile-time error detection. Component prop, slot, and ui-key types are exported alongside each component:

import type { ButtonProps } from '@hareui/vue'

// Type-safe props
const props: ButtonProps = {
  variant: 'primary', // Autocomplete: primary | secondary | tertiary | ghost | outline | danger | danger-soft
  size: 'md',          // Type checked: sm | md | lg
}

7. Semantic Styling — tv Slots + :ui

HeroUI separates logic (@heroui/react) from BEM-class styles (@heroui/styles). HareUI keeps the same separation of concerns, but each component's theme is a tailwind-variants slot object instead of BEM classes — overridden per-instance with :ui, or globally with createHareUI:

<!-- Per-instance override -->
<Button :ui="{ base: 'uppercase tracking-wider' }">Click me</Button>
// Global override (main.ts)
createHareUI({
  ui: {
    button: { slots: { base: 'uppercase tracking-wider' } },
  },
})

8. Developer Experience Excellence

Clear APIs, descriptive prop types, IntelliSense, and data-slot attributes that mirror HeroUI's — so existing HeroUI selector knowledge ([data-slot=button], [data-slot=card-header], …) carries over directly.

9. Complete Customization — HeroUI's Own Tokens

Beautiful defaults out of the box. Transform the entire look with CSS variables, exactly like HeroUI — HareUI copies HeroUI's token names and values verbatim, so existing HeroUI theme CSS (--accent, --radius, --surface, …) applies unchanged:

/* Theme-wide changes with variables */
:root {
  --accent: oklch(0.7 0.25 260);
  --radius: 0.375rem;
}

See Colors for the full token reference.

10. Open and Extensible

Override any slot, any variant, at any scope — per instance with :ui, or app-wide with createHareUI. Precedence (lowest to highest): theme file → createHareUI({ ui }) → variant props → :ui → class (root only). See Theming for the full precedence rules.

Differences from HeroUI React

HeroUI (React)HareUI (Vue)
Compound parts (Card.Header, Card.Title)Named slots (#header, #title)
isDisabled, isPending, isInvalid, isIconOnlydisabled, pending, invalid, iconOnly
onPress@click (native DOM events)
onChange / isSelected / isOpenv-model / v-model:open
classNameclass (root, tailwind-merged) / :ui (per-slot)
@heroui/styles BEM classestailwind-variants slot theme per component

On this page

No Headings