Button
Triggers a user action. The main action of a screen or form. Follows shadcn structure with the Yuno primary color.
Variants
<Button>Continue</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="link">Link</Button>Sizes
<Button size="sm">Small</Button>
<Button>Default</Button>
<Button size="lg">Large</Button>
<Button size="icon"><Plus weight="light" /></Button>With icons
<Button>Continue <ArrowRight weight="light" /></Button>
<Button variant="outline"><MagnifyingGlass weight="light" /> Search</Button>
<Button><Trash weight="light" /> Delete rule</Button>States
<Button disabled>Disabled</Button>
<Button variant="outline" disabled>Disabled</Button>When to use
- As the primary CTA of a screen or section.
- For Submit / Save / Cancel inside a form.
- To trigger a Dialog or Sheet.
- For row-level actions in tables (usually variant='ghost' or 'outline').
When not to use
- For navigation between pages — use next/link with anchor styling.
- As a long list of options — use a Select or a Menu instead.
- For toggles or booleans — use Switch or Checkbox.
Usage
- One Primary button per screen.
- Include a Phosphor icon with the label when it adds meaning (arrow for continue, trash for delete).
- For destructive actions (delete rules, lists, routes), use variant='default' (Primary) — clarity comes from the copy, not the color.
- Group Cancel + Confirm together with Cancel on the left, Confirm on the right.
- Don't stack multiple Primary buttons in the same view.
- Don't hardcode background colors — use variants.
- Don't use variant='destructive' (red) — it is not part of the Yuno Dashboard vocabulary.
- Don't use variant='link' when you actually mean a real link — use next/link.
- Don't make icon-only buttons without an aria-label.
Recipes
Ready-to-copy compositions covering the most common Yuno usages of this atom.
One primary button per screen, with an optional trailing icon that reinforces the action (ArrowRight for continue).
<Button>
Continue <ArrowRight weight="light" />
</Button>Cancel (outline, on the left) + primary Confirm (right) inside a DialogFooter. Canonical pattern for confirmations.
<Dialog>
<DialogTrigger asChild>
<Button variant="outline">Delete rule</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete rule?</DialogTitle>
<DialogDescription>
Transactions matching this rule will stop being evaluated.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button>Delete rule</Button>
</DialogFooter>
</DialogContent>
</Dialog>3-dots (DotsThreeOutline, fill) trigger with the Yuno table-row hover style (border + bg + shadow-xs on hover, tokens on data-[state=open]). Wrap in a Tooltip.
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<button
type="button"
aria-label="Actions"
className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-transparent bg-transparent text-muted-foreground transition-all hover:border-border hover:bg-background hover:text-foreground hover:shadow-xs data-[state=open]:border-border data-[state=open]:bg-background data-[state=open]:text-foreground"
>
<DotsThreeOutline weight="fill" className="size-4" />
</button>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={6}>Actions</TooltipContent>
</Tooltip>
</TooltipProvider>Button disabled while a request is in flight, with a spinner icon replacing the leading icon. Prevents double-submits.
function LoadingButton() {
const [loading, setLoading] = React.useState(false);
return (
<Button
onClick={() => { setLoading(true); /* fire request */ }}
disabled={loading}
>
{loading ? (
<><CircleNotch weight="light" className="animate-spin" /> Saving…</>
) : (
<><Pencil weight="light" /> Save rule</>
)}
</Button>
);
}Import
Copy this import line at the top of the file where you compose this atom.
import { Button } from "@/components/ui/button";
import { ArrowRight } from "@phosphor-icons/react/dist/ssr";Props
Everything else from the underlying HTML or Radix primitive is forwarded via ...props.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "secondary" | "outline" | "ghost" | "link" | "default" | Visual variant. Note: destructive is not used at Yuno — see the callout below. |
| size | "default" | "sm" | "lg" | "icon" | "default" | Height + padding preset. Use 'icon' for square icon-only buttons (size-9). |
| asChild | boolean | false | Merge props onto the child element (Radix Slot). Use to make the Button render as an <a> or <Link>. |
| disabled | boolean | false | Native disabled attribute. Dims the button and blocks interaction. |
| type | "button" | "submit" | "reset" | "button" | Native button type. Always set 'submit' explicitly when the Button is inside a <form>. |
| className | string | — | Merged with the variant class list via cn(). |
Related
Cross-links to atoms and patterns you may reach for next.
- DialogThe canonical place to put a delete / confirmation Button pair.
- TooltipWrap icon-only buttons to expose their meaning to sighted + AT users.
- Dropdown menuFor 3+ related actions grouped behind one trigger.
- ToggleWhen the button represents a persistent on/off state (like bold in a rich text editor).
Not used at Yuno
Use Primary instead
For deletes and other 'destructive' flows, use the Primary button inside a Dialog. Clarity comes from the copy ('Delete rule?'), not from the color.
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost" size="sm">
<Trash weight="light" /> Delete rule
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete rule?</DialogTitle>
<DialogDescription>
Transactions matching this rule will stop being evaluated.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button>Delete rule</Button>
</DialogFooter>
</DialogContent>
</Dialog>