# Link

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.

Block: `smbk-link`. Status: candidate. Figma: the Figma file, https://www.figma.com/community/file/1688324308358870630. Desktop and mobile: differ by presentation only.

## Options

| Axis | Values | Default | Meaning |
|---|---|---|---|
| `size` | `lg`, `md`, `sm`, `inherit` | `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. |
| `standalone` | on / off | off | A link standing on its own, outside running text: underlined on hover and press only. |
| `pressable` | on / 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. |

Parts: `smbk-link__icon`, `smbk-link__label`

## Markup

```html
<a
  class="smbk-link smbk-link--size-lg"
  href="#"
><span class="smbk-link__label">View all projects</span><i class="ph ph-arrow-up-right smbk-icon smbk-icon--size-md smbk-link__icon" aria-hidden="true"></i></a>
```

## React

```tsx
import { Link } from './design-system/components/link/link.js';

<Link size="lg" trailingIcon="ArrowUpRight" href="#">View all projects</Link>
```

## Stylesheet

```css
/* 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

```json
{
  "color": {
    "text": {
      "link": {
        "$type": "color",
        "$value": "{{color.grey.900}}",
        "$description": "Link text."
      },
      "link-underline": {
        "$type": "color",
        "$value": "{{color.alpha.grey-900.19}}",
        "$description": "The line under a link at rest, see-through so it sits the same on every surface."
      },
      "link-hover": {
        "$type": "color",
        "$value": "{{color.grey.950}}",
        "$description": "Link text on hover."
      },
      "link-underline-hover": {
        "$type": "color",
        "$value": "{{color.alpha.grey-700.50}}",
        "$description": "The line under a link, on hover."
      },
      "link-pressed": {
        "$type": "color",
        "$value": "{{color.grey.800}}",
        "$description": "Link text while pressed."
      },
      "link-underline-pressed": {
        "$type": "color",
        "$value": "{{color.grey.700}}",
        "$description": "The line under a link, while pressed."
      },
      "link-disabled": {
        "$type": "color",
        "$value": "{{color.grey.600}}",
        "$description": "An unavailable link."
      },
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.700}}",
        "$description": "Secondary text: descriptions, metadata."
      }
    },
    "border": {
      "focus": {
        "$type": "color",
        "$value": "{{color.red.600}}",
        "$description": "Keyboard focus ring on controls."
      }
    }
  },
  "typography": {
    "body-lg": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "20px",
        "fontWeight": 400,
        "lineHeight": 1.5,
        "letterSpacing": "0px"
      }
    },
    "body-md": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "16px",
        "fontWeight": 400,
        "lineHeight": 1.5,
        "letterSpacing": "0px"
      }
    },
    "body-sm": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "14px",
        "fontWeight": 400,
        "lineHeight": 1.5,
        "letterSpacing": "0px"
      }
    }
  },
  "border-width": {
    "link-underline": {
      "$type": "dimension",
      "$value": "1.5px",
      "$description": "Thickness of the line under a link. Figma sets a text's underline as a plain number with no variable behind it, and this one is not a step on the width scale."
    }
  },
  "space": {
    "link-underline": {
      "$type": "dimension",
      "$value": "{{space.4}}",
      "$description": "Gap between a link's words and the line under them. Figma sets a text's underline as a plain number with no variable behind it."
    },
    "inset": {
      "2xs": {
        "$type": "dimension",
        "$value": "{{space.2}}",
        "$description": "Padding inside a component, smallest."
      }
    },
    "stack": {
      "md": {
        "$type": "dimension",
        "$value": "{{space.12}}",
        "$description": "Gap between things stacked one above another, medium."
      }
    }
  },
  "focus": {
    "ring": {
      "width": {
        "$type": "dimension",
        "$value": "{{border-width.2}}",
        "$description": "Thickness of the focus ring."
      },
      "offset": {
        "$type": "dimension",
        "$value": "{{space.2}}",
        "$description": "Gap between a control and its focus ring."
      }
    }
  },
  "radius": {
    "full": {
      "$type": "dimension",
      "$value": "{{radius.9999}}",
      "$description": "Capsule: pills, chips, switches."
    }
  },
  "size": {
    "icon": {
      "md": {
        "$type": "dimension",
        "$value": "{{size.20}}",
        "$description": "Icon box, medium."
      },
      "sm": {
        "$type": "dimension",
        "$value": "{{size.16}}",
        "$description": "Icon box, small."
      },
      "xs": {
        "$type": "dimension",
        "$value": "{{size.12}}",
        "$description": "Icon box, extra small."
      }
    },
    "target": {
      "comfortable": {
        "$type": "dimension",
        "$value": "{{size.44}}",
        "$description": "Comfortable touch target (44)."
      }
    }
  }
}
```

## 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.

## 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.

## 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](popover.md)'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](button.md), 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 it `href` and leave `as` out.
- As a button visually. The button wrapper can render a link with button classes when that is truly needed.

## 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](popover.md)'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 `standalone` on 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. |
| Enter or Space | Presses words with `as="button"`, and a button carrying the link's classes. |

## Usage rules

### Do

- Keep the underline on links inside paragraphs; set `standalone` only 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.
