Button
A clickable action trigger with token-driven variants and sizes.
Button is the primary action trigger of the design system. It is styled entirely through
design tokens, so switching themes restyles it with no markup changes.
Installation
pnpm add @infinitibit_gmbh/ui @infinitibit_gmbh/theme-default
Usage
import '@infinitibit_gmbh/theme-default/theme.css';
import '@infinitibit_gmbh/ui/styles.css';
import { Button } from '@infinitibit_gmbh/ui';
<Button variant="primary" size="md">
Save
</Button>;
Variants
Three visual styles: primary (filled), secondary (outlined), and tertiary (text-only).
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="tertiary">Tertiary</Button>
Sizes
Four sizes: xs, sm, md (default), and lg.
<Button size="xs">Extra small</Button>
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
Destructive
Add destructive to any variant to signal a dangerous action.
<Button variant="primary" destructive>
Delete
</Button>
Icons
Add a leading or trailing icon with leftIcon / rightIcon — always via the
Icon API, never a raw SVG.
import { Button } from '@infinitibit_gmbh/ui';
import { Icon } from '@infinitibit_gmbh/ui/icons';
<Button leftIcon={<Icon name="search" />}>Search</Button>;
Composition (asChild)
Set asChild to render the Button's styling and semantics onto your own child
element instead of a <button> — the standard pattern for a Button-as-link. The
button classes, props, and ref are merged onto the child, and the icon slots compose
inside it, so the link looks and reads exactly like a button.
import { Button } from '@infinitibit_gmbh/ui';
import { Icon } from '@infinitibit_gmbh/ui/icons';
// Renders <a class="ib-btn …" href="/docs"><icon/>Read the docs</a> — no <button>.
<Button asChild leftIcon={<Icon name="search" />}>
<a href="/docs">Read the docs</a>
</Button>;
Pass exactly one child element (Radix Slot merges onto a single child). Choose a
child with the right native semantics — <a href> for navigation, <button> for actions
— so assistive tech announces the correct role.
API
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | — | Render as the single child element instead of a <button>, merging the
button styling/semantics + props/ref onto it (Radix Slot). Use for a
Button-as-link (<a>) or any custom interactive element. |
destructive | boolean | false | Swaps the active variant to the critical/danger ramp. |
leftIcon | ReactNode | — | Icon before the label — use the Icon API, e.g. <Icon name="search" />. |
rightIcon | ReactNode | — | Icon after the label — use the Icon API. |
size | "xs" | "sm" | "md" | "lg" | md | Button size. |
variant | "primary" | "secondary" | "tertiary" | primary | Visual style: filled, outlined, or text-only. |
Plus the className escape hatch (appended classes) and all native <button> attributes
(disabled, onClick, type, …).
This table is generated from the component's TypeScript source by the stable catalog (issue #51) and kept in sync by the
verify:cataloggate — no hand maintenance.
Server & client
Button is universal — it has no client-only hooks or state, so it renders in React
Server Components (zero client JS for a static button) and in client components alike. Add
interactivity (onClick) from your own client component; the icon slots accept any node.
Accessibility
- Renders a native
<button>— focusable, announced by assistive tech, and operable via Enter and Space. :focus-visibleshows a keyboard-only focus ring (hidden for mouse clicks).disableduses the native attribute — correct semantics and removed from the tab order.- Verified by the automated axe + keyboard gates plus a Storybook interaction test.