drawably README

The project's README from github.com/danielwh2/drawably. As one markdown file: README.md.

drawably

Hand-drawn UI controls. Every mount generates a fresh pen sketch from seeded randomness, and the stroke boils like an animated doodle. Zero dependencies, ~7 KB of JS gzipped (React wrappers add under 1 KB) and a 3 KB stylesheet. An optional pen font is a separate 31 KB.

Buttons, checkbox, radio and toggle drawn in a boiling pen stroke

Install

npm i drawably

Quick start

import { drawablyButton } from "drawably";
import "drawably/style.css";

drawablyButton(document.querySelector("#done"), { variant: "solid" });

React:

import { DrawablyButton } from "drawably/react";
import "drawably/style.css";

<DrawablyButton variant="solid" onClick={submit}>Done</DrawablyButton>

Each attach call returns a sketch handle:

const sketch = drawablyButton(el);
sketch.resketch();     // redraw with a new random seed
sketch.resketch(42);   // redraw with a specific seed
sketch.destroy();      // remove the SVG and all listeners

Buttons

Three variants: outline (default), solid, scribble. Buttons also carry a state machine for async work:

The four button states: idle, loading, error, success

const button = drawablyButton(el);
button.setState("loading");  // dims the button, boils faster
button.setState("error");    // redraws in red
button.setState("success");  // redraws in green
button.setState("idle");

In React, pass the state prop; the sketch stays put and only the state changes:

<DrawablyButton state={saving ? "loading" : "idle"}>Save</DrawablyButton>

Override the state colours with --drawably-error and --drawably-success. For secondary or destructive actions, set tone: "neutral" (warm grey) or tone: "danger" (red).

Controls

Function Element it expects
drawablyButton(el, opts) a <button>
drawablyCheckbox(el, opts) wrapper containing <input type="checkbox">
drawablyRadio(el, opts) wrapper containing <input type="radio">
drawablyToggle(el, opts) wrapper containing <input type="checkbox">
drawablyInput(el, opts) wrapper containing an <input>
drawablyTextarea(el, opts) wrapper containing a <textarea>
drawablySelect(el, opts) wrapper containing a <select>
drawablyDivider(el, opts) an <hr> or div
drawablyCard(el, opts) any block element
drawablyBadge(el, opts) any inline element
drawablyList(el, opts) a <ul> or <ol>; each <li> gets a sketched marker

Badges take variant: "outline" | "scribble"; lists take marker: "dash" | "check". Selects reserve the widest option's width so picking never shifts layout; in Chromium the options list gets a sketched frame and pen check (appearance: base-select), Safari and Firefox keep the OS popup.

The real inputs stay in the DOM, so keyboard, forms, labels and screen readers all work as usual. The sketch is an aria-hidden SVG layered underneath.

Every control has a React counterpart in drawably/react: DrawablyButton, DrawablyCheckbox, DrawablyRadio, DrawablyToggle, DrawablyInput, DrawablyTextarea, DrawablySelect, DrawablyDivider, DrawablyCard, DrawablyBadge, DrawablyList.

Text decoration

Annotate copy the way you would with a pen. Each attaches to an inline element and leaves its layout alone; use them on a word or a short phrase.

Function Draws
drawablyUnderline(el, opts) a rough line under the text, re-sketched on hover
drawablyHighlight(el, opts) a marker wash behind the text
drawablyCircle(el, opts) a hand-drawn ellipse looping around the text
drawablyArrow(from, to, opts) an arrow from one element to another
import { DrawablyUnderline, DrawablyHighlight, DrawablyCircle, DrawablyArrow } from "drawably/react";

<p>
  <DrawablyUnderline>Hand-drawn</DrawablyUnderline> UI, a{" "}
  <DrawablyHighlight>fresh sketch</DrawablyHighlight> on{" "}
  <DrawablyCircle>every mount</DrawablyCircle>.
</p>
<DrawablyArrow from={noteRef} to={buttonRef} />

A decoration that wraps onto several lines gets one drawing per line. The arrow's SVG is appended to <body> in document coordinates and redraws on resize. Anchors inside a scrolling container will drift as it scrolls.

Options

All controls take the same base options:

Option Default What it does
seed random Omit for a unique sketch per mount, pass a number for a reproducible one
roughness 1 Wobble of the base sketch
boil 0.3 Px of frame-to-frame flicker; 0 renders one static path
stroke, fill, paper pen blue / white Colours, set as --drawably-* custom properties
width 2 Stroke width in px

The colours are plain CSS custom properties, so a theme can set them once:

:root {
  --drawably-stroke: #1a1a1a;
  --drawably-fill: #1a1a1a;
}

Type is Inter when the page has it loaded, falling back to system-ui. The library loads no font unless you opt into the one below.

Motion

Strokes boil gently: three frames of the same sketch, micro-wobbled around a shared base, cycled by pure CSS at 1200ms. Hover or press re-sketches buttons, checkboxes, radios, toggles, underlines and circles; buttons also lift on hover and sink on press. prefers-reduced-motion freezes everything to a single static sketch, including the demo images above.

Font (optional)

Drawably Pen is the same strokes as a typeface: a–z, A–Z, digits and punctuation, built by the library's own pen code (font/) into a 31 KB TrueType. It is not loaded by style.css; nothing in the library needs it. If you want labels in the same hand as the chrome:

import "drawably/font.css";
.drawably-button {
  font-family: "Drawably Pen", Inter, sans-serif;
}

Build your own shapes

The rough renderer is exported. Each function returns an SVG path string, and variants produces the boil frames:

import { roughRoundedRect, roughLine, roughCircle, variants } from "drawably";

const frames = variants(
  (o) => roughRoundedRect(0, 0, 200, 100, 12, o),
  { seed: 7, roughness: 1, boil: 0.3 },
);
// three path strings — render them and cycle opacity

Also exported: roughEllipse, roughArrow, roughCheckmark, scribbleFill, and the seeded PRNG mulberry32 with randomSeed.

License

MIT.

drawably (danielwh2/drawably)

Searching Database…

What is this?Drawably is a UI component library that renders buttons, inputs, checkboxes and other controls with hand-drawn, sketchy outlines. Each mount generates a fresh pen-style sketch from seeded randomness, and the stroke subtly animates like a boiling doodle.
Is this a vanilla JS library or a React framework?It is a vanilla JavaScript and CSS library (about 7 KB of JS gzipped plus a 3 KB stylesheet) with thin React wrapper components for every control, so it works with plain HTML or React.
How do I add it to a project?Install the package, import the stylesheet, then call an attach function such as drawablyButton(el, opts) on a real DOM element, or use the matching React component like DrawablyButton that handles the attaching for you.
Do I need React to use it?No, the core library has zero dependencies and attaches to plain DOM elements; React is only required if you want to use the optional React wrapper components.
Does the select dropdown look hand-drawn in every browser?Only Chromium gets the full sketched look, since it supports the appearance: base-select feature that draws a sketched frame and pen check inside the options list; Safari and Firefox fall back to the normal OS dropdown popup.
Does it stay accessible since the controls are drawn as SVG?Yes, the real form inputs stay in the DOM and keep keyboard, label and screen-reader support; the hand-drawn sketch is just an aria-hidden SVG layered underneath them.
Can I get the exact same sketch every time instead of a random one?Yes, pass a numeric seed option instead of leaving it random so the same sketch is reproduced, and use roughness and boil to tune the wobble and flicker amount.
Is there a matching hand-drawn font for labels?An optional 31 KB TrueType font called Drawably Pen mimics the same pen strokes for letters, digits and punctuation, but it is not loaded automatically and nothing in the library requires it.
Can I build my own hand-drawn shapes with it?Yes, the rough renderer is exported along with helpers like roughEllipse, roughArrow, roughCheckmark, scribbleFill and the seeded PRNG mulberry32, so you can generate custom sketchy SVG paths.
Creator
danielwh2
Size
0.4 MB
Stars
809
Updated
CommitSep 19
Age
2mo
Commits · 90 Days