npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kagadmodyaa/sketchbook

v0.1.0

Published

A page-turning sketchbook component for React, built from DOM + CSS 3D transforms, no WebGL, no dependencies beyond react/react-dom.

Readme

@kagadmodyaa/sketchbook

A page-turning sketchbook component 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.

Live playground: https://kagadmodyaa.vercel.app/components/sketchbook

Install

a) npm

npm i @kagadmodyaa/sketchbook
import '@kagadmodyaa/sketchbook/style.css'

b) shadcn CLI — copies the component source straight into your project, so you can edit it:

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

c) "Copy for AI" — from the component page on the site, for pasting into an AI coding assistant.

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' } }}
/>

Next.js: this is a client component (it renders "use client", both in the built bundle and in the source you get from the shadcn CLI), so it works as-is inside the App Router — no extra directive needed in the file that imports it.

Page model

Each entry in pages is one opening of the book (what you see when it lies open):

  • spread — one image laid across both pages, split at the fold. For the default square page shape this should be a 2:1 image (e.g. 2400×1200 for a landscape portfolio page).
  • left / right — a separate image for each side; either may be left out.

Props

| Prop | Type | Description | |---|---|---| | pages | SketchbookPage[] | The openings of the book. Each is { alt, caption?, project? } plus either spread or left/right (see Page model above). alt is required. | | book | BookOptions | Look and behaviour of the book — see table below. | | projects | SketchbookProject[] | { slug, title, color? }[]. Lets pages link to a project (for tabs and the lightbox's "View project" link). | | className | string | Extra class on the root <section>. | | label | string | Accessible label for the book (aria-label). Default "Sketchbook". | | onEvent | (event: SketchbookEvent) => void | Fires on page turns ({ type: 'turn', opening, total }) and opens ({ type: 'open', opening, side }). | | onPageClick | (opening: number, side: 'left' \| 'right') => boolean \| void | Called when a page is clicked or tapped. Return true to stop the built-in lightbox from opening (e.g. to navigate yourself). | | projectHref | (slug: string) => string | Href for a project, used by the lightbox's "View project" link. |

book options

| Option | Type | Description | |---|---|---| | pageShape | 'portrait' \| 'square' \| 'landscape' | Shape of one page. 'square' (default) opens to a 2:1 spread. | | binding | 'spiral' \| 'wire-o' \| 'stitched' \| 'coptic' \| 'glued' | 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. | | bindingColor | string | Wire colour (spiral, wire-o) or thread colour (stitched, coptic). | | paper | string | Paper colour, any CSS colour. Page images are printed onto it (dark papers switch that off). | | paperTexture | 'cold-press' \| 'hot-press' \| 'smooth' \| 'kraft' \| 'dotted' \| 'grid' \| 'none' | cold-press: toothy watercolour paper · hot-press: fine and smooth · smooth: plain · kraft: fibrous · dotted: dot-grid sketchbook · grid: squared sketchbook · none: flat colour. | | printOnPaper | boolean | When true, whites in your images take the paper colour (looks printed). | | imageFit | 'contain' \| 'cover' | 'contain' (default) shows every image whole, with paper around it if its shape differs from the page. 'cover' fills the page and trims the rest. | | cover | CoverOptions \| false | The book starts closed on this cover. false starts open. See below. | | ribbon | string \| false | Ribbon bookmark colour, or false for none. | | intro | boolean | Open the cover and riffle through to openAt when the page loads. | | openAt | number | Which opening (1 = first page in pages) to settle on after the intro. | | loupe | boolean | Draggable magnifying glass (desktop only). | | captions | boolean | Show page captions under the book. | | tabs | boolean | Coloured index tabs on the page edge, one per project. | | maxHeight | string | Largest height the book may take, as a CSS length. |

cover (CoverOptions): title, subtitle?, color? (board colour), ink? (title lettering colour), material? ('cloth' | 'kraft' | 'leather' | 'card'), image? (artwork filling the front board), band? (elastic band colour, or false), align? ('left' | 'right', where the title sits).

Imperative handle

const book = useRef<SketchbookHandle>(null)
<Sketchbook ref={book} … />

| Method | Description | |---|---| | goTo(opening) | Go to an opening (1-based, like openAt). 0 closes the book. | | goToProject(slug) | Go to the first page of a project. | | next() / prev() | Turn one page. | | current() | Current opening, 0 when closed. | | replayIntro() | Close the book and play the opening animation again. |

How it works

| File | Role | |---|---| | engine/model.ts | Positions: 0 = closed cover, n = n-th opening. L(n)/R(n) faces. A forward turn lifts R(n) and its back is L(n+1). | | engine/leaf.ts | The turning sheet: a chain of nested strips, each rotated a little relative to its parent, so the sheet bows. Bending is weighted towards the spine and fades to flat at both ends of the turn. Each strip is shaded at both edges from its angle. The cover is one rigid strip. | | engine/paint.ts | Paints a face or a vertical slice of one as CSS backgrounds, fitted to its frame (one page, or the whole spread for images crossing the fold): whole by default, or filling the page. | | engine/engine.ts | State, DOM building, turn control (drag, tap, keys, springs, riffle), tilt/zoom, captions, deep links (#page-n). React renders the skeleton once; the engine writes styles and CSS variables per frame. | | engine/loupe.ts | Magnifier: a masked, scaled copy of the resting spread under a draggable glass. | | engine/chrome.ts | Paper surfaces (tooth, dot and line grids), cover materials, and the bindings (spiral, Wire-O, stitched, Coptic, glued), generated as inline SVG. | | engine/motion.ts | Spring and tween. | | Sketchbook.tsx, Lightbox.tsx | React wrapper and the larger-view dialog. | | sketchbook.css | All styles, themed through --skb-* custom properties. |

Interaction: a drag turns the page. A tap (under 7 px of movement) opens the larger view. Arrow keys turn pages; +/-/0 zoom when the book has focus. prefers-reduced-motion swaps turns for a fade.

MIT © Kagadmodyaa