Skip to content

Button

Every call to action: a link when it has href, a button otherwise.

import { Button } from '@zitdevs/ui';
Read the guide
<Button label="Book a consultation" icon="arrow-right" />
<Button label="See services" variant="secondary" />
<Button label="Download" variant="ghost" icon="download" iconPosition="before" />
<Button label="Cancel" variant="quiet" />
<Button label="Read the guide" variant="link" href="/guide" icon="arrow-right" />

inverse, inverseOutline and linkInverse are for the primary and the inverse band.

[email protected]
[email protected]

Minimum heights of 40, 48 and 56px. On the link variants, size sets the text size only.

<Button label="Sending" type="submit" loading />
<Button label="Unavailable" href="/booking" disabled /> {/* renders <button disabled> */}

A label that fits stays on one line, as drawn. One that does not wraps inside the button rather than running past its column, whatever text spacing the reader sets (WCAG 1.4.12).

asChild renders the button’s one child instead of its own <a>: the child brings the element and its routing props, the button its look, content and state. See Router links.

import Link from 'next/link';
<Button asChild label="See services" icon="arrow-right">
<Link href="/services" />
</Button>;
Prop Type Default Notes
label string required Visible text and accessible name.
variant ButtonVariant 'primary' primary secondary ghost quiet on light fields; inverse inverseOutline on a band; link linkInverse are text links.
size 'sm' | 'md' | 'lg' 'md' Minimum height 40 / 48 / 56px. On the link variants it sets the text size only.
icon IconName — Decorative; never part of the name. See Icon.
iconPosition 'before' | 'after' 'after' before gives way to the spinner while loading.
href string — Renders an <a>. Ignored while disabled.
target <a> target — Links only. _blank adds rel="noopener noreferrer".
type 'button' | 'submit' | 'reset' 'button' Buttons only.
disabled boolean false Always renders <button disabled>, even with href.
loading boolean false Spinner and aria-busy="true". Does not disable the control.
block boolean false Fills its container. No effect on the link variants.
asChild boolean false Renders its one child element (a router’s link) with the button’s classes and content. Ignored while disabled.
children ReactElement — With asChild, the element to render as. Its own children are replaced by the label and icon.
ref Ref<HTMLAnchorElement | HTMLButtonElement> — The element that renders.
className string — Merged last with cn(), so it can place the button or override a class.
anything else <button> attributes — name, value, form, formAction, aria-*, data-*, handlers: passed to the rendered element.

Exported types: ButtonProps, ButtonVariant, ButtonSize.

  • A control that navigates is a link; one that acts is a button. A disabled control is always a <button disabled>: an anchor cannot be disabled, and a dead link that looks live is worse.
  • A button is type="button" unless told otherwise, so it never submits a form by accident.
  • While loading, the label stays the accessible name and aria-busy says why nothing happens yet. The control stays enabled: the form handler ignores a second submit.
  • Icons and the spinner are hidden from assistive technology, and the label is always shown. For a control that shows only an icon, use IconButton, whose label is its accessible name.
  • The focus ring is drawn by the button itself: 2px of --ui-focus-offset then 2px of --ui-focus, which reaches 3:1 on light fields and on both bands. Forced-colours modes paint its outline instead.
  • Every size is at least 40px tall, above the 24px target minimum.
  • The spinner stops for readers who ask for reduced motion.

A <Button href> is a link and a <Button type="submit"> submits its form: both work in plain HTML. The component holds no state and renders as a Server Component.

primary, primary-hover, primary-active, primary-disabled, primary-soft, primary-soft-active, primary-line, primary-inverse, on-primary, on-inverse, ink, subtle, border-default, surface-hover, surface-active, and the focus ring.