Text that goes somewhere, in three sizes or the size of the text around it, with an optional icon; underlined, unless it stands on its own. As a button it stays in the color of the words around it and opens something where it stands.
Options
| Choice | Values | Default | What it does |
|---|---|---|---|
sizesmbk-link--size-<value> | lgmdsminherit | lg | Figma calls these Large, Medium and Small, each a body preset; inherit takes the size of the text around it, as in a heading or a card title. |
standalonesmbk-link--standalone | On or off | Off | A link standing on its own, outside running text: underlined on hover and press only. |
pressablesmbk-link--pressable | On or off | Off | Words that are pressed rather than followed, such as a name that opens its card: a button in the color of the text around it, its line always there. |
Examples
Standalone
A link standing on its own, in a list or a card: underlined on hover and press only.
Inherited size
Takes the size of the text around it, here a heading and a card title; the icon takes it too.
Guidelines
Purpose
Text that takes a person somewhere. It is set in the body face at body sizes so that it sits inside running text or stands alone as an inline action, and it carries the link color family (rest, hover, pressed, disabled) and an underline. Inside running text the underline is always there; a link standing on its own shows it on hover and press only. Inside a sentence it is part of the sentence: it wraps across lines like the words around it.
With as="button" the same words open something where they stand instead of going anywhere: a name that opens its card, or words that open a part of the page. They keep the color and size of the text around them, and the link's underline says they can be pressed.
When to use
- Navigation inside text: "read the guide", "see all".
- A standalone inline action that reads as text, not as a control.
- With
as="button": a piece of text that opens something about what it names, such as a name in a table cell or in a line of words that opens its card. It is the popover's opener when the words should stay words. - With
as="button": words in a sentence that open a part of the page and have no address of their own.
When not to use
- For an action that changes state on the page, such as saving or deleting. Use a button, ghost tone if it should read lightly, even where it could be pressed words: pressed words open something, and a control that changes things should look like one.
- With
as="button", for anything with an address. A real link opens in a new tab and copies its address, so give ithrefand leaveasout. - As a button visually. The button wrapper can render a link with button classes when that is truly needed.
Usage rules
Do
- Keep the underline on links inside paragraphs; set
standaloneonly on a link that stands on its own, in a list, a footer or a card. - Keep a link's words in the label part when writing the markup by hand. A link with an icon draws its underline on the label, so words outside it would leave the line running under the icon.
- Match the link size to the surrounding text's preset. Inside a heading or a title, which no body size matches, set the size to
inherit: the link takes the text's face and keeps its underline. - Use
as="button"for a name that opens its card, in place of a button with its look taken off.
Don't
- Attach a click handler to a link to make it act like a button. Use
as="button", which has the role and the keys. - Put a link inside a button or a label.
- Put a space between the label and an icon when writing the markup by hand. The line may break at that space and leave the icon alone on the next line.
- Put a link that belongs to a sentence in a flex or grid row with the text around it. A flex or grid parent turns the link into a block, which wraps on its own instead of with the words; keep it directly in the sentence's text.
Keep the underline on links inside a paragraph, so they stand out from the words around them without relying on color.
A standalone link inside a sentence is marked by color alone and disappears for readers who cannot tell the colors apart.
Anatomy
- The block, a real anchor. With
as="button", inline words with a button's role and keys, because a real button is one box that cannot break across lines with the words around it. - The label part, the text. In a link with an icon, the label draws the underline, so the line stops where the words stop.
- The icon part, before or after the label. Optional. Figma hides the leading icon by default and shows a trailing one. It sits in the line like one more letter of the word beside it, so the two are never split across lines.
Behavior
Hover darkens the text; pressed uses the pressed color. The underline sits a small gap below the words. It is quiet at rest, stronger on hover and strongest while pressed, and it is see-through at rest and on hover, so it looks the same on any surface. A standalone link draws its underline on hover and press. A disabled link keeps its text and loses its address. Without an address it is no longer a link, so a screen reader reads it as plain text; it does not say that a link is unavailable.
A link flows with the text it sits in. A long link inside a paragraph breaks across lines wherever the words around it would, and its underline follows it onto each line. An icon travels with its word: a trailing icon never starts a line on its own and a leading icon never ends one. The underline runs under the words only, never under the icon or the gap beside it, in every state. Standing alone in a flex or grid layout, a link is one line tall and wraps inside its own width if it has to.
On a touch screen a link standing on its own takes an invisible press area round its words, out to the comfortable touch target, and its look does not change. A link inside a sentence does not, so a press on the words around it never follows the link.
With as="button" the words take the color of the text around them, and its size unless size says otherwise. Under the pointer they keep that color and the line grows stronger; pressed, the words turn to the subtle text color and the line is at its strongest. Nothing but the line tells them from the words beside them, so the line stays, in a sentence or on their own, and standalone only gives them the press area on a touch screen. There is no disabled form: when there is nothing to open, write plain text.
As the popover's trigger the words stay in their line and break with it. A paragraph cannot hold the panel, so inside a p element put the popover after the paragraph and name the words with openerId.
Accessibility
What it does for you
- It is a real anchor with an address, so Enter follows it. A link without an address is not a link; the disabled state removes the address on purpose, which leaves plain text behind.
- With
as="button"it is a button to a screen reader and a Tab stop, and Enter and Space press it, once however long they are held. Its words name it. As a popover's opener it says that it opens a dialog and whether that dialog is open. - Focus draws the system ring, rounded, around the whole link; a link that wraps gets a ring around each of its lines.
- Nothing is cut short or pushed outside its container, so a link inside text still reads whole at twice the text size and at the narrowest reflow width (WCAG 1.4.4 Resize Text and 1.4.10 Reflow). Inline links wrap naturally, as the words around them do.
- A link inside running text is underlined at rest, so it can be told from the words around it without color (WCAG 1.4.1 Use of Color).
- Icons are decorative.
- On a touch screen a link standing on its own takes an invisible press area round its words, out to the comfortable touch target. A link inside a sentence does not, so a press on the words around it never follows the link.
What you must do
- Make the link text say where it goes, since the icons do not.
- Drop the underline at rest, with the standalone flag, only on a link that stands on its own, where its place already says it is a link.
- Use a button carrying the link's classes, not an anchor, for an action that sits among words and changes the page rather than going anywhere, such as a filter bar's Clear all. It is announced as a button and pressed with Space or Enter.
- Set
standaloneon pressed words that stand on their own, such as a name in a table cell, so they take the press area on a touch screen. Leave it off inside a sentence. - Write the role and the keys yourself when writing pressed words by hand:
role="button",tabindex="0", and Enter and Space pressing it.
How to test
- Tab to the link: the focus ring shows around the whole link, and around each line of one that wraps, in both themes and in a forced-color mode.
- Press Enter: the link is followed.
- With
as="button", press Enter and Space: each presses it once, the page does not scroll, and a screen reader calls it a button. - With a screen reader, it is read as a link by its text. A link with no address is read as plain text.
- With text at 200% and the page 320 pixels wide, a link inside a paragraph breaks across lines with the words around it and nothing is cut off.
- In a forced-color mode, a disabled link is drawn in the system's gray text color.
- On a touch screen, a press just outside a standalone link's words follows it; a press on the words around a link in a sentence does not.
Keyboard
| Key | What it does |
|---|---|
| Tab | Moves focus to the link. |
| Enter | Follows the link. |
| EnterorSpace | Presses words with as="button", and a button carrying the link's classes. |
Source
The files the link is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.
Stylesheet
/* smbk-link — Link. Figma "Link" 4129:21956.
* An inline box, not a flex box: a link inside a sentence wraps across lines
* like the words around it. The icon is inline too, so its glyph joins the
* word beside it as one more letter would: a line never breaks between them,
* and the icon travels with its word. */
.smbk-link {
display: inline;
color: var(--smbk-color-text-link);
font-family: var(--smbk-typography-body-lg-font-family);
font-size: var(--smbk-typography-body-lg-font-size);
font-weight: var(--smbk-typography-body-lg-font-weight);
line-height: var(--smbk-typography-body-lg-line-height);
letter-spacing: var(--smbk-typography-body-lg-letter-spacing);
text-decoration-line: underline;
text-decoration-color: var(--smbk-color-text-link-underline);
text-decoration-thickness: var(--smbk-border-width-link-underline);
text-underline-offset: var(--smbk-space-link-underline);
cursor: pointer;
-webkit-tap-highlight-color: transparent;
touch-action: manipulation;
}
/* A button can carry the link's classes for an action that sits among words and acts on the page rather than going
anywhere, such as a filter bar's Clear all: the button's own box goes, and the link's look stays. */
button.smbk-link {
margin: 0;
padding: 0;
border: 0;
background: none;
text-align: inherit;
appearance: none;
}
@media (hover: hover) {
.smbk-link:hover,
.smbk-link--standalone:hover {
color: var(--smbk-color-text-link-hover);
text-decoration-line: underline;
text-decoration-color: var(--smbk-color-text-link-underline-hover);
}
}
.smbk-link:active,
.smbk-link--standalone:active {
color: var(--smbk-color-text-link-pressed);
text-decoration-line: underline;
text-decoration-color: var(--smbk-color-text-link-underline-pressed);
}
.smbk-link:focus-visible {
outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
outline-offset: var(--smbk-focus-ring-offset);
border-radius: var(--smbk-radius-full);
}
.smbk-link[aria-disabled="true"] {
color: var(--smbk-color-text-link-disabled);
pointer-events: none;
}
/* A link standing on its own, outside running text, shows its underline on hover and press only.
Inside a sentence the underline stays, so a link never reads as plain words. */
.smbk-link--standalone {
text-decoration-line: none;
}
/* Words that are pressed rather than followed, such as a name that opens its card. They are the words of the text
around them, in its color, so the line is what says they can be pressed: it stays, standing on its own too, and
the words keep their color under the pointer. A press shows in the words as well as the line. */
.smbk-link--pressable {
color: inherit;
text-decoration-line: underline;
}
@media (hover: hover) {
.smbk-link--pressable:hover {
color: inherit;
}
}
.smbk-link--pressable:active {
color: var(--smbk-color-text-subtle);
}
/* The underline runs under the words, never under an icon or the gap beside it. A line set on the link reaches
everything inline inside it and nothing inside can take it off: only a box of its own would shed it, and that would
let the line break before the icon. So a link with an icon leaves the underline to its label, and the icon stays
inline. The label takes its line's color and thickness from the link, whichever state the link is in. */
.smbk-link__label {
text-decoration-color: inherit;
text-decoration-thickness: inherit;
}
.smbk-link:has(> .smbk-link__icon):has(> .smbk-link__label) {
text-decoration-line: none;
}
.smbk-link:has(> .smbk-link__icon):is(:not(.smbk-link--standalone), .smbk-link--pressable) > .smbk-link__label {
text-decoration-line: underline;
}
@media (hover: hover) {
.smbk-link:has(> .smbk-link__icon):hover > .smbk-link__label {
text-decoration-line: underline;
}
}
.smbk-link:has(> .smbk-link__icon):active > .smbk-link__label {
text-decoration-line: underline;
}
/* Not a box of its own, which would let the line break before it. Sitting on
the text baseline centers Phosphor's glyph on the capitals. The icon follows
the text: 20 beside the large link, 16 beside the medium, 12 beside the small. */
.smbk-link__icon {
display: inline;
font-size: var(--smbk-size-icon-md);
}
.smbk-link__icon:first-child {
margin-inline-end: var(--smbk-space-inset-2xs);
}
.smbk-link__icon:last-child {
margin-inline-start: var(--smbk-space-inset-2xs);
}
.smbk-link--size-md {
font-family: var(--smbk-typography-body-md-font-family);
font-size: var(--smbk-typography-body-md-font-size);
font-weight: var(--smbk-typography-body-md-font-weight);
line-height: var(--smbk-typography-body-md-line-height);
letter-spacing: var(--smbk-typography-body-md-letter-spacing);
}
.smbk-link--size-md .smbk-link__icon {
font-size: var(--smbk-size-icon-sm);
}
.smbk-link--size-sm {
font-family: var(--smbk-typography-body-sm-font-family);
font-size: var(--smbk-typography-body-sm-font-size);
font-weight: var(--smbk-typography-body-sm-font-weight);
line-height: var(--smbk-typography-body-sm-line-height);
letter-spacing: var(--smbk-typography-body-sm-letter-spacing);
}
.smbk-link--size-sm .smbk-link__icon {
font-size: var(--smbk-size-icon-xs);
}
/* The size of the text around it, as in a heading or a card title: every part of the face comes from
the parent, and the icon takes the text's size. The underline stays. */
.smbk-link--size-inherit {
font: inherit;
letter-spacing: inherit;
}
.smbk-link--size-inherit .smbk-link__icon {
font-size: inherit;
}
/* ---- touch: a link standing on its own takes an invisible press area ----
On a touch screen an invisible area, centred on a standalone link, takes the press out to the comfortable target
in each direction it falls short of it, and halfway to a link standing right under it; its words, its underline,
its focus ring and its place stay as drawn. A
link inside a sentence keeps its line, so a press on the words round it never follows the link. */
@media (pointer: coarse) {
.smbk-link--standalone {
position: relative;
}
.smbk-link--standalone::after {
content: '';
position: absolute;
inset: min(0%, (100% - var(--smbk-size-target-comfortable)) / 2);
}
/* Links standing one under another, a stack's gap apart: halfway into it. */
.smbk-link--standalone + .smbk-link--standalone::after {
inset-block-start: max(min(0%, (100% - var(--smbk-size-target-comfortable)) / 2), var(--smbk-space-stack-md) / -2);
}
.smbk-link--standalone:has(+ .smbk-link--standalone)::after {
inset-block-end: max(min(0%, (100% - var(--smbk-size-target-comfortable)) / 2), var(--smbk-space-stack-md) / -2);
}
}
/* ---- high contrast (forced colors) ---- */
/* In Windows high-contrast mode the browser drops fills and shadows; this puts the meaning back with the
user's own colors. Nothing here applies outside that mode. */
@media (forced-colors: active) {
.smbk-link[aria-disabled="true"] {
color: GrayText;
}
}Tokens it reads
| Token and CSS | Value | Preview | Foundation |
|---|---|---|---|
| color.text.link--smbk-color-text-link | light: #232527dark: #e7ebf0changes with theme | Color | |
| typography.body-lg--smbk-typography-body-lg-font-family--smbk-typography-body-lg-font-size--smbk-typography-body-lg-font-weight--smbk-typography-body-lg-line-height--smbk-typography-body-lg-letter-spacing--smbk-typography-body-lg-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 20pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
| color.text.link-underline--smbk-color-text-link-underline | light: #23252730dark: #e7ebf030changes with theme | Color | |
| border-width.link-underline--smbk-border-width-link-underline | 1.5px | Border width | |
| space.link-underline--smbk-space-link-underline | 4px | Spacing | |
| color.text.link-hover--smbk-color-text-link-hover | light: #111213dark: #f3f5f8changes with theme | Color | |
| color.text.link-underline-hover--smbk-color-text-link-underline-hover | light: #4e555e80dark: #9ba5b180changes with theme | Color | |
| color.text.link-pressed--smbk-color-text-link-pressed | light: #373d43dark: #c4cdd8changes with theme | Color | |
| color.text.link-underline-pressed--smbk-color-text-link-underline-pressed | light: #4e555edark: #9ba5b1changes with theme | Color | |
| focus.ring.width--smbk-focus-ring-width | 2px | Focus | |
| color.border.focus--smbk-color-border-focus | light: #cc0d0ddark: #f34336changes with theme | Color | |
| focus.ring.offset--smbk-focus-ring-offset | 2px | Focus | |
| radius.full--smbk-radius-full | 9999px | Radius | |
| color.text.link-disabled--smbk-color-text-link-disabled | light: #606a75dark: #9ba5b1changes with theme | Color | |
| color.text.subtle--smbk-color-text-subtle | light: #4e555edark: #9ba5b1changes with theme | Color | |
| size.icon.md--smbk-size-icon-md | 20px | Size | |
| space.inset.2xs--smbk-space-inset-2xs | 2px | Spacing | |
| typography.body-md--smbk-typography-body-md-font-family--smbk-typography-body-md-font-size--smbk-typography-body-md-font-weight--smbk-typography-body-md-line-height--smbk-typography-body-md-letter-spacing--smbk-typography-body-md-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 16pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
| size.icon.sm--smbk-size-icon-sm | 16px | Size | |
| typography.body-sm--smbk-typography-body-sm-font-family--smbk-typography-body-sm-font-size--smbk-typography-body-sm-font-weight--smbk-typography-body-sm-line-height--smbk-typography-body-sm-letter-spacing--smbk-typography-body-sm-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
| size.icon.xs--smbk-size-icon-xs | 12px | Size | |
| size.target.comfortable--smbk-size-target-comfortable | 44px | Size | |
| space.stack.md--smbk-space-stack-md | comfortable: 12pxcompact: 8pxchanges with density | Spacing |
Manifest
{
"name": "link",
"block": "smbk-link",
"summary": "Text that goes somewhere, in three sizes or the size of the text around it, with an optional icon; underlined, unless it stands on its own. As a button it stays in the color of the words around it and opens something where it stands.",
"adapt": "css",
"axes": {
"size": {
"values": [
"lg",
"md",
"sm",
"inherit"
],
"default": "lg",
"description": "Figma calls these Large, Medium and Small, each a body preset; inherit takes the size of the text around it, as in a heading or a card title."
}
},
"flags": {
"standalone": {
"description": "A link standing on its own, outside running text: underlined on hover and press only."
},
"pressable": {
"description": "Words that are pressed rather than followed, such as a name that opens its card: a button in the color of the text around it, its line always there."
}
},
"parts": [
"icon",
"label"
],
"figmaNodeId": "4129:21956"
}