Button
A clickable control that runs an action or navigates.
<template> <Button variant="primary" size="md">Button</Button></template>Props
| Name | Type | Default |
|---|---|---|
| variant | "link" | "primary" | "outline" | "secondary" | "destructive" | "ghost" | "plain" | "primary" |
| size | "md" | "sm" | "xs" | "md" |
| target | string | - |
| htmlType | "button" | "submit" | "reset" | "button" |
| ...props | UButton props (loading, block, as, icon, ui, ...) | - |
Slots
| Name | Description |
|---|---|
| default | Button label. |
| leftIcon | Icon before the label. Figma models this as a leftIcon boolean + fixed icon; adapted to a slot to match this codebase's existing icon-slot pattern. |
| rightIcon | Icon after the label. |
Guidelines
Do
- Use
variant="primary"for the single primary action in a view (e.g. "Save changes", "Run scan"). - Use
variant="destructive"only for actions that delete or revoke access, and pair it with a confirmation step. - Use
variant="outline",variant="secondary", orvariant="ghost"for secondary actions sitting next to a primary button.
- Use
variant="link"only for navigation that reads as a reference - a standalone link with atowhose destination is the point (a docs link). It renders as link-blue text; blue means "this takes you somewhere". It warns in dev withoutto. - Use
variant="plain"for an action tightly bound to other content (e.g. a sort icon inline in a table header) - no padding, no background ever (not even on hover), just a tertiary-to-primary text/icon color change on hover. This is also the standalone link: card-header "View all"/"Configure" (size="xs"), metadata, and ticket/PR links. A link inside a sentence isTextLink. - Use
variant="plain"for a text-style action - anything that doesn't navigate (open a modal, resend a code, switch a step, select all). Actions are never link-blue. - Pick between
link,plainandTextLinkwith the Links and buttons pattern page. - Give a button that leaves the app (
target="_blank", or a redirect to another site)trailing-icon="i-lucide-arrow-up-right"- the one "leaves the app" icon. Atarget="_blank"button also gets visually hidden "(opens in new tab)" text automatically. Never use the arrow, ori-lucide-external-link, on in-app navigation. - Keep button labels as short, specific verbs - "Run scan", not "Click here to run a scan".
Don't
- Don't put more than one
variant="primary"button in the same view - it removes the visual hierarchy that tells the user what to do next. - Don't use
variant="destructive"for non-destructive actions just to draw attention to them. - Don't use
variant="link"for an action, or for UI-chrome navigation (card-header "View all", metadata links) - the first isplain, the secondplainwithto.
- Don't navigate from
@click(navigateTo/router.push) when the button only navigates - passtoso it renders a real link (open in new tab, link semantics for screen readers). - Don't disable a button without a visible reason nearby (tooltip, helper text) - an unexplained disabled state reads as a bug.
Examples
Primary
The single primary action in a view - save changes, run a scan.
<template> <Button variant="primary">Button</Button></template>Secondary
A secondary action sitting next to a primary button.
<template> <Button variant="secondary">Cancel</Button></template>Outline
A secondary action with a visible border, for a lighter footprint than Secondary.
<template> <Button variant="outline">Outline</Button></template>Ghost
The lowest-emphasis action that still needs its own click target.
<template> <Button variant="ghost">Ghost</Button></template>Destructive
Actions that delete or revoke access - pair with a confirmation step.
<template> <Button variant="destructive">Delete</Button></template>Link
Navigation only: a standalone reference link with `to`, like a docs link. Never for an action.
<template> <Button variant="link" to="/design-system/text-link">Docs</Button></template>Plain
A text-style action (resend, select all, open a modal), or UI-chrome navigation with `to` like a card-header "View all".
<template> <Button variant="plain">Plain</Button></template>Sizes
The three height steps - xs, sm, and md.
<template> <Button variant="primary" size="xs">Button</Button> <Button variant="primary" size="sm">Button</Button> <Button variant="primary" size="md">Button</Button></template>Icons
A leading or trailing icon alongside the label.
<template> <Button variant="primary" icon="i-lucide-git-compare">Button</Button> <Button variant="primary" trailing-icon="i-lucide-git-compare">Button</Button></template>Related components
IconButton
An icon-only button.
<template> <IconButton icon="i-lucide-git-compare" variant="primary" size="md" /></template>Props
| Name | Type | Default |
|---|---|---|
| icon | string | - |
| variant | "ghost" | "primary" | "outline" | "secondary" | "plain" | "primary" |
| size | "md" | "sm" | "xs" | "md" |
| htmlType | "button" | "submit" | "reset" | "button" |
Guidelines
Do
- Use it only for actions whose icon is unambiguous on its own (close, delete, expand) - if you have to think about what the icon means, add a label instead and use
ButtonwithleftIcon/rightIcon. - Add an accessible label (e.g.
aria-label) since there's no visible text. - Use
variant="plain"for an icon tightly bound to other content (e.g. inline next to a table cell's text) - no padding, no background ever, just a tertiary-to-primary icon color change on hover.
Don't
- Don't use
variant="primary"for a purely decorative or low-stakes icon action - reserve it the same wayButton'svariant="primary"is reserved, for the one primary action in the view.
Examples
Primary
The one primary icon action in a view, reserved the same way Button's Primary is.
<template> <IconButton icon="i-lucide-git-compare" variant="primary" /></template>Secondary
A secondary icon action next to a primary one.
<template> <IconButton icon="i-lucide-git-compare" variant="secondary" /></template>Outline
A secondary icon action with a visible border.
<template> <IconButton icon="i-lucide-git-compare" variant="outline" /></template>Ghost
An icon action with no border or fill until interacted with.
<template> <IconButton icon="i-lucide-git-compare" variant="ghost" /></template>Plain
An icon tightly bound to other content, with no padding or background.
<template> <IconButton icon="i-lucide-git-compare" variant="plain" /></template>