Btn
A polymorphic button component supporting six visual variants, five sizes, loading state with spinner, and left/right icon slots.
Basic Usage
import { Btn } from "@paramanu/buttons-react"
export default function App() {return <Btn variant="primary">Click me</Btn>}<button class="pm-btn pm-btn--primary pm-btn--md">Click me</button>Variants
import { Btn } from "@paramanu/buttons-react"
export default function App() {return ( <div style={{ display: "flex", gap: "8px", flexWrap: "wrap" }}> <Btn variant="primary">Primary</Btn> <Btn variant="secondary">Secondary</Btn> <Btn variant="danger">Danger</Btn> <Btn variant="ghost">Ghost</Btn> <Btn variant="outline">Outline</Btn> <Btn variant="link">Link</Btn> </div>)}<div style="display: flex; gap: 8px; flex-wrap: wrap;"><button class="pm-btn pm-btn--primary pm-btn--md">Primary</button><button class="pm-btn pm-btn--secondary pm-btn--md">Secondary</button><button class="pm-btn pm-btn--danger pm-btn--md">Danger</button><button class="pm-btn pm-btn--ghost pm-btn--md">Ghost</button><button class="pm-btn pm-btn--outline pm-btn--md">Outline</button><button class="pm-btn pm-btn--link pm-btn--md">Link</button></div>Sizes
import { Btn } from "@paramanu/buttons-react"
export default function App() {return ( <div style={{ display: "flex", gap: "8px", alignItems: "center", flexWrap: "wrap" }}> <Btn size="xs">Extra Small</Btn> <Btn size="sm">Small</Btn> <Btn size="md">Medium</Btn> <Btn size="lg">Large</Btn> <Btn size="xl">Extra Large</Btn> </div>)}<div style="display: flex; gap: 8px; align-items: center; flex-wrap: wrap;"><button class="pm-btn pm-btn--primary pm-btn--xs">Extra Small</button><button class="pm-btn pm-btn--primary pm-btn--sm">Small</button><button class="pm-btn pm-btn--primary pm-btn--md">Medium</button><button class="pm-btn pm-btn--primary pm-btn--lg">Large</button><button class="pm-btn pm-btn--primary pm-btn--xl">Extra Large</button></div>Disabled
import { Btn } from "@paramanu/buttons-react"
export default function App() {return <Btn disabled>Disabled</Btn>}<button class="pm-btn pm-btn--primary pm-btn--md pm-btn--disabled" disabled aria-disabled="true">Disabled</button>Loading
import { Btn } from "@paramanu/buttons-react"
export default function App() {return ( <div style={{ display: "flex", gap: "8px" }}> <Btn loading>Save</Btn> <Btn loading loadingText="Saving...">Save</Btn> </div>)}<button class="pm-btn pm-btn--primary pm-btn--md pm-btn--loading" aria-busy="true" disabled>Loading</button>Full Width
import { Btn } from "@paramanu/buttons-react"
export default function App() {return <Btn fullWidth>Full Width</Btn>}<button class="pm-btn pm-btn--primary pm-btn--md pm-btn--full-width">Full Width</button>Accessibility
- Uses semantic
<button>element by default - Focus-visible ring for keyboard navigation (WCAG 2.2 AA compliant)
- Disabled buttons set both
aria-disabled="true"and thedisabledattribute - Loading buttons set
aria-busy="true"and are automatically disabled - Supports
aria-labelfor icon-only usage
Best Practices
- Use the right variant for the action’s importance:
primaryfor the main action,secondaryfor alternatives,dangerfor destructive actions,ghost/outlinefor low-emphasis actions, andlinkfor inline text actions. - One primary button per section to avoid competing calls to action.
- Use clear, concise labels with action verbs like “Save”, “Delete”, “Submit”.
- Indicate loading states for async actions to prevent double-clicks.
- Always include a
typeattribute in forms.
Do’s and Don’ts
| Do | Don’t |
|---|---|
Use variant="danger" for destructive actions | Style primary buttons red with custom CSS |
| Disable buttons when action is unavailable | Hide buttons and confuse users |
Use fullWidth for mobile or single-action layouts | Use fullWidth for every button |
Provide aria-label when using icon-only buttons | Leave icon-only buttons without accessible names |
React Props
The Btn component extends all native <button> HTML attributes.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "primary" | "secondary" | "danger" | "ghost" | "outline" | "link" | "primary" | Visual style variant |
size | "xs" | "sm" | "md" | "lg" | "xl" | "md" | Size preset |
disabled | boolean | false | Disabled state |
fullWidth | boolean | false | Stretch to container width |
loading | boolean | false | Loading state with spinner |
active | boolean | false | Active/pressed visual state |
loadingText | string | — | Text shown alongside spinner |
spinnerPlacement | "start" | "end" | "start" | Spinner position relative to text |
type | "button" | "submit" | "reset" | "button" | HTML button type |
leftIcon | ReactNode | — | Icon before label |
rightIcon | ReactNode | — | Icon after label |
spinner | ReactNode | — | Custom spinner element |
children | ReactNode | — | Btn content |
CSS Classes
| Class | Purpose |
|---|---|
pm-btn | Base button styles |
pm-btn--primary | Primary variant |
pm-btn--secondary | Secondary variant |
pm-btn--danger | Danger variant |
pm-btn--ghost | Ghost variant |
pm-btn--outline | Outline variant |
pm-btn--link | Link variant |
pm-btn--xs | Extra small size |
pm-btn--sm | Small size |
pm-btn--md | Medium size |
pm-btn--lg | Large size |
pm-btn--xl | Extra large size |
pm-btn--disabled | Disabled state |
pm-btn--full-width | Full width |
pm-btn--loading | Loading state |
pm-btn--active | Active state |
CSS Custom Properties
The button reads design tokens from @paramanu/tokens. Override at :root to customize:
:root { --pm-color-primary: #2563eb; --pm-color-danger: #dc2626;}