@vinyasa/button
v2.0.3
Published
A styled, accessible button component with six visual variants, three sizes, configurable corner radius, icon slots, and a loading state — built on `@vinyasa/tokens`.
Readme
@vinyasa/button
A styled, accessible button component with six visual variants, three sizes, configurable corner radius, icon slots, and a loading state — built on @vinyasa/tokens.
Installation
pnpm add @vinyasa/button @vinyasa/tokens react react-dom@vinyasa/tokens is a peer dependency: Button's styles reference the shared design-token contract, and rendering it requires a VinyasaProvider (from @vinyasa/tokens) somewhere above it in the tree. See @vinyasa/tokens's README for what that provider does.
No separate stylesheet import is needed — Button's CSS is bundled into its own JS entry and loads automatically when you import the component.
This package has no root export (import Button from '@vinyasa/button/button', never from '@vinyasa/button'). Button is single-component, so there's no CSS-bloat reason for this the way there is for a multi-component package — it's here purely for consistency: every @vinyasa/* package is subpath-only, with no exceptions to remember.
Setup
Wrap your app once, near the root, in VinyasaProvider:
import { VinyasaProvider } from '@vinyasa/tokens';
import Button from '@vinyasa/button/button';
function App() {
return (
<VinyasaProvider>
<Button>Save changes</Button>
</VinyasaProvider>
);
}Usage
Variants
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="tertiary">Tertiary</Button>
<Button variant="danger">Danger</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>variant defaults to "primary".
Sizes
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>size defaults to "md".
Radius
<Button radius="sm">Sharper corners</Button>
<Button radius="md">Rounded rectangle</Button>
<Button radius="full">Pill (default)</Button>radius defaults to "full". Independent of size — pick any radius at any size.
Icons
<Button leadingIcon={<PlusIcon />}>Add item</Button>
<Button variant="outline" trailingIcon={<ArrowIcon />}>Continue</Button>Icons are rendered at 1em, so they scale automatically with whichever size is active.
Loading
<Button loading>Saving</Button>While loading is true, the button is disabled, gets aria-busy="true", and its leadingIcon (if any) is replaced by a spinner. The label stays visible so the button doesn't change width.
Disabled
<Button disabled>Can't click me</Button>Any other native <button> attribute (onClick, type, id, aria-*, ...) can be passed directly — ButtonProps extends ComponentPropsWithoutRef<'button'>.
Ref forwarding
const ref = useRef<HTMLButtonElement>(null);
<Button ref={ref}>Save</Button>;Props
| Prop | Type | Default | Description |
| -------------- | ---------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------ |
| children | ReactNode | — | Required. The button's label. |
| variant | 'primary' \| 'secondary' \| 'tertiary' \| 'danger' \| 'outline' \| 'ghost' | 'primary' | Visual style. |
| size | 'sm' \| 'md' \| 'lg' | 'md' | Size. |
| radius | 'sm' \| 'md' \| 'full' | 'full' | Corner rounding, independent of size. |
| loading | boolean | false | Shows a spinner in place of leadingIcon, forces disabled. |
| leadingIcon | ReactNode | — | Rendered before children. Hidden while loading. |
| trailingIcon | ReactNode | — | Rendered after children. Hidden while loading. |
| disabled | boolean | false | Native disabled state. |
| type | 'button' \| 'submit' \| 'reset' | 'button' | Defaults to 'button' (not the native 'submit') to avoid accidental form submits inside a <form>. |
All other native <button> props (onClick, id, aria-*, name, ...) pass through unchanged.
Subpath import
import Button from '@vinyasa/button/button';There is no root @vinyasa/button entry, so this is the only way to import it. See "This package has no root export" above.
Development
From the repository root:
pnpm --filter @vinyasa/button build
pnpm --filter @vinyasa/button test
pnpm --filter @vinyasa/button lint
pnpm storybook # Components/Button — Playground, Variants, Sizes, Radii, Disabled, Loading, WithIcons