Hacktron Design System

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"
targetstring-
htmlType"button" | "submit" | "reset""button"
...propsUButton props (loading, block, as, icon, ui, ...)-

Slots

Name Description
defaultButton label.
leftIconIcon 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.
rightIconIcon 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", or variant="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 a to whose destination is the point (a docs link). It renders as link-blue text; blue means "this takes you somewhere". It warns in dev without to.
  • 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 is TextLink.
  • 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, plain and TextLink with 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. A target="_blank" button also gets visually hidden "(opens in new tab)" text automatically. Never use the arrow, or i-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 is plain, the second plain with to.
  • Don't navigate from @click (navigateTo/router.push) when the button only navigates - pass to so 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>

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>

IconButton

An icon-only button.

<template>  <IconButton icon="i-lucide-git-compare" variant="primary" size="md" /></template>

Props

Name Type Default
iconstring-
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 Button with leftIcon/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 way Button's variant="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>