Design system

KettleUI.

by Apurva ✶
Hand-inked React components

React components with ink outlines, hard offset shadows and spring animations on a cream background. Built with React, Tailwind CSS v4 and Motion.

$ npx shadcn add @kettle/pill-button
♪♫♪
Kettle UI ✶ by Apurva
Fresh
brew
Components
22
Brand colours
10
Typefaces
3
Cursors
6
Ink outlines✶Hard shadows✶Springy motion✶Cream paper✶Made by Apurva✶
Foundations

Install

Kettle UI is a shadcn registry. The CLI copies each component into your project with its npm packages, the colour tokens and the keyframes it needs, so you own the code and can edit it.

  1. 1

    Set up shadcn

    Skip this if your project already has a components.json.

    npx shadcn@latest init
  2. 2

    Add the registry

    Put this in components.json so you can use the short @kettle names.

    // components.json
    {
      "registries": {
        "@kettle": "https://raw.githubusercontent.com/apurvamukherjee/kettle-ui/main/public/r/{name}.json"
      }
    }
  3. 3

    Add components

    One at a time, or everything at once with kettle-all.

    npx shadcn@latest add @kettle/pill-button @kettle/cursor
  4. 4

    Or skip step 2

    Every item also installs straight from its URL.

    npx shadcn@latest add https://raw.githubusercontent.com/apurvamukherjee/kettle-ui/main/public/r/kettle-all.json

The display and mono fonts are Bricolage Grotesque and Fragment Mono. Load them with next/font and set the--font-bricolage and --font-fragment-monovariables, or the theme falls back to system fonts. PillButton uses next/link, so it needs Next.js. Everything else is plain React.

Foundations

Colours

Most surfaces are paper and ink, and each accent colour has its own job. Tap a swatch to copy its hex, or the token name to copy the CSS variable.

Brand palette

PaperPage background, milky tea
Paper 2Secondary surface, muted
InkText, outlines, hard shadows
TeaSuccess, nature, primary accent
Tea DeepIllustration shade, pressed states
TunesFocus ring, loud CTAs
StringsPlayful highlights
LilacMountains, calm accents
SunEyebrows, selection, badges
SkyIllustration skies, info

Semantic

CardRaised surfaces
Muted textCaptions, secondary copy
DestructiveErrors, invalid fields

Tokens

Every colour is a CSS variable mapped into Tailwind's theme.

Foundations

Typography

Three families: a variable display grotesque, a warm sans for reading and a mono for labels.

Type families

Aa Tea

Bricolage Grotesque · Display

Aa Steeped slowly

Manrope · Body

Aa 0123

Fragment Mono · Labels

Display

clamp(3.75rem, 15vw, 10.5rem)

Steep it slowly

H1

4.5rem / 0.9

Steep it slowly

H2

2.25rem

Steep it slowly

H3

1.5rem

Steep it slowly

Body

1.125rem / 1.6

Steep it slowly

Caption

0.75rem mono

Steep it slowly

Foundations

Shadows & Motion

Shadows are solid ink offsets with no blur. Elements lift on hover and press flat when tapped.

Elevation

Hover or tap the tiles.

--ease-bounce

cubic-bezier(0.34, 1.56, 0.64, 1)

Hover or press the card

--ease-out-expo

cubic-bezier(0.16, 1, 0.3, 1)

Hover or press the card

Components

Pill Button

Chunky pill with a hard shadow. A second colour floods up on hover and the arrow hops out and back in. Renders a Next.js Link when given an href.

Tones

Components

Chip

Tilted mono eyebrow for section labels, statuses and tags.

Tones

ScheduleOpenSold outNewBetaInfoInk
Components

Field

Labelled input with an ink outline. Lifts on focus; turns red with a message when invalid.

States

Type a bad email to see the error state.

We only send product updates.

Components

Event Card

Poster card. On hover a colour wash floods up and the description slides in. On touch screens the description is always shown.

Accents

Live Session

An evening of loud guitars and louder crowds.

Register →

Trail Run

Morning trails, fresh air and a finish-line breakfast.

Register →

Makers Lab

A stage for creators and digital storytellers.

Register →
Components

Polaroid

Instant-photo frame with a caption strip. Drop any image, video or illustration in.

Frame

☕
Studio ✶ Day 1
⛰
Rooftop ✶ Sunrise
Components

Sticker

Springs in tilted, idles with a gentle bob and, on mouse devices, can be dragged and snaps home when let go.

Draggable

Grab one with your mouse.

Brewed fresh ☕
Live
music
Limited ed.
Components

Marquee

Infinite ticker. On desktop its speed and direction react to scroll velocity and it slows on hover; on touch screens it is a pure CSS animation.

Ticker

Scroll the page to push it.

Tea ✶ Tunes ✶ Strings ✶
Kettle UI · by Apurva · Kettle UI · by Apurva ·
Components

Section Heading

Eyebrow chip, a giant word-by-word masked reveal title and an optional subtitle. Pass objects to colour single words.

Split reveal

Schedule

Event Itinerary

Five days of talks, workshops and good coffee.

Components

Reveal & Stagger

Scroll-triggered entrances that run on the compositor, so they stay smooth on iOS.

Entrances

Fades up on scroll

Pointers

Cursors

Six custom cursors plus the native one. The one you pick here follows you around the whole site. Mark any element with data-cursor="Label" and the cursor turns into that label over it.

Pick a cursor

Custom cursors only run with a mouse or trackpad. On touch screens the native behaviour stays as it is.

Now usingLeaf PickMove around in here.
HoverViewdata-cursor="View"Over this box Leaf Pick turns into the label.
ClickTap in hereThe pick flips and presses down.
Illustrations

Hero Scene

The animated hero illustration: a toy train crossing rolling green hills below a snowy peak, with drifting clouds, steam puffs and a cup of tea playing tunes. Every loop pauses while off-screen.

Scene

♪♫♪

Pure SVG, coloured entirely by tokens, so it re-themes with the palette.

  • ✶ 400 × 300 viewBox, scales to any box
  • ✶ Transform-string animations (GPU)
  • ✶ on prop pauses everything
  • ✶ Labelled for screen readers
Illustrations

Toy Train

The little engine on its own: chugging bob, spinning wheels and smoke puffs, all in CSS.

Engine

Illustrations

Train Stepper

Ticket-stub tabs with the toy train springing along the track to the selected stop. Use it for itineraries or multi-step forms.

Itinerary

Pick a step.

Next stop → Plan