Skip to sheet

C-501Button

Button

A button starts an action. One ink button carries the primary action on a view; everything else is secondary. Buttons rest on the page with an edge, and press in when you click.

Overview

Five looks, from loudest to quietest. Choose by how much the action matters on this view, not by how it should look.

Anatomy

Four parts. The edge and the label do the work; the icon is optional and the focus ring appears only for the keyboard.

  1. Container and edgeborder-edge with shadow-edge, 8px radius, 40px tall by default.
  2. LabelAsap 14/24 semibold, sentence case, a verb first.
  3. IconOptional, 16px, before the label. Decorative: aria-hidden.
  4. Focus ring2px cyan outline, offset 2px. Keyboard focus only.

Variants & sizes

Every state, every variant. Hover is a notch darker, press adds an inset, focus adds the cyan ring, disabled drops to half opacity.

Variantresthoverpressedfocusdisabled
Primary · default
Secondary · outline
Ghost
Destructive
Link

Sizes

xs · 24pxsm · 32pxdefault · 40pxlg · 48px

Icon only

icon-xsicon-smiconicon-lg

Usage

One primary per view. Labels say what happens, in sentence case, starting with a verb.

DoOne ink button for the main action; the rest are secondary.
Don'tSeveral ink buttons in a row. When everything is primary, nothing is.
DoSay what happens: “Retry import”, “Delete project”.
Don'tVague or shouting labels: “OK”, “Submit”, “CLICK HERE”.

Accessibility

Role
A real <button>, or a link rendered through render when it navigates. Never a div with a click handler.
Name
The visible label. Icon-only buttons need aria-label, and the icon stays aria-hidden.
Keyboard
Enter and Space activate. Focus shows the 2px cyan outline; never remove it.
Contrast
White on ink is 18.9:1 and ink on vellum 17.6:1. The edge is decoration; the label carries the meaning.
Disabled
Disabled buttons can’t take focus. When an action is blocked, say why next to it.

Props

PropTypeDefaultNotes
variant"default" | "secondary" | "outline" | "ghost" | "destructive" | "link""default"outline and secondary draw the same button; both names stay for existing call sites.
size"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg""default"Heights 40, 24, 32 and 48. Icon sizes are square.
renderReactElement—Base UI: render as a link. Pass nativeButton={false} with an <a>.
disabledbooleanfalseHalf opacity and no pointer events. Prefer explaining why nearby.
…button propsComponentProps<'button'>—type, onClick, aria-* and the rest pass through.
import { Button } from "@/components/ui/button"

<Button>Send from Revit</Button>
<Button variant="secondary" size="sm">Download</Button>
<Button nativeButton={false} render={<a href="/portal" />}>Open the Portal</Button>