Bridge view

Link

An interactive element that navigates the user to another resource — another page, an anchor within the current page, an external URL, a downloadable file, or a `mailto:` / `tel:` target. Distinct from Button (which performs an action without navigation). Renders as `<a href>` in HTML and inherits the entire native anchor contract: middle-click opens in a new tab, right-click exposes the URL, copy- as-link works, the URL is visible in the status bar.

When to use

Use

When the user needs to navigate to another resource — another page, an anchor on the current page, an external URL, a downloadable file, or a `mailto:` / `tel:` target. Default activator for navigation.

Avoid

For in-page actions that do not navigate (form submit, modal open, panel toggle) — that is `Button`. For the disclosure affordance of a menu — that is `MenuButton`. For decorative in-prose markers that should not be interactive — that is plain text (or a `<dfn>` / `<abbr>` element).

Versus related

  • button

    `Link` navigates; `Button` performs an action without navigation. Visual styling may converge (button-styled link, link-styled button), but the semantic choice never does — middle-click, copy-link, status-bar URL preview, and keyboard activation (Enter-only vs Enter+Space) all depend on the underlying element.

  • menu-button

    `MenuButton` opens a menu of options and exposes `aria-haspopup="menu"`. `Link` navigates immediately on activation. A "links-list rendered as a menu" pattern is either a real menu (MenuButton + menu items) or a navigation list with `<a>` items — never both.

Highlight
Fig 1.1 · Link · Bridge view
Both

Figma↔Code mismatches

Where designer and developer worlds typically misalign on this component.

  1. 01
    Figma

    An underlined text style drawn for inline links in body prose

    Code

    A `<a>` element with `text-decoration: underline` plus `:hover` reinforcement and `:focus-visible` ring

    Consequence

    Designers may rely on Figma's underline rendering and forget that `text-decoration` interacts with line-height and descender clearance differently per browser. Removing the underline entirely (relying on color alone) violates WCAG 1.4.1 (Use of Color); Figma can show a styled link without an underline and pass a visual review the rendered version would fail.

    Correct

    Document underline as the canonical default for inline links; colour-only differentiation is the documented WCAG failure case to avoid. Standalone and button-styled variants may opt out of underline only when they carry sufficient non-color affordances (button-styled buttons, distinct background).

  2. 02
    Figma

    A "link styled as button" variant drawn alongside actual buttons

    Code

    A `<a class="button">` with button visual treatment but anchor semantics

    Consequence

    Designers and developers diverge on whether the element is a Link or a Button — they look identical visually but their keyboard contracts differ (Enter only on `<a>`, Enter + Space on `<button>`), middle-click behavior differs, and form participation differs. The Figma file does not encode the semantic choice.

    Correct

    Treat the semantic question (does this navigate, or does it perform an in-page action?) as the canonical distinguisher. A "link styled as a button" remains a Link with anchor semantics; a button-styled `<a>` does not get Spacebar activation. Document the variant explicitly as `variant: button-styled` so the visual treatment is named and the semantic choice stays clear.

  3. 03
    Figma

    External link icon drawn as a separate static glyph next to the label

    Code

    A `::after` pseudo-element on `[target="_blank"]` or a slot-based icon driven by the `external` property

    Consequence

    Designers may forget the "external" indicator on individual links and developers may forget to set `rel="noopener noreferrer"`. The two artefacts disagree about which links go external; users cannot predict before clicking which links will open a new tab.

    Correct

    Model `external` as a boolean property. The Figma component exposes it as a Boolean; the code wires it to both the visual icon and the `target="_blank" rel="noopener noreferrer"` attributes plus an `aria-label` suffix or hidden text announcing "(opens in new tab)".

  4. 04
    Figma

    Disabled link drawn as greyed-out text with no underline

    Code

    There is no `disabled` attribute on `<a>` — `aria-disabled="true"` plus `tabindex="-1"` plus removed `href` is the closest equivalent

    Consequence

    Designers may treat "disabled link" as a real state; developers shipping it implement disabled inconsistently — sometimes the `<a>` is still keyboard-reachable but a no-op (confusing), sometimes it's removed from focus order entirely (announced as nothing).

    Correct

    Document that "disabled link" is not a native concept. Either replace the `<a>` with a span (no semantic), or use `aria-disabled="true"` plus an `onClick` guard plus removal of `href` (announces as a non-focusable disabled link in some SR). Prefer not rendering the link at all when the destination is genuinely unavailable.

Both

Variants, properties, states

Variants

Structurally different versions of the component.

inlinestandalonebutton-styled

Properties

The same component, parameterised.

PropertyType
externalboolean
downloadboolean
sizesm | md | lg
emphasissubtle | default | strong

States

Browser/user-driven (interactive) vs. app-driven (data).

KindStates
interactive
hoverfocus-visibleactivevisited
data
disabledloading
Both

Figma ↔ Code property map

FigmaTypeCodeNotes
VariantVariantvariantMaps inline / standalone / button-styled.
SizeVariantsizesm / md / lg. Mostly relevant for standalone variant; inline links inherit surrounding text size.
EmphasisVariantemphasissubtle / default / strong. Drives colour intensity and underline thickness.
ExternalBooleanexternalToggles trailing-icon visibility and adds `target="_blank" rel="noopener noreferrer"` plus a "(opens in new tab)" SR announcement.
DownloadBooleandownloadToggles trailing-icon visibility and sets the `download` attribute. Cross-origin caveat documented in mistakes.
Has Trailing IconBooleaniconTrailingSlot-visibility toggle; auto-true when external or download is true.
Trailing IconInstance SwapiconTrailingSwap the trailing-icon component (chevron, arrow, download-arrow, external-square).
LabelTextchildrenDefault slot — the link text.
VisitedBooleandata-visitedFigma-only state for design preview; in production the `:visited` pseudo-class drives the visual without a code prop.
Designer

Figma anatomy

Slot Figma type Hint
root text Inline text or auto-layout horizontal frame; bound to "destination" property documented separately
label text Inline text style; underline applied by variant treatment
icon-trailing instance Icon component instance; visibility bound to the relevant property
visited-marker rectangle 6×6 dot or label-color-shift; visibility bound to "visited" state
Dev

Code anatomy

Slot Code slot Semantic
root root anchor
label label text
icon-trailing icon-trailing presentational-or-img
visited-marker visited presentational
Dev

Cross-framework expression

FrameworkStructure mechanismVariant mechanism
Web Components A `<ui-link>` host element internally rendering a real `<a>` with `href` reflected from a `to` attribute; named slots for icon-trailing attributes (`variant="standalone"`, `external`, `download`, `size="md"`); reflected to host classes
React a single `<Link>` component that wraps `<a>` (or framework router's `<Link>` like Next.js, React Router); accepts `to` plus icon-trailing children props with class-variance-authority; `external` and `download` boolean props drive both attributes and visual indicator
Angular (signals) a `[uiLink]` directive on `<a>` plus optional `<ui-link>` component that wraps internal `<a>`; supports Angular Router `[routerLink]` for in-app navigation input<'inline' | 'standalone' | 'button-styled'>(); `[external]`, `[download]` host bindings reflect to attributes
Vue a `<Link>` SFC wrapping `<a>` (or `<router-link>` for Vue Router); slot for icon-trailing defineProps with literal-union types; `:external` boolean drives the indicator and rel attribute
Both

Internationalisation

RTL · mirroring

Underline and text alignment follow the document direction (LTR underline below the baseline; RTL same — underlines are direction-neutral). Trailing icons (external arrow, download arrow) move from inline-end to inline-start visually but remain semantically "trailing"; their glyphs do *not* mirror (a download arrow points down in both directions; an external-square glyph is direction-neutral). Numerals in link text follow the locale's preferred numeral system. Chevrons indicating jump direction (anchor-down, see-also-right) flip horizontally to preserve semantic direction.

Text expansion

Link labels can grow significantly. Inline links inside prose inherit line-wrap; standalone links may need a larger inline-size budget. Avoid hard-coding link min-width; use the label's natural width plus padding (button-styled variant only). Generic phrases like "Read more" / "Learn more" expand differently in DE / RU / FI — DE "Mehr erfahren" is shorter than "Read more"; RU "Подробнее" is longer; the canonical surface is sized for the longest expected expansion.

Both

Accessibility

Slot Accessibility hint
root Always render as `<a href="...">`. A link without `href` has no role, is not focusable, and is not announced as a link by SR. Avoid `<div>` or `<span>` with click handlers and `role="link"` — the native element is always preferable. Visited state must be perceivable (CSS `:visited` styled beyond the page baseline) for content where revisit recall matters.
label Plain text node. Avoid generic phrases ("read more", "click here") — they are uninformative when SR users navigate by link list. Distinguish links to the same destination from links to different destinations through the link text alone, not just through surrounding context.
icon-trailing Decorative when its meaning is also conveyed in the label text or the link's accessible name (e.g. "Download report, opens PDF" with a download icon). When the icon carries meaning the label omits, surface the meaning in `aria-label` on the link, not on the icon. Per WCAG, "opens in new tab" / "external link" must be announced for SR users.
visited-marker Visited state is rendered visually only. SR does not announce "visited" by default. For lists where revisit recall matters (search results, archives), pair the visual marker with an `aria-describedby` reference to a visually-hidden "visited" text — but only on links the user is meant to revisit-aware.
Both

Accessibility acceptance

Keyboard walk

KeysExpected
TabFocus enters the link in document order. Disabled-styled links (via aria-disabled) remain focusable so SR users hear the disabled-link state; truly unavailable links should be rendered as plain text instead.
EnterActivates the link — navigates to the `href`. Spacebar does not activate (anchors are Enter-only by native convention, unlike buttons).
Shift+Click or middle-click (when the underlying device supports)Opens the link in a new tab. Native `<a>` honours this for free; bespoke `role="link"` divs do not.

Screen-reader announcements

TriggerExpected
Focus enters an internal linkSR announces the link's accessible name followed by "link" (e.g. "Q3 report, link"). The destination URL is *not* spoken automatically; verbose verbosity modes may include it.
Focus enters an external link with `external: true`SR announces "<label>, link, opens in new tab" (or equivalent — "external link"). The "opens in new tab" portion comes from the canonical announcement pattern (visually-hidden suffix or `aria-label` extension).
Focus enters a download link with `download: true`SR announces the link followed by file metadata: "<label> (PDF, 2 MB), link". The metadata lives in the link text or an accessible-name suffix; SR does not infer it from the file extension.
Visited state setVisited is rendered visually only; SR does not announce "visited" by default. For revisit-aware lists, the accessible name may include a "visited" suffix via `aria-describedby`.

axe-core rules to assert

  • link-name
  • link-in-text-block
  • color-contrast
  • target-size
  • identical-links-same-purpose
Both

Common mistakes