Button
A polymorphic, accessible, and composable action component designed for primary interactions. Built on @nofinite/nuicss design tokens and superclasses, Button supports visual variants, size scales, loading indicators, icon slots, and framework routing via slot-based polymorphism (asChild).
import { Button } from '@nofinite/nui';Interactive Preview
Variants
Choose from 6 visual styles to clearly express hierarchy and action intent:
| Variant | Superclass | Purpose |
|---|---|---|
primary | btn-primary | High-emphasis primary call-to-action on a screen or card |
default | btn-default | Standard secondary action (neutral background) |
outline | btn-outline | Bordered action with transparent background |
ghost | btn-ghost | Low-emphasis toolbar or utility action |
danger | btn-danger | Destructive actions (delete, terminate, purge) |
link | btn-link | Understated inline navigation action styled as a link |
<div className="flex flex-wrap items-center gap-3">
<Button variant="primary">Primary Action</Button>
<Button variant="default">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="danger">Delete</Button>
<Button variant="link">Learn more</Button>
</div>Sizes
Buttons support 4 size scales to adapt across dense data tables, standard forms, and prominent hero layouts:
<div className="flex flex-wrap items-center gap-3">
<Button size="sm" variant="primary">Small</Button>
<Button size="md" variant="primary">Medium</Button>
<Button size="lg" variant="primary">Large</Button>
<Button size="icon" variant="outline" aria-label="Add bookmark">★</Button>
</div>| Size | Superclass | Common Use Case |
|---|---|---|
sm | btn-sm | Compact table rows, dense toolbars, data grid actions |
md | btn-md | Standard dialog actions, forms, settings screens (default) |
lg | btn-lg | Landing page CTAs, onboarding steps, hero banners |
icon | btn-icon | Square square-aspect icon action buttons (e.g. close, search) |
States & Icons
Loading & Disabled
Built-in loading indicator that preserves the button's layout dimensions, prevents duplicate submissions, sets aria-disabled="true", and displays an accessible spinner:
<div className="flex flex-wrap gap-3">
<Button loading variant="primary">Saving changes...</Button>
<Button disabled variant="outline">Disabled</Button>
</div>Both loading and isLoading props are supported interchangeably for developer ergonomics.
Icons (iconLeft & iconRight)
Prepend or append icons while maintaining optical alignment and spacing:
import { Button } from '@nofinite/nui';
import { ArrowLeft, ArrowRight, Download } from 'lucide-react';
export default function IconButtons() {
return (
<div className="flex gap-3">
<Button iconLeft={<ArrowLeft size={16} />} variant="outline">
Back
</Button>
<Button iconRight={<ArrowRight size={16} />} variant="primary">
Next
</Button>
<Button iconLeft={<Download size={16} />} variant="default">
Export CSV
</Button>
</div>
);
}Polymorphism (asChild)
Use asChild to delegate rendering to a framework link component (such as Next.js Link or React Router Link). This ensures proper client-side routing while inheriting complete button styling, focus rings, and keyboard accessibility:
import Link from 'next/link';
import { Button } from '@nofinite/nui';
export default function NavCTA() {
return (
<Button asChild variant="primary">
<Link href="/dashboard">Go to Dashboard</Link>
</Button>
);
}NUICSS Superclass Mapping
Under the hood, @nofinite/nui maps all variants and sizes directly to @nofinite/nuicss superclasses. If you are building pure HTML templates or headless components, you can use the exact same classes:
<!-- Equivalent pure CSS usage -->
<button class="btn btn-primary btn-md">Primary Button</button>
<button class="btn btn-danger btn-sm">Delete</button>
<a href="/login" class="btn btn-outline">Sign In</a>Keyboard Interaction
| Key | Behavior |
|---|---|
| Enter | Activates the button |
| Space | Activates the button |
| Tab | Moves focus to the button |
| Shift + Tab | Moves focus to previous focusable element |
Accessibility
- Renders a native
<button>element with semantic attributes. - Automatically handles
disabledandaria-disabled="true"when inloadingordisabledstates. - When using
size="icon", always provide an explicitaria-labelfor screen reader announcements. - Slot-based polymorphism (
asChild) preserves accessibility tags on underlying custom elements.
Best Practices
Do
- Use
variant="primary"for the single most prominent action in a layout or view. - Always provide an
aria-labelwhen rendering icon-only buttons (size="icon"). - Set
loadingduring async server actions to prevent duplicate submissions. - Use
asChildwhen navigating between routes.
Don't
- Do not place multiple competing
primarybuttons inside the same container. - Avoid using
variant="danger"for non-destructive actions. - Never nest a native
<button>inside an<a>tag; useasChildinstead.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
variant | enum | default | — |
size | enum | md | — |
isLoading | boolean | false | — |
loading | boolean | false | — |
iconLeft | ReactNode | — | — |
iconRight | ReactNode | — | — |
asChild | boolean | false | — |