I have a notebook problem.

I have an unreasonable number of notebooks because I like the idea of writing things down.

Naturally, I am reluctant to write in any of them. A clean notebook contains limitless possibilities. Writing on the first page would force it to become an actual notebook, which seems unnecessarily risky.

So I keep buying more.

Eventually, I decided to bring some of what I like about notebooks to the browser instead.

That became Papertooth, a free and open-source library for giving web interfaces an imperfect, drawn-on-paper feeling.

What is Papertooth?

Paper-makers call the fine grain on a sheet its tooth. It is what grips the pencil and what you feel beneath your fingers.

Papertooth is a collection of web components and styles inspired by paper, handwriting and Japanese stationery.

  • dotted, lined, grid and graph paper- highlighter marks- hand-drawn circles, boxes and underlines- rubber stamps and picture stamps- sticky notes and washi tape- calendars and planner pieces- handwritten forms and to-do lists- photo frames and print effects- hand-drawn diagrams The CSS works on its own. An optional JavaScript module adds ink textures, responsive hand-drawn shapes, grid alignment and draw-on animations.

There are also React components for the elements that benefit from a React API.

Installing Papertooth

Install the package from npm:

npm install papertooth

Import the styles and initialise the drawing behaviour:

import 'papertooth/css';

import { init } from 'papertooth';

const kit = init({ pen: 'ink-ai', motion: 'scroll' });

Papertooth can also be used directly from a CDN if you do not have a build step.

The module does nothing when it is imported, so it is safe to import during server rendering. Call init() in the browser. With React or Next.js, use papertooth/react. TypeScript types are included.

Adding some paper to a page

Papertooth mostly uses classes and data attributes, so ordinary HTML can become part of the paper interface.

Add the pt-desk class to the body and a pt-sheet with cream paper and dot ruling. Inside it, use pt-wobble on a heading, pt-mark on highlighted text, pt-link on a link, pt-btn on a button and pt-sticky for a note.

The result still uses real HTML elements. The button remains a button, the link remains a link and the text remains text.

Papertooth adds the visual treatment around them.

Using it with React

Papertooth includes React components for buttons, marks, annotations, planner pieces and form controls.

Import Btn, Mark, Anno, Wobble and Check from papertooth/react.

Elements such as sticky notes, tape and stamps remain ordinary markup, so a React-specific wrapper is not required for everything.

Picture stamps

The library currently includes 18 picture-stamp illustrations:

  • frog- bear- tiger- rabbit- tanuki- owl- cat- paw- daruma- Mount Fuji- cherry blossom- onigiri- cloud- sun- tick- star- heart- arrow A picture stamp can be added with the pt-stamp class, a data-pic value such as frog, a data-ink colour and an accessible label.

Each illustration is also available as an individual SVG file in the package.

Only load what you use

The complete stylesheet is about 20 KB gzipped, but Papertooth can also be imported in smaller parts.

Start with papertooth/css/base, then import only the parts a page uses, such as papertooth/css/highlight, papertooth/css/marks or papertooth/css/goods.

Separate stylesheets are available for forms, planner pieces, images, diagrams and the other component groups.

Interaction and accessibility

The hand-drawn treatment should not replace the behaviour people expect from an interface.

Papertooth accounts for:

  • pointer hover- pressed states- keyboard focus- selected and expanded states- disabled controls- visited links- reduced-motion preferences- forced-colour and high-contrast modes Visitors who prefer reduced motion see the completed marks without the drawing animation.

The library also uses real controls underneath its drawn checkboxes, radio buttons and form elements.

Imperfection without randomness

A hand-drawn interface needs variation, but it should not change unpredictably every time the page renders.

Papertooth uses seeded shapes. The same seed produces the same line, circle or outline, including during server rendering and hydration.

You can give an element a fixed data-seed when you want its shape to remain consistent.

This keeps the result imperfect without making the layout feel unstable.

Try it

Papertooth is free and open source.

You can explore the components and interactive examples here:

https://tbudin.github.io/papertooth

The package is available on npm:

https://www.npmjs.com/package/papertooth

I built it because I wanted some of the character of paper without having to make a mark in one of the notebooks I am apparently preserving for a future museum.

What should I add to Papertooth next?