Button
shadcn/ui button (radix-nova) on DESIGN.md tokens. One filled action per view; everything else is bordered, soft or text. Press any of them: a 0.97 squash plus a ripple from the pointer, both ease-out.
import { Button } from "@stocklots/ui-components/components/button";
<Button variant="outline" size="sm">Save changes</Button>Variants
default = primary action · secondary = quiet fill · outline = bordered · ghost = text · destructive / success / warning / info / hold = soft status · link = inline
Website variants
brand = green fill, navy label · inverse = white outline, white label · inverse-brand = green outline, white label. For dark brand surfaces and photos (hero, CTA band, audience cards); never on the page surface.
Sizes
Heights 28 / 32 / 40 / 48 on the 4px grid.
Icon sizes
Square, same heights as the text sizes.
With icon
Mark the icon data-icon="inline-start" | "inline-end" so padding tightens on that side.
Disabled
50% opacity, not-allowed cursor, no hover reaction. Still receives pointer events so a tooltip can say why. Pair with visible copy that says why, e.g. "Approve (waiting on bank)".
Loading
loading reserves the width of both labels, disables the control and sets aria-busy. Toggle to compare.
Success and error
success / error show an animated check or cross with their text, keep the width, and re-enable the button so the user can retry. Press to run save → outcome → idle; error is paired with an inline message beside the button.
As child
asChild renders the styles onto another element, here a Next.js Link. Disabled becomes aria-disabled and the click is blocked.