Sketchbook

A page-turning sketchbook for React — a book you drag to turn, built from DOM and CSS 3D transforms. No WebGL, no dependencies beyond react/react-dom. It renders a full book: cover, binding, paper texture, page-turn physics, an optional magnifying loupe, and a larger lightbox view, all themeable through CSS custom properties.

Installation

pnpm dlx shadcn@latest add https://kagadmodyaa.vercel.app/r/sketchbook.json

Needs a React + Tailwind project set up with shadcn. Copies the component source straight into your project, so you can edit it.

Usage

import { Sketchbook } from '@kagadmodyaa/sketchbook'
import '@kagadmodyaa/sketchbook/style.css'

<Sketchbook
  pages={[
    { spread: '/pages/01-title.webp', alt: 'Title page' },
    { left: '/pages/02a.webp', right: '/pages/02b.webp', alt: 'Floor plan and section' },
  ]}
  book={{ binding: 'spiral', cover: { title: 'Portfolio' } }}
/>

Pages

Each entry in pages is one opening of the book — what you see when it lies open, two pages side by side.

spread lays one image across both pages, split at the fold. Use this for an exported landscape portfolio page. For the default square page shape, export spreads at 2:1 — e.g. 2400×1200.

left / right give a separate image to each side; either may be left out.

Images are never cropped by default — imageFit: 'contain' shows every image whole, with paper around it if its shape differs from the page.

Options

Every book option.

NameTypeDefaultDescription
pageShape'portrait' | 'square' | 'landscape''square'Shape of one page. Square opens to a 2:1 spread.
binding'spiral' | 'wire-o' | 'stitched' | 'coptic' | 'glued''stitched'spiral: one continuous coil · wire-o: twin wire loops · stitched: sewn signatures (thread in the fold) · coptic: exposed chain stitch across the spine · glued: perfect-bound, a deep fold, nothing visible.
bindingColorstring—Wire colour (spiral, wire-o) or thread colour (stitched, coptic).
paperstring (any CSS colour)'#f2ede3'Paper colour. Page images are printed onto it (dark papers switch that off).
paperTexture'cold-press' | 'hot-press' | 'smooth' | 'kraft' | 'dotted' | 'grid' | 'none''cold-press'cold-press: toothy watercolour paper · hot-press: fine and smooth · smooth: plain · kraft: fibrous · dotted: dot-grid sketchbook · grid: squared sketchbook · none: flat colour.
printOnPaperbooleantrueWhen true, whites in your images take the paper colour (looks printed).
imageFit'contain' | 'cover''contain''contain' shows every image whole, with paper around it if its shape differs from the page. 'cover' fills the page and trims the rest.
coverCoverOptions | falsefalseThe book starts closed on this cover. false starts open.
ribbonstring | falsefalseRibbon bookmark colour, or false for none.
introbooleantrueOpen the cover and riffle through to openAt when the page loads.
openAtnumber1Which opening (1 = first page in pages) to settle on after the intro.
loupebooleantrueDraggable magnifying glass (desktop only).
captionsbooleantrueShow page captions under the book.
tabsbooleantrueColoured index tabs on the page edge, one per project.
maxHeightstring (CSS length)'72vh'Largest height the book may take.

Props

NameTypeDefaultDescription
pagesSketchbookPage[]—The openings of the book. Each is { alt, caption?, project? } plus either spread or left/right.
bookBookOptions{}Look and behaviour of the book — see Options below.
projectsSketchbookProject[][]{ slug, title, color? }[]. Lets pages link to a project (for tabs and the lightbox).
classNamestring—Extra class on the root <section>.
labelstring'Sketchbook'Accessible label for the book (aria-label).
onEvent(event: SketchbookEvent) => void—Fires on page turns ({ type: 'turn', opening, total }) and opens ({ type: 'open', opening, side }).
onPageClick(opening, side) => boolean | void—Called when a page is clicked or tapped. Return true to stop the built-in lightbox from opening.
projectHref(slug: string) => string—Href for a project, used by the lightbox's "View project" link.

Keyboard & accessibility

  • Arrow keys turn pages when the book has focus.
  • + / - / 0 zoom the book.
  • Enter opens the larger lightbox view of the focused page.
  • Every image needs meaningful alt text, used for screen readers and search.
  • prefers-reduced-motion swaps page turns for a fade.

Built from DOM and CSS 3D transforms, no WebGL. MIT licensed — free to use, change and share.