Components / navigation

Link

The underline is on by default rather than off, which is the opposite of most systems' inline links. The reason is the one above: an inline link with no underline is identified by color alone.

Rendered from the package · open on its own

Summary

A native anchor with the contract applied.

APG patternLink
Built onnative elements
Statusstable
Since0.1.0

Use it for anything that navigates, and a Button for anything that acts.

<Link href="/projects/northlight">Northlight</Link>
<Link href="https://studioedit.app" external icon={<Icon path={ICONS.arrowRight} />}>studioedit.app</Link>
<Link href="/settings" emphasis="standalone" intent="neutral">Account settings</Link>

Anatomy

An <a data-se="link">. It owns its contents.

PartElementRequired
iconthe caller'sthe caller's
itemthe caller'sthe caller's
sr<span>yes

Properties

PropertyValuesDefaultWhat it does
hrefstringrequiredRequired. An anchor without one is not a link — role generic, no focus, no activation. Use <Button> for something that acts rather than navigates.
emphasisinline · standaloneinlineinline sits in a sentence and stays underlined. standalone is the whole line.
intentaccent · neutralaccentaccent for a link in the reading flow, neutral where a row of links would otherwise shout.
externalbooleannoneOpens in a new tab, sets rel="noopener noreferrer", and says so to a screen reader.
iconReact.ReactNodenoneAn <Icon> marking an external destination.

States

StateExpressed as
Pressed:active
Hover:hover

Behavior

It navigates. external sets rel="noopener noreferrer" and appends screen-reader text.

Accessibility

The link text is the accessible name, so it has to make sense read alone. A page of links all named "Read more" is a list of identical entries to anyone navigating by link.

external says so out loud. It appends "(opens in a new tab)" in data-part="sr" — a link that leaves the site without saying so is a small betrayal, and a reader who has lost their place cannot get it back with the back button.

Space does not activate a link, and Enter does. That is the platform, and it is the reason the Link/Button distinction is not cosmetic.

href is required, and it was not always. An anchor without one has role="generic", takes no focus and does nothing on Enter — while reading as ordinary text, which is why the omission survived review for so long.

Keyboard

KeyResult
EnterFollows the link
SpaceNothing — the platform reserves it for scrolling

Roles, states and properties

_None written. The elements carry their own._

Focus

A normal tab stop. That Space does nothing here and everything on a Button is the clearest reason the two must not be swapped.

Screen reader

Not recorded as verified. Stage 4 of the readiness ladder — driving each component with VoiceOver and NVDA — is unstarted across the package, and no component should claim otherwise until docs/journal/ records the session that did it.

Contrast and target size

npm run check:contrast audits every color pair this component uses across all four skins, light and dark, and fails the build on a shortfall. Two waivers are on the record in docs/audit/DEFERRED.md.

It sets no target size of its own; anything interactive inside it brings its own.

Tested

  • test/apg/partials-sweep.test.jsx
  • test/browser/icon-box.spec.mjs
  • test/browser/site.spec.mjs
  • test/browser/upstream-natives.spec.mjs

Known gaps

_No upstream requirement is recorded as not applicable._

Appearance

TokenUsed for
--se-accentcolor
--se-border-widthtext-decoration-thickness
--se-duration-instanttransition
--se-ease-standardtransition
--se-mix-downcolor
--se-mix-shadecolor
--se-radius-smborder-radius
--se-space-1margin-inline-start
--se-textcolor
--se-text-mutedcolor
--se-weight-mediumfont-weight

Motion

Color crosses --se-duration-instant on hover. The underline does not animate — a moving underline under text being read is noise.

Content

Name the destination, not the act of going there. Account settings, not Click here for account settings.

Sentence case, and no terminal punctuation inside a sentence.

Usage

If it changes the address, it is a Link. A link that is really a button cannot be triggered with Space; a button that is really a link cannot be opened in a new tab. This is a governance question rather than a styling one.

emphasis="standalone" drops the underline and is only safe when the link is the whole of its line. Inside a sentence, keep it: color alone fails roughly one man in twelve.

When not to use it. For an action styled to look quiet. That is Button with emphasis="transparent".

It sets no size, and that is the feature. A link takes the family, size and weight of the text it sits in — verified in the specimen across lead, body, ui and eyebrow, including the narrow face the last of those switches to. So there is no size prop and there should not be one: a link in a heading is heading-sized because it is in a heading, and a prop would let the two disagree.

What it does set is the underline and the color, which are the only things that make it a link rather than the words around it.

Notes

The underline is on by default rather than off, which is the opposite of most systems' inline links. The reason is the one above: an inline link with no underline is identified by color alone.