Components

Each one is shown live, with the markup to copy. The caption says where the imperfection is, and why it is free. The styles are in the package's imperfect.css; the markup below uses the same class names.

Hand marks and links

Seven of the fifteen hand-drawn shapes, one per family plus two more underlines. Each is an SVG symbol, drawn in currentColor, and always aria-hidden.

  • Start a project

    Button (perfect)

    Where the imperfection is: Nowhere. The primary button is solid ink, 48px tall, with a 2px radius.

    Why it is free: It is the one thing on the screen that must be easy to find and press. A border and a colour are enough.

    Markup
    <a class="btn btn--primary" href="/contact">Start a project</a>
    <button type="button" class="btn btn--quiet">Quiet button</button>
  • Hand marks

    Where the imperfection is: The shapes themselves: underline, circle, arrow, bracket and strike, three variants each where they exist.

    Why it is free: They are aria-hidden inline SVG, painted with currentColor, and never change the size of their parent.

    Markup
    <svg class="mark mark--underline" viewBox="0 0 200 12" preserveAspectRatio="none" aria-hidden="true">
      <use href="#m-underline-a"></use>
    </svg>

Text marks

The edit, the flick, the highlight and the misregistration. Each one is text-safe: the words are straight and their contrast is unchanged.

  • Build it for the human user.

    Edit

    Where the imperfection is: The pink strike, the caret and the handwritten correction above the line.

    Why it is free: The strike is an absolutely positioned SVG, and the space for the correction is reserved, so nothing reflows. Screen readers hear only the correction.

    Markup
    <span class="edit">
      <span class="edit__old" aria-hidden="true">perfect<svg class="edit__strike" viewBox="0 0 200 24" preserveAspectRatio="none" focusable="false"><path d="…" /></svg></span>
      <svg class="edit__caret" viewBox="0 0 30 20" aria-hidden="true" focusable="false"><path d="…" /></svg>
      <span class="edit__new">human</span>
    </span>
  • Perfect Imperfect

    imperfect

    Wordmark (pink flick)

    Where the imperfection is: The pink flick under the name, and the kern gap in the large wordmark.

    Why it is free: The flick is an SVG sized in em from the text box and painted with multiply. The name itself is plain text.

    Markup
    <span class="flick">Perfect Imperfect<svg class="flick__mark mark" viewBox="0 0 200 12" preserveAspectRatio="none" aria-hidden="true"><use href="#m-underline-a"></use></svg></span>
  • Text on a highlight stays ink, on a yellow plate.

    Highlight

    Where the imperfection is: A yellow plate behind a phrase in a paragraph.

    Why it is free: The plate is a paint-only background. The text uses the on-highlight ink, 14.2:1 in light and dark text on the dark highlight.

    Markup
    <p>Text on a <span class="hl">highlight</span> stays ink, on a yellow plate.</p>
  • Large display type

    Misregistration

    Where the imperfection is: A hard pink offset under very large display type.

    Why it is free: The shadow is capped by --misreg (1.5px), has no blur, and the ink text is unchanged, so the contrast is too.

    Markup
    <h1 class="display-misreg">Large display type</h1>

Prints, plates and notes

Surfaces that carry the imperfection. The tilt is on the sheet or the plate. The text and the controls are not rotated.

  • Print card

    Where the imperfection is: The sheet: two inks, halftone dots, a misregistered second ink, a stepped top-right corner and a small settle when it loads.

    Why it is free: It is a transform on a wrapper, and the caption stays straight. The button is the focusable element, and it has no rotation.

    Markup
    <div class="print" style="--spot:#FF48B0;--tilt:calc(var(--tilt-xs) * -1)">
      <span class="print__sheet"><svg class="print__art" role="img" aria-label="A two-ink sketch of a table of rows.">…</svg></span>
      <span class="print__caption">
        <span class="print__head"><span class="print__title">Ledger</span><span class="print__type mono">Print</span></span>
        <span class="print__line">A two-ink sketch of a table of rows.</span>
      </span>
    </div>
  • Straight text, on a paper plate that is not.

    Plate and index card

    Where the imperfection is: The paper behind the text, slightly off square.

    Why it is free: The plate is a ::before layer. The text sits on top of it, straight, so it never loses contrast or moves.

    Markup
    <div class="plate" style="--tilt:calc(var(--tilt-xs) * 1)">
      <h3>Index card</h3>
      <p>The text is straight. Only the paper behind it turns.</p>
    </div>
  • Start here if the screen feels off.

    Margin note

    Where the imperfection is: A small casual note with a hand-drawn arrow pointing at the item it annotates.

    Why it is free: It is static text in a casual cut of the text font, with one SVG arrow. It moves into the margin only from 1100px, so it never causes a horizontal scroll.

    Markup
    <p class="note">
      <svg class="note__arrow" viewBox="0 0 24 30" aria-hidden="true" focusable="false"><path d="M9 28 C7 19 11 12 17 5 M12 9 L17 4 L19 11" /></svg>
      <span>Start here if the screen feels off.</span>
    </p>
  • Rule

    Where the imperfection is: A hairline that stops 1px short of its neighbour.

    Why it is free: A 1px pseudo-element with height calc(100% - 1px). Nothing reflows.

    Markup
    <div class="hairline-short" aria-hidden="true"></div>
  • A repaired card.

    Kintsugi repair

    Where the imperfection is: A gold hairline across a repaired card.

    Why it is free: It is a decorative SVG stroke. Gold is never used as text, so it has no contrast problem.

    Markup
    <div class="plate">
      <p>A repaired card.</p>
      <svg class="card__repair" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true">
        <path class="kintsugi-line" d="M99 0 L95 12 L100 26 L96 40 L100 56 L95 70 L99 86 L96 100" />
      </svg>
    </div>

Perfect by rule

These never get an imperfection. They are here so the straight parts are documented too.

  • Focus ring

    Where the imperfection is: Nothing. Every focus ring is a straight 3px line with a 3px gap, in the blue token.

    Why it is free: Focus is how a keyboard user knows where they are. An imperfect ring would be a bug.

    Markup
    :focus-visible {
      outline: 3px solid var(--focus);
      outline-offset: 3px;
    }
  • Theme toggle

    Where the imperfection is: Nothing. The label names the theme it switches to, and the width is fixed.

    Why it is free: The width is fixed, so the label never moves the layout. The choice is stored with a try/catch around localStorage.

    Markup
    <button type="button" class="theme-toggle" data-theme-toggle aria-label="Switch to dark theme">…</button>
  • A perfect dialog

    Straight, solid and high contrast. Escape closes it, and focus returns to the button.

    Dialog

    Where the imperfection is: Nothing. A native dialog, straight and solid.

    Why it is free: Escape closes it, focus returns to the button that opened it, and the backdrop is the only other way out.

    Markup
    <button type="button" class="btn btn--quiet" data-open-dialog="dlg-demo">Open the dialog</button>
    <dialog id="dlg-demo" class="dialog" aria-labelledby="dlg-demo-title">
      <div class="dialog__inner">
        <h3 id="dlg-demo-title">A perfect dialog</h3>
        <p>Escape closes it, and focus returns to the button.</p>
        <button type="button" class="btn btn--quiet" data-close>Close</button>
      </div>
    </dialog>

Next

The rules list what each component must never do. The use it page shows how to copy the CSS into a project.