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

tendril-wireframes

v0.1.11

Published

Hand-drawn (Balsamiq-style) React component library mirroring the Ivy Framework widget set.

Readme

Tendril — hand-drawn wireframe components

A React component library drawn as if someone sketched it on paper. Think Balsamiq: wobbly borders, hatched fills, a handwriting typeface and a deliberately unfinished feel, so nobody mistakes a wireframe for a finished design.

Built on React 19, Tailwind CSS v4, Radix primitives (the same base shadcn/ui uses) and rough.js, with Storybook for the catalogue.

npm install
npm run storybook   # browse every component at http://localhost:6006
npm run build       # emit dist/index.js, dist/index.cjs, dist/tendril.css and types

Using it in another project

Everything except React is a normal dependency and stays external in the build, so you get one copy of Radix, lucide and rough.js — not a second set bundled inside the library.

import "tendril-wireframes/styles.css";
import { SketchProvider, Button, Card, TextInput, Field } from "tendril-wireframes";

export function App() {
  return (
    <SketchProvider>
      <div className="tendril">
        <Card title="New project" width="20rem">
          <Field label="Name" required>
            <TextInput value={name} onChange={setName} width="100%" />
          </Field>
          <Button title="Create" icon="Rocket" />
        </Card>
      </div>
    </SketchProvider>
  );
}

SketchProvider mounts the shared SVG filters and sets the pencil (roughness, bowing, stroke width) for everything beneath it. It is optional — without it the borders still draw, they just lose the turbulence wobble on icons and images. The tendril class applies the handwriting font and ink colour; put it on whatever wraps your app.

Styles: pick one of two paths

You don't use Tailwind — import the prebuilt stylesheet and you're done. It carries its own reset and every class the components need:

import "tendril-wireframes/styles.css";

You already use Tailwind v4 — import the tokens instead of the full sheet, and point @source at the package so your Tailwind generates the utilities the components use. This avoids shipping Tailwind's preflight twice, and lets you write text-ink or font-sketch in your own markup:

@import "tailwindcss";
@import "tendril-wireframes/theme.css";
@source "../node_modules/tendril-wireframes/dist";

Two things worth knowing

Linked installs need dedupe. With npm link or a file: dependency, your bundler can resolve two copies of React — one yours, one from the library's own node_modules — and every hook throws Cannot read properties of null (reading 'useContext'). This is not specific to Tendril, but you will hit it while developing against a local checkout:

// vite.config.ts
export default defineConfig({ resolve: { dedupe: ["react", "react-dom"] } });

A normal registry or tarball install has no such problem.

Icons cost about 60 kB gzipped. icon="Rocket" resolves lucide icons by name at runtime, which means the whole icon map has to be in the bundle and cannot be tree-shaken. It is the single largest part of the package. If that matters to you, import lucide components directly and pass them as children instead of using the icon prop.

The prop surface

Props are named enums rather than booleans and utility classes: variant="Destructive", density="Small", borderRadius="Full", formatStyle="Currency", colorScheme="Rainbow". Events are ordinary React callbacks — onClick, onChange, onSelect, onOpenChange — and composite widgets take named ReactNode props for their regions (header, children, footer) rather than a slot object.

Every component also takes a shared base: width, height, aspectRatio, density, visible, id, plus className, style and data-testid. visible={false} hides a widget without unmounting it. Sizes accept CSS lengths ("20rem"), fractions ("1/2") and bare numbers, which mean quarter-rem steps. density defaults to Medium everywhere.

Tendril covers the layouts that are worth drawing by hand — TabsLayout, SidebarLayout, ResizablePanelGroup and FloatingPanel. Purely structural layout has no equivalent here, since flexbox and CSS grid already say the same thing with less.

Component index

Primitives — TextBlock, Markdown, Json, Xml, CodeBlock, Terminal, Embed, Separator, Skeleton, Icon, Box, Callout, Kbd, Avatar, Loading, Stepper, AudioPlayer, VideoPlayer

CodeBlock and CodeInput colour their own syntax — keywords, strings, numbers and comments, in the same desaturated range as the rest of the palette, from a small built-in tokenizer rather than a highlighting dependency.

Widgets — Button, Badge, Card, Progress, StackedProgress, Tooltip, Toolbar, Sheet, Breadcrumbs, Expandable, DropDownMenu, Pagination, Tree, List + ListItem, Details + Detail, Table + TableRow + TableCell, DataTable, Dialog + DialogHeader + DialogBody + DialogFooter, Chat + ChatMessage + ChatLoading + ChatStatus, Kanban + KanbanCard

Layouts — TabsLayout + Tab, SidebarLayout + SidebarMenu, ResizablePanelGroup

  • ResizablePanel, FloatingPanel

Wireframe marks — WireframeNote, WireframeCallout, WireframePlaceholder, WireframeArrow, WireframeCurlyBrace, WireframeRedX, WireframeScratchOut, WireframeMockup, WireframeTransform

WireframeMockup wraps real content in a phone, tablet, desktop-window or browser frame; WireframeTransform rotates, scales, skews or flips a subtree without disturbing its neighbours. WireframeShape is the one wireframe widget with no Tendril equivalent yet.

Inputs — Field, Form, TextInput, ReadOnlyInput, BoolInput, NumberInput, NumberRangeInput, SelectInput, AsyncSelectInput, DateTimeInput, DateRangeInput, ColorInput, IconInput, FeedbackInput, FileInput, CodeInput, SignatureInput, AudioInput, CameraInput

Charts — LineChart, AreaChart, BarChart, ScatterChart, PieChart, RadarChart, FunnelChart, GaugeChart, SankeyChart, ChordChart

Charts are drawn directly with rough.js rather than wrapping a charting library, so the geometry matches the rest of the sketch. Each chart takes its series as an array (lines, bars, areas, scatters, pies, radars, funnels) alongside xAxis, yAxis and colorScheme.

Listing components from outside JavaScript

tendril.manifest.yaml describes every component and every prop — types, enum members, defaults, which props are events, which are inherited — so a code generator, a design tool or an LLM tool schema can enumerate the library without keeping its own copy of the API.

npm run manifest         # regenerate (part of `npm run build`)
npm run manifest:check   # fail if the committed file is stale

Props are read from the TypeScript types by the compiler's own checker, so they cannot drift from the code. Prose — category, tags, examples — comes from JSDoc tags in the component files:

/**
 * The workhorse control.
 *
 * @tags action submit cta
 * @example <Button title="Save" icon="Save" onClick={save} />
 */
export const Button = ...

Published as tendril-wireframes/manifest.yaml, with the same data at tendril-wireframes/manifest.json. See MANIFEST.md for the tag vocabulary and the file's shape.

The sketch layer

Everything is built on three pieces in src/sketch:

  • SketchFrame — measures its own box with a ResizeObserver and renders the border (and optional fill) as rough.js paths in an SVG behind the content. Corners can be sharp, rounded, pill or ellipse; fills use rough.js's hachure, cross-hatch, zigzag, dots, dashed or solid.
  • RoughShape — the same idea inside an existing <svg>, used by the charts.
  • SketchProvider — the pencil settings, plus the feTurbulence filters that give icons and images their wobble.

Every frame takes a seed, so a component redraws with the same wobble on each render instead of shimmering. Pass deterministic={false} to the provider if you want it to re-scribble.

Surfaces and stroke weights come from two tables in src/sketch/colors.ts rather than literal values, so a border's weight always means the same thing across the set: SURFACE (raised, sunken, quiet, code, pressed, muted, highlight) and STROKE (hairline, thin, regular, emphasis, heavy). Containers are drawn with a faint regular edge, controls with a full-ink one, and inputs move from faint to ink on focus.

Theming

The palette and typeface are Tailwind v4 theme tokens in src/styles/tendril.css. Override them anywhere in your own CSS:

:root {
  --font-sketch: "Comic Neue", cursive;
  --color-ink: #1f2933;
  --color-paper: #ffffff;
}

The stylesheet imports Balsamiq Sans from Google Fonts. If you self-host fonts or work offline, drop the @import and point --font-sketch at your own face.

Repository layout

src/
  lib/          shared types (Densities, Align, MenuItem, Option, …) and helpers
  sketch/       the rough.js drawing layer and colour palette
  widgets/      the components, grouped by kind
    primitives/
    inputs/
    charts/
    layouts/
    wireframe/
  stories/      Storybook catalogue — one section per component, mirroring
                the widget folders (widgets/, inputs/, charts/, layouts/,
                wireframe/, primitives/), plus shared fixtures and helpers
  styles/       Tailwind theme and keyframes

Scripts

| Script | What it does | | --- | --- | | npm run storybook | Storybook on port 6006 | | npm run build | library bundle, stylesheet and .d.ts files into dist/ | | npm run build-storybook | static Storybook into storybook-static/ | | npm run typecheck | tsc --noEmit over src | | npm run manifest | regenerate tendril.manifest.yaml |

The build emits dist/index.js (ESM), dist/index.cjs, dist/tendril.css, dist/theme.css and per-file .d.ts declarations.