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

@vinumcms/react

v0.21.0

Published

Public block renderer, core block components, and the inline-editor primitives.

Readme

@vinumcms/react

The block renderer and the inline editor — deliberately the same code path in two modes. View mode is what a visitor gets; edit mode is the identical component tree with an EditApi supplied, so text becomes editable where it sits.

<PageRenderer blocks={page.blocks} registry={getBlocks()} forms={forms} entries={entries} />

What's here: PageRenderer · the thirteen core blocks · defineBlock and the isomorphic block registry · EditContext, Editable, BlockShell · the inline formatter (**bold** *italic* ~highlight~ [link](/path)) · form-control primitives for settings panels · the --vinum-* theme contract.

Isomorphic; never imports @vinumcms/server.

The theme contract is nineteen --vinum-* CSS variables, set once on :root. VINUM_TOKENS exports the names and VINUM_DEFAULT_THEME a complete starting block — both listed in full under Theming in @vinumcms/admin.

Variants are the one styling choice an author gets. A block declares which axes it accepts; today there is one, tone, with four values:

defineBlock({ type: "pricing", variants: ["tone"], /* … */ });

| tone | renders on | |---|---| | default | --vinum-bg / --vinum-fg | | muted | --vinum-bg-raised / --vinum-fg | | accent | --vinum-accent / --vinum-on-accent | | invert | --vinum-bg-invert / --vinum-fg-invert |

--vinum-accent-invert is the accent to use on the inverted surface. It is the one colour a tone cannot work out for itself: an accent is chosen to contrast with the page, and on the opposite surface it often does not — deriving one would be the CMS picking a brand colour. It defaults to --vinum-accent-soft's value, which is already the softer variant intended for the other end of the theme. Set it if your accent is tuned to your page background:

:root {
  --vinum-accent: #e35468;          /* reads on your dark page */
  --vinum-accent-invert: #a3283c;   /* reads on the light band */
}

A tone rebinds --vinum-fg, --vinum-fg-dim, --vinum-accent and the border tokens inside its own scope, so a block that writes color: var(--vinum-fg) — which is what the theming docs ask for — gets the right colour for the surface it is sitting on rather than the page's. Setting only color would leave such a block unreadable on an inverted band.

It exists to band a long page into sections without a developer forking a block to change one background. Every value resolves to variables you already set, so an author cannot reach a combination your theme has not styled, and background and foreground always move together — never one without the other.

The editor picks it from the block's settings panel. In the DOM it is data-vinum-tone="invert", so your own CSS overrides the defaults without a specificity fight:

[data-vinum-tone="accent"] { background: var(--vinum-accent-soft); }

A block with no variant renders exactly the markup it always did — no wrapper, no attribute. Of the core blocks, only the five section ones (stats, faq, columns, imageGrid, collection) declare tone: a flow block sits inside a shared content column, so a tone there tints the column instead of banding the page. Values outside the vocabulary, and axes a block never declared, are dropped on save.

Inline marks nest. **[the setup guide](/setup)** is a bold link and [a **bold** label](/x) is a link containing bold. The exception is a mark inside the same mark — **bold *italic*** — which needs parsing this deliberately does not have and renders as visible asterisks. A URL is never parsed for marks, so a * in a query string cannot rewrite the destination.

FormRenderer renders a form built in the admin: widths become a six-column grid that collapses to full on narrow screens, sections and dividers render as structure, conditional fields appear and disappear as the visitor answers, and a page break turns the form into steps with Next/Back. Layout, visibility and paging all come from @vinumcms/core, so the admin canvas and the public page agree by construction.

Custom blocks register through the same defineBlock({ type, label, group, create, sanitize, Render }) API the thirteen core blocks use, and are passed to defineVinum({ blocks }). If a core block ever needs something that API cannot express, the API is wrong — there is no private access.