Use it

The package is plain CSS and one small module. There is no build step. Copy the files, or install from GitHub. It is not published to npm on purpose.

Install and copy

  1. Install from GitHub, or copy the files

    With pnpm, pnpm add github:hardik88t/imperfect fetches the repository. The package is in its packages/imperfect folder. To copy instead, take tokens.css, imperfect.css, fonts.css, the fonts/ folder and, if you need it, seed.js.

    pnpm add github:hardik88t/imperfect
    # The package lives in packages/imperfect of that repository:
    #   node_modules/imperfect/packages/imperfect/

    A bare import such as @hardik88t/imperfect/imperfect.css needs the package at the repository root. This repository keeps it in a subfolder, so link the files by path.

  2. Link the three stylesheets, in order

    Fonts first, then tokens, then the components. Put has-grain on the body if you want the paper grain.

    <!doctype html>
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Perfect Imperfect</title>
        <link rel="stylesheet" href="imperfect/fonts.css" />
        <link rel="stylesheet" href="imperfect/tokens.css" />
        <link rel="stylesheet" href="imperfect/imperfect.css" />
      </head>
      <body class="has-grain">
        <main class="wrap">
          <h1 class="display-misreg">Make software feel
            <span class="edit"><span class="edit__old" aria-hidden="true">perfect</span><span class="edit__new">human</span></span>.</h1>
          <p><a class="link-hand" href="#">Read how it works<svg class="mark mark--underline" viewBox="0 0 200 12" preserveAspectRatio="none" aria-hidden="true"><path vector-effect="non-scaling-stroke" d="M3 7 C40 3 90 10 140 6 S190 5 197 8" /></svg></a></p>
          <button type="button" class="btn btn--primary">Start a project</button>
        </main>
      </body>
    </html>
  3. Seed your own strings

    Use a stable key for each imperfection. The helpers return the same numbers for the same key, on every build and every visit. These four keys are computed on this page at build time:

    KeyTilt (deg)Nudge x (px)MarkHeading axes
    card:pricing -0.8 3 b "SOFT" 40, "WONK" 0
    card:team -0.35 -1 b "SOFT" 20, "WONK" 0
    hero -0.8 -3 b "SOFT" 80, "WONK" 1
    edit:hero 0.8 -3 b "SOFT" 60, "WONK" 0
    import { tiltDeg, offsetPx, variant, headingAxesCss } from '@hardik88t/imperfect/seed';
    
    const tilt = tiltDeg('card:pricing');                   // a fixed number of degrees
    const dx = offsetPx('card:pricing', ['xs', 'sm'], 'x'); // a fixed number of px
    const mark = variant('hero', 3);                        // 0, 1 or 2
    const axes = headingAxesCss('hero-title');              // '"SOFT" 40, "WONK" 0'

    Use a slug or an index, never a random number. The types are in seed.d.ts.

Before you ship

A short check

  • Run the page at 360px and 1440px. No horizontal scroll.
  • Record layout-shift entries during load and any view change. The total is 0.
  • Turn on reduced motion. Nothing should move.
  • Tab through every control. Each focus ring is straight and visible.
  • Check each text pair against the tokens page ratios. Pink and gold are never text.
  • Count the imperfections in one viewport. About three is the budget.