Skip to content
Ink

The short brief

For agents

If you are an agent building an interface with Ink, you need four things: what to load, the token and class vocabulary, the icons, and the rules you must not break. All of it is also in SKILL.md.

Setup

What to load

ink-design/
  dist/ink.css         Paper + Night, flattened, self-contained
  dist/CHECKSUMS        sha256, for a pinned vendored copy
  fonts/                8 woff2 + 2 OFL license texts — copy all of it
  src/icons/*.svg       the drawn icons — inline them, never <img>
  SKILL.md              this brief

Link ink.css, put ink-paper or ink-night on <html>, and copy fonts/ next to it. With no stored choice, use Night when prefers-color-scheme is dark, and set the class before first paint, so the page is drawn once, not twice.

<!DOCTYPE html>
<html lang="en" class="ink-paper">   <!-- or ink-night; follow the OS until chosen -->
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="ink.css">
</head>
<body>
  <main class="ink-page">
    <div class="ink-runninghead"><span>Walden</span><span>Solitude</span></div>
    <div class="ink-prose"><p>…</p></div>
    <div class="ink-folio">Page 113 of 268</div>
  </main>
  <nav class="ink-pager" aria-label="Pages">
    <button class="ink-btn ink-btn--icon">…<span class="ink-visually-hidden">Previous page</span></button>
    <span class="ink-pager__label" aria-live="polite">Page 113 of 268</span>
    <button class="ink-btn ink-btn--icon">…<span class="ink-visually-hidden">Next page</span></button>
  </nav>
</body>
</html>

Vocabulary

Tokens: semantics only

surfaces   --surface-canvas  --surface-raised  --surface-sunk  --surface-inverse
text       --text-heading  --text-body  --text-secondary  --text-inverse
non-text   --mark-muted (idle marks, never type)  --rule (decorative)  --rule-strong
controls   --border-control  --focus-ring  --selected-fill / --on-selected
levels     --gray-0..15 (for tokens only; components never name one)
space      --space-1..9        radius  --radius-s|m|pill
stroke     --stroke (1px)  --stroke-control (2px)  --stroke-focus (3px)
type       --font-book (Lora)  --font-ui (Atkinson Hyperlegible Next)
           --weight-body|ui|heading|strong  --size-label|caption|ui|body|lead|h3|h2|h1|title
           --leading-tight|ui|body  --measure
target     --hit (48px)
motion     none, by design

Three that will catch you out

--mark-muted is for leader dots and idle marks. Never set type in it; use --text-secondary, which clears 7:1.

--border-control, not --rule, for the edge of anything interactive.

There are no motion, shadow or color tokens. Their absence is the design.

Vocabulary

The class list, complete

modes      html.ink-paper  html.ink-night  [+ .ink-full for pure paper and pure ink]
type       .ink-title  .ink-h1  .ink-h2  .ink-h3  .ink-lead  .ink-body  .ink-secondary  .ink-italic
           .ink-ui  .ink-caption  .ink-caps  .ink-link
prose      .ink-prose  .ink-dropcap  .ink-quote  .ink-dinkus (hr)  .ink-rule [--strong] (hr)
reading    .ink-page  .ink-runninghead  .ink-folio  .ink-pager  .ink-pager__label
           .ink-progress (style="--at: 40%")  .ink-toc  .ink-toc__title  .ink-toc__page  .ink-end
actions    .ink-btn [--primary --quiet --icon --wide]  .ink-chip (aria-pressed)
surfaces   .ink-card [--sunk]  .ink-list  .ink-list__hint
forms      .ink-field  .ink-label  .ink-hint  .ink-input (aria-invalid)
           .ink-check > input + .ink-check__box
           .ink-switch > input + .ink-switch__track + .ink-switch__state
feedback   .ink-notice [--note]  .ink-notice__glyph  .ink-notice__actions
icons      .ink-icon [--l --xl]
utility    .ink-visually-hidden

Variants are --modifier; state is an ARIA attribute or a native checked. Anything not on this list does not exist. In particular there is no .ink-toast, .ink-modal, .ink-carousel, .ink-skeleton or .ink-badge, and there will not be.

Art

Icons: inline the file

book
bookmark
check
close
contents
light
menu
moon
next
pen
prev
search
type
<!-- paste the contents of src/icons/bookmark.svg -->
<button class="ink-btn ink-btn--icon"><svg class="ink-icon" viewBox="0 0 24 24" …>…</svg><span class="ink-visually-hidden">Bookmark this page</span></button>

Constraints

Not negotiable

  1. Use only the sixteen gray levels, through semantic tokens. No hue, no rgba(), no color-mix(), no partial opacity. Components read tokens like --text-secondary, never --gray-4, so markup works in both modes.
  2. Nothing moves. No transition, animation, @keyframes or smooth scroll. Change state in one step. The pressed inversion is built in.
  3. Page long content; don’t scroll it. Use .ink-page and .ink-pager with the count in view, keep the controls in the same place on every page, and finish with .ink-end. No infinite feeds, no carousels.
  4. Nothing soft. No gradient, box-shadow, text-shadow, blur or filter. Separate things with a rule, a border or space.
  5. Nothing on hover. Every control is visible and labeled at rest, at least 48px (--hit).
  6. Nothing updates itself. No ticking clocks, live counters, shimmering skeletons or polling. No modal on load, no toast, no role="alert", no aria-live="assertive", no setInterval, no autoplay, no autofocus. A notice sits where the person caused it.
  7. Meaning never rests on gray alone. Say it in words; then use shape, fill and border style. Dashed means look again. A switch shows On or Off as text.
  8. No red, no punishment. A problem is aria-invalid="true" (dashed edge) plus a hint that says what to do. Offer undo after anything that changes something.
  9. Lora for reading, Atkinson Hyperlegible Next for operating. No weight below 400 anywhere. Long text goes in .ink-prose, on a plain surface.
  10. No emoji. Inline an icon from src/icons/ (book, bookmark, check, close, contents, light, menu, moon, next, pen, prev, search, type), aria-hidden="true", with the meaning in the text or a visually hidden label. If none fits, draw one the same way: 24-unit grid, 2-unit currentColor stroke on whole coordinates, round caps and joins, no fills.
  11. Never load anything over a network, and never disable pinch zoom.

Most of these are checked: run npm run check. If a requirement you have been given conflicts with one of them, say so rather than resolving it quietly. The conflict is usually the most useful thing you have found.

Copy

Write it like this

Page 3 of 12                         not  Keep going! Only 9 left!
That’s the end of the chapter.       not  Next chapter starts in 5… 4… 3…
Bookmarked. Page 113.                not  Saved! (a toast that slides in)
Your notes didn’t sync. Nothing      not  ERROR: Sync failed
is lost.
This book ends on page 268.          not  Invalid input

Plain, quiet and exact, like the front matter of a good book. Sentence case. Say where the person is and what is there; never hurry them, never count down, never cheer.