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

periwinkle

v0.4.0

Published

Static API documentation generator for OpenAPI 3.x — themable, self-contained, no runtime framework

Readme

CI GitHub Pages npm License Last commit

periwinkle banner

periwinkle

Static API documentation generator for OpenAPI 3.x — turn a spec plus a small config into a polished, themable, self-contained docs site. Named after the violet-blooming periwinkle flower (Vinca).

Live demo: phranck.github.io/periwinkle — built from a fictional bookstore contract on every push.

  • Static output: index.html, one stylesheet, one small vanilla-JS bundle, and a copy of the spec as openapi.json. No runtime framework, deployable to any host.
  • Sticky top navigation with a frosted-glass backdrop: optional brand logo, home link, search, GitHub link, and theme toggle — every affordance toggleable via config.
  • Sidebar navigation with endpoint groups, integration guide, endpoint blocks with generated curl examples, schema cards with field tables and raw JSON view.
  • Light/dark theming via CSS custom properties, fully configurable (colors, fonts, logo, radius). Pick which palette first-time visitors see, or follow their operating system.
  • Document search dialog (⌘K) and persisted collapsible sections — all progressive enhancement over working plain HTML.
  • Embeddable React components for host apps (e.g. Astro via @astrojs/react).

Contents

Quickstart

npm install --save-dev periwinkle
npx periwinkle build --spec openapi.json --out dist
npx periwinkle preview --dir dist

The spec may be JSON or YAML. Broken specs fail the build loudly — periwinkle never produces a silently wrong site.

Configuration

Create a periwinkle.config.ts (or .mts/.js/.mjs) next to your project; it is discovered automatically, or passed explicitly with --config. Every field is optional — an empty config produces a fully working site.

import { defineConfig } from "periwinkle";

export default defineConfig({
  spec: "openapi.json",
  site: { basePath: "/docs", title: "Example API" },
  theme: {
    colors: {
      light: { accent: "#6667ab" },
      dark: { accent: "#9a9bd4" },
    },
  },
  navigation: { logo: "assets/logo.svg", github: { url: "https://github.com/acme/api" } },
});

The config covers site identity, the full color palette per mode, fonts, corner radius, the top navigation bar, sidebar affordances, feature switches, typography/layout sizing, animation timing, the integration guide content, custom Markdown chapters, and the footer.

  • Full reference: every option, its type, default, and where it appears on the page is documented in CONFIGURATION.md.

  • Interactive builder: click your config together at phranck.github.io/periwinkle/config-builder/ — live preview, import an existing config, copy to clipboard, save file.

    The builder is a second page periwinkle can generate, and it is off by default: a published API reference should not ship the tool that authors its own config. Turn it on where it belongs, as the demo does:

    features: { configBuilder: true },

Sidebar icons

Endpoint groups take their sidebar icon from the tag name, so a generated reference does not repeat one mark down the whole rail. Shops gets a storefront, Search a magnifier, Users two avatars. Titles that are not covered keep a neutral default, and singular and plural resolve to the same entry unless the icon set offers a real plural counterpart.

The mapping lives in src/render/section-icons.json and covers common API vocabulary out of the box.

To adapt it, use the icon picker: browse or search all 993 Iconsax icons in both the Bulk and TwoTone style, add your own titles, then download the JSON and save it over the mapping file. The page keeps everything in the browser and uploads nothing.

It also ships in this repository as tools/icon-picker.html and runs offline, with no server and no network:

open tools/icon-picker.html          # or double-click it

After upgrading iconsax-react, refresh the tool's icon data:

node tools/extract-icons.mjs
node tools/build-icon-picker.mjs

Deploying

The output directory is plain static files. Recipes:

Any static host (nginx, GitHub Pages, …) — upload dist/. With a sub-path (e.g. Pages project sites), set site.basePath accordingly.

GitHub Actions → Pages — see .github/workflows/pages.yml in this repo; it builds the live demo.

Hono / Node backend under /docs:

import { serveStatic } from "@hono/node-server/serve-static";

app.use("/docs/*", serveStatic({ root: "./docs-dist", rewriteRequestPath: (p) => p.replace(/^\/docs/, "") }));

Build with site.basePath: "/docs" and serve the directory — no server-side rendering involved.

Embedding in an existing app

The same components that power the CLI are exported for host apps:

import { ApiDocs, prepareDocsData, resolveConfig } from "periwinkle";
import "periwinkle/styles.css";

const data = await prepareDocsData(openApiDocument, resolveConfig({ site: { basePath: "/docs" } }));
// e.g. in Astro with @astrojs/react:
<ApiDocs data={data} />

Add periwinkle/client.js as a deferred script for search, collapsing, and the theme toggle, and emit compileThemeCss(config) (it takes the full resolved config, since sizing and motion tokens compile alongside the palette) into a <style> tag placed after the stylesheet link. All interactivity binds via data-pw-* attributes; the markup works without JavaScript.

CLI

periwinkle build   [--spec <file>] [--config <file>] [--out <dir>]
periwinkle preview [--dir <dir>] [--port <number>]
periwinkle --version | --help

Projects using periwinkle

| Project | API reference | | --- | --- | | lmaa.space, a curated directory of independent online shops in Europe | api.lmaa.space/docs |

Built your docs with periwinkle? Add yourself to this list. Open a pull request with one more row, or open an issue with your link and it gets added for you. Seeing where periwinkle actually ends up is the most useful feedback this project can get, and it helps the next person decide whether it fits their API.

License

This repository has been published under the MIT license.