Hacktron Design System

Links and buttons

Choosing between TextLink, a link button and a plain button.

The principle

Link blue means "this takes you somewhere". It is reserved for navigation that reads as content. Two questions decide every case:

  • What is it? If it navigates (changes the URL), it renders an <a>. If it does something on this page, it renders a <button>. Screen readers announce them differently, and only a link can open in a new tab.
  • How should it look? Link blue is for navigation that reads as content: a link inside a sentence, or a standalone reference. UI navigation and every action use a button style.

Which to use

What it is Renders Use Look Examples
Navigation inside a sentence<a>TextLinkLink blue, underlined, takes the sentence's size and weight"Add a payment method in billing settings", "Don't have an account? Sign up"
A standalone reference link<a>Button variant="link" with toLink blueSettings "Docs" links
Navigation that is part of the UI<a>Button variant="plain" with to, or the component's own link (LinkCell, row links, LinkCard)Gray, primary on hover"View all", "Configure", PR and ticket links, "Back to login"
An action: opens a modal, resends, switches a step, selects, saves<button>Button variant="plain", or another button variant (in an Alert, its #actions slot)Never link blue"Forgot password?", "Resend code", "Select all", "Edit", "End trial"
A Banner or Alert call-to-action that navigates<a>TextLink with toThe banner or alert color"Manage billing", "Change seat limit"

Links that leave the app

One icon, i-lucide-arrow-up-right, means "this leaves the app": a new tab, or a redirect to another site. It follows the common ground between Carbon(opens in new tab), GOV.UK(opens in new tab) and NN/g(opens in new tab).

  • Standalone links that open a new tab (LinkCell external, plain or link buttons to PRs, tickets, docs) show the arrow. Buttons take it as trailing-icon; LinkCell adds it itself.
  • Menu items that open a new tab (target: '_blank') take the arrow as their leading icon. DropdownMenu adds it when the item has none and appends the hidden "(opens in new tab)" text to the label.
  • Links inside a sentence show the arrow too. TextLink adds it after the text, sized to the sentence.
  • Every new-tab link is announced: TextLink, Button and LinkCell add hidden "(opens in new tab)" text for screen readers.
  • In-app navigation never shows the arrow, and i-lucide-external-link is not used for links: one icon, one meaning.
  • Open a new tab only when it keeps the user's place (docs or an external tool opened mid-flow, a PR opened from a table). Everything in-app opens in the same tab.

Guidelines

  • Give a button that only navigates a to, not @click="navigateTo(...)". A real link can open in a new tab and is announced as a link.
  • Use TextLink inside a sentence, a banner message or a form hint. A standalone "View all" or metadata link is Buttonvariant="plain" with to.
  • Inside a Table, use its own links: createLinkColumn, createRowLinkColumn or <LinkCell>.
  • Do not style an action as a link. "Forgot password?", "Resend code" and "Select all" act on the page, so they are variant="plain", not blue.
  • Do not use Button variant="link" without to. It logs a dev warning pointing to variant="plain".
  • Do not use TextLink without to, even in a Banner or Alert. An action there is a Button; TextLink logs a dev warning without to.

Examples

TextLink: link blue and underlined, sized by the sentence.

Add a payment method in billing settings to keep scanning.

<template>  <p>    Add a payment method in <TextLink to="/billing">billing settings</TextLink> to keep scanning.  </p></template>

Standalone reference

Button variant="link" with to.

<template>  <Button variant="link" size="xs" to="https://www.hacktron.ai/docs" target="_blank" trailing-icon="i-lucide-arrow-up-right">    Docs  </Button></template>

Button variant="plain" with to: gray, primary on hover.

Recent scans View all
<template>  <div class="flex items-center justify-between">    <span>Recent scans</span>    <Button variant="plain" size="xs" to="/scans">View all</Button>  </div></template>

An action

Button variant="plain" with @click: never link blue, even inside a sentence.

2 filters applied.

<template>  <p>    2 filters applied.    <Button variant="plain" size="sm" @click="clearFilters">Clear filters</Button>  </p></template>

Alert call-to-action

TextLink with to in a colored Alert takes the alert color. An action there is a Button in the #actions slot.

Seat limit reached
You have used all available seats. Upgrade plan
<template>  <Alert variant="warning" title="Seat limit reached">    <template #description>      You have used all available seats.      <TextLink to="/billing">Upgrade plan</TextLink>    </template>  </Alert></template>