Button

Triggers an action with solid primary, 1px ghost, or borderless subtle styling. Every target is at least 44px; icon size is a 44px square.

Print

Studio / Alkmaar

Issue 03 goes on the press at 06:00. Print is the primary action; the proof stays close for review.

Use it for

  • One primary action per view, with ghost for the secondary action.
  • Subtle for supporting actions in a toolbar or response. Its transparent surface stays unchanged while muted text turns to ink on hover.
  • Inside ButtonGroup, subtle buttons use an opaque paper surface and a soft fill for application-supplied aria-pressed selection.
  • Icon size for an icon-only action with a 44px square target and an accessible label.
  • Submitting a form or answering a dialog.

Not for

  • Navigation that changes the URL; use Link or a nav component.
  • Standalone persistent settings; use Toggle or Switch. ButtonGroup can present application-owned selection with aria-pressed.

Three ways in. They share one source, so the pixels match whichever you pick.

1. Import the package

npm install @noorddev/vlak-react

// once, next to your app's root
import "@noorddev/vlak-react/css";

import { Button, Icon } from "@noorddev/vlak-react";

Precompiled React and one stylesheet. No compiler to configure. Per-component imports work too: @noorddev/vlak-react/components/button.

2. Vendor the source

npx @noorddev/vlak-cli add button

The StyleX leaf lands in components/vlak/, for your own compiler to own. See StyleX.

3. Through shadcn

npx shadcn add https://vlak.dev/r/button.json

The same registry item, installed by shadcn's CLI.

CSS only

<link rel="stylesheet" href="node_modules/@noorddev/vlak/css/vlak.css" />

No React. Link vlak.css and use the markup and classes below.

import { Button, Icon } from "@noorddev/vlak-react";

<Button>Primary action</Button>
<Button variant="ghost" size="sm">Secondary</Button>
<Button variant="subtle">Details</Button>
<Button variant="subtle" disabled>Unavailable</Button>
<Button variant="subtle" size="icon" aria-label="Download">
  <Icon name="download" />
</Button>

Button

PropTypeDefaultDescription
variant"primary" | "ghost" | "subtle""primary"Solid ink primary, hairline ghost, or borderless subtle. One primary per view.
size"default" | "sm" | "icon""default"Icon buttons stay square at every breakpoint. Supply an accessible name.
groupedbooleanfalseFlush into a ButtonGroup: no own stroke, one ink seam.

Also accepts ButtonHTMLAttributes<HTMLButtonElement>.

KeysDoes
TabMoves focus to the button
Enter, SpaceActivates it
<button class="rs-btn-primary">Primary action</button>
<button class="rs-btn-ghost">Secondary</button>
<button class="rs-btn-subtle">Details</button>
<button class="rs-btn-subtle" disabled>Unavailable</button>
<button class="rs-btn-subtle rs-btn-icon" aria-label="Download"><svg viewBox="0 0 16 16" width="16" height="16" fill="none" stroke="currentColor" aria-hidden="true"><path d="M3.5 11.5V13h9v-1.5M8 3.5v7M5.5 8 8 10.5 10.5 8" /></svg></button>
.rs-btn-primary.rs-btn-ghost.rs-btn-subtle.rs-btn-sm.rs-btn-icon.rs-btn-grouped.rs-btn-grouped-ghost.rs-btn-grouped-subtle