Design Principles

Core principles that guide HeroUI v3's design and development

Overview

HeroUI v3 follows 10 core principles that prioritize clarity, accessibility, customization, and developer experience.

Core Principles

1. Semantic Intent Over Visual Style

Components use semantic naming (primary, secondary, tertiary) instead of visual descriptions (solid, flat, bordered).

Inspired by Uber's Base design system, our variants follow a clear hierarchy:

Semantic Intent Hierarchy

// ✅ 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

Built on React Aria Components for WCAG 2.1 AA compliance, keyboard navigation, and screen reader support.

// Automatic ARIA attributes, keyboard navigation, and focus management
<Tabs defaultSelectedKey="profile">
  <Tabs.List aria-label="Settings">
    <Tabs.Tab id="profile">Profile</Tabs.Tab>
    <Tabs.Tab id="security">Security</Tabs.Tab>
  </Tabs.List>
  <Tabs.Panel id="profile">Content</Tabs.Panel>
  <Tabs.Panel id="security">Content</Tabs.Panel>
</Tabs>

3. Composition Over Configuration

Compound components let you rearrange, customize, or omit parts as needed.

// Compose parts to build exactly what you need
<Accordion>
  <Accordion.Item id="1">
    <Accordion.Heading>
      <Accordion.Trigger>
        Question Text
        <Accordion.Indicator />
      </Accordion.Trigger>
    </Accordion.Heading>
    <Accordion.Panel>
      <Accordion.Body>Answer content</Accordion.Body>
    </Accordion.Panel>
  </Accordion.Item>
</Accordion>

4. Progressive Disclosure

Start simple, add complexity only when needed.

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

// Level 2: Enhanced
<Button variant="primary" size="lg">
  <Icon icon="gravity-ui:check" className="mr-2" />
  Submit
</Button>

// Level 3: Advanced
<Button variant="primary" isDisabled={isLoading}>
  {isLoading ? <><Spinner size="sm" className="mr-2" /> Loading...</> : 'Submit'}
</Button>

5. Predictable Behavior

Consistent patterns across all components: sizes (sm, md, lg), variants, className support, and data attributes.

// All components follow the same patterns
<Button size="lg" variant="primary" className="custom" data-pressed="true" />
<Chip size="lg" variant="success" className="custom" />
<Avatar size="lg" className="custom" />

// Compound components use dot notation
<Alert.Icon />
<Card.Header />
<Accordion.Trigger />

6. Type Safety First

Full TypeScript support with IntelliSense, auto-completion, and compile-time error detection.

import type { ButtonProps } from '@heroui/react';

// Type-safe props and event handlers
<Button
  variant="primary"  // Autocomplete: primary | secondary | tertiary | danger | ghost
  size="md"          // Type checked: sm | md | lg
  onPress={(e) => {  // e is properly typed as PressEvent
    console.log(e.target);
  }}
/>

// Extend types for custom components
interface CustomButtonProps extends Omit<ButtonProps, 'variant'> {
  intent: 'save' | 'cancel' | 'delete';
}

7. Separation of Styles and Logic

Styles (@heroui/styles) are separate from logic (@heroui/react), enabling use with any framework or vanilla HTML.

Full Example: Tailwind Play

<!-- Use with plain HTML -->
<button class="button button--primary">Click me</button>

or with React:

// Apply styles to any component
import { buttonVariants } from '@heroui/react';

<Link className={buttonVariants({ variant: "primary" })} href="/home">
  Home
</Link>

8. Developer Experience Excellence

Clear APIs, descriptive errors, IntelliSense, AI-friendly markdown docs, and Storybook for visual testing.

9. Complete Customization

Beautiful defaults out-of-the-box. Transform the entire look with CSS variables or BEM classes.

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

/* Component-specific customization */
@layer components {
  .button {
    @apply uppercase tracking-wider;
  }
  .button--primary {
    @apply bg-gradient-to-r from-purple-500 to-pink-500;
  }
}

10. Open and Extensible

Wrap, extend, and customize components to match your needs.

// Custom wrapper component
const CTAButton = ({
  intent = 'primary-cta',
  children,
  ref,
  ...props
}: CTAButtonProps) => {
  const variantMap = {
    'primary-cta': 'primary',
    'secondary-cta': 'secondary',
    'minimal': 'ghost'
  };

  return (
    <Button ref={ref} variant={variantMap[intent]} {...props}>
      {children}
    </Button>
  );
};

Or extend with Tailwind Variants:

import { Button, buttonVariants } from '@heroui/react';
import { tv } from 'tailwind-variants';

// Extend button styles with custom variants
const myButtonVariants = tv({
  extend: buttonVariants,
  variants: {
    variant: {
      'primary-cta': 'bg-gradient-to-r from-blue-500 to-purple-600 text-white shadow-lg',
      'secondary-cta': 'border-2 border-blue-500 text-blue-500 hover:bg-blue-50',
    }
  }
});

// Use the custom variants
function CustomButton({ variant, className, ...props }) {
  return <Button className={myButtonVariants({ variant, className })} {...props} />;
}

// Usage
<CustomButton variant="primary-cta">Get Started</CustomButton>
<CustomButton variant="secondary-cta">Learn More</CustomButton>

Comparison with HeroUI v2

AspectHeroUI v2HeroUI v3
AnimationsFramer MotionCSS + GPU accelerated
Component PatternSingle components with many propsCompound components
VariantsVisual-based (solid, bordered, flat)Semantic (primary, secondary, tertiary)
StylingTailwind v4 partially supportedTailwind v4 fully supported
AccessibilityExcellent (React Aria powered)Excellent (React Aria powered)
Bundle SizeLarger (Bundle)Smaller (tree-shakeable)
Customization DifficultyMedium (Props-based)Simple (Compound components + Native CSS)