II Type

Star on GitHubFeedback
II TypeNative HTMLot-link

Link

One link: a hairline underline at rest, and pointing at it, focusing it or holding its card open draws a highlighter stroke through the whole word, on from the left and off to the right.

Report an issue with Link

Example

Pointed atAda Lindqvist
ReferenceSwiss school
Reference, pointed atSwiss school
AddressWrite to us
Address, pointed atWrite to us

Install

Run it in a project that has the shadcn CLI set up. It brings the 0nlyType base along.

npx shadcn@latest add https://thedirectors.agency/ui/r/link.json

Usage

link-example.tsx
import { Link } from "@/components/ui/link"export default function Example() {return (<div className="grid gap-10"><p className="ot-mp max-w-[36rem]">The work is led by <Link href="#">Ada Lindqvist</Link>, and the reading list sits with{" "}<Link href="https://example.com" external>the Halden archive</Link>.</p><p className="ot-p max-w-[36rem]">The grid follows the <Link href="#" variant="reference">Swiss school</Link>, the pairs follow a type{" "}<Link href="#" variant="reference">specimen</Link> of 1923, and the pauses follow the <Link href="#" variant="reference">score</Link>.</p><ul className="ot-p grid gap-2"><li><Link href="mailto:studio@example.com" variant="address">Write to the studio</Link></li><li><Link href="https://example.com/halden/" variant="address" external>Read the archive</Link></li></ul></div>)}

Contract

  • Underneath: native <a>. Its CSS lives in base.css, so every item may use it. .ot-prose a is the same link.
  • One link. At rest, a hairline under the words in the current colour, clear of the descenders. Pointing, focusing (:focus-visible) or holding its card open (data-state="open", a hover card's trigger) draws the highlighter through the whole word, ascenders and descenders included, on from the left and off to the right (mirrored right to left), and the hairline gives way to it. The stroke runs 0.12em past each end, like a pen overshooting, while the words stay put. The letters turn --ot-on-mark exactly where the stroke has reached (two layers clipped to the text), so they are never dark on dark in Nocturne. The hairline and the overshoot come back once the pen has passed.
  • Focus: the stroke, with the accent line under it at --ot-stroke in place of the outline box (forced colours keep the outline). Pressed (:active): the line snaps back under the stroke in ink, at --ot-stroke. Read (:visited): the line fades to pencil; browsers let :visited change colour only, which is why the line is the box's bottom border and not a painted layer.
  • external (the component prop): opens in a new tab, with "(opens in a new tab)" for screen readers, and a ↗ that hangs just past the end of the words, off the line, on the last line however the link wraps; the link keeps room for it, so it can't be stranded at the start of a line. Pointing sends it a step the way it points.
  • data-variant="quiet": the line in --ot-rule-strong, for a run of links that already reads as links (an index, a programme), so the run isn't a barcode. --ot-link-line sets the line's colour directly.
  • data-variant="reference": after the book's reference marks. No line; a raised mark after the words in the printer's order (* † ‡ § ‖ ¶, then doubled, a CSS counter with @counter-style ot-notes), numbered down the page. The mark says there is more; pointing strokes the words and the mark together. The mark is generated content with empty alternative text, so screen readers read only the words. For sources and definitions in running text.
  • data-variant="address": after the printed page, where a link is spelled out so a reader can go there. Pointing or focusing writes where it goes, small, pencil and in parentheses (no scheme, no www., no trailing slash), after the words from where they end, clear of the ↗ on an external link. A wrapping .ot-link-addressed span holds the link and its .ot-link-address sibling, reserving the address's measure even at rest so revealing it moves nothing. When there is no room beside the words, the address steps under; long addresses wrap. Use it for a link on its own line (a list, a footer). The address is aria-hidden; it isn't drawn with asChild.
  • Docs rows pin states with data-force: hover, focus, press, visited.

Props

Props of Link
PropTypeDefaultWhat it does
asChildbooleanfalseRender the child element, such as your router's link, with the link's look.
externalbooleanfalseOpens in a new tab and adds the ↗ after the words, clear of the underline; pointing sends it a step the way it points.
variant"quiet" | "reference" | "address"quiet sets the underline in rule rather than the text's colour, for a run of links that already reads as links (an index, a programme). reference drops the line for a raised reference mark after the words, numbered down the page in the printer's order (* † ‡ § ‖ ¶). address writes where the link goes, in parentheses after the words, when you point at or focus it; its measure is reserved at rest, and it steps under when the line is full. Use it for a link on its own line, and not with asChild.
href / …nativeA real anchor, so every native prop works.

Craft

Where its move comes from

Where its move comes from
ComponentReferenceMove
LinkWeingart letter's highlighterThe highlighter passes through the word
Reference linkThe book's reference marks (* † ‡ § ‖ ¶)No line; a raised mark says there is more
Address linkThe printed page's spelled-out address; 20(25)Pointing writes where it goes, in parentheses, after the words

How it moves

How it moves
ComponentArticulationWhat moves
LinkPass-throughThe highlighter passes through the word and the line gives way; an external link's ↗ steps the way it points
Address linkWrittenPointing writes the address in from where the words end; leaving, it's taken back the other way