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

@obvi/blueprint

v1.9.2

Published

A classless-first CSS design system for beautiful technical blueprint documents.

Readme

@obvi/blueprint

The CSS design system behind Obvious blueprints.

This package is built for and used by Obvious. It is published so Obvious-generated documents resolve their stylesheet, not as a general-purpose framework — outside of an Obvious blueprint there is little reason to reach for it.

The idea

A blueprint is a technical document that should look like one: monochrome ink on paper, drafting-blue reserved for illustrations, and structure that carries its own meaning.

  • Semantic HTML is the API. Headings, paragraphs, lists, tables, figures, and landmarks render as a finished document with no classes to memorize. You write meaning; the design arrives for free.
  • Classless-first, not class-only. Components exist only for the few patterns native HTML cannot express, and each is a composition of real semantic elements rather than a replacement for them.
  • Interactive blocks read as working surfaces. Live components (choices, pre-flights, source, mockups, galleries) sit on a faint dot-grid mat, so a reader can tell an interactive surface from settled prose at a glance — a texture distinct from the diagonal hatch that marks held or rejected regions.
  • Zero-specificity by contract. Everything ships in cascade layers wrapped in :where(), so the library never fights your own CSS. Your styles always win, with no !important and no parent-scoping.
  • Runtime-complete package. The default stylesheet includes the chrome that blueprint.js injects, the published browser entry bundles Choice components, and runtime icons are inline SVG, so installed or host-inlined documents do not depend on sibling modules or repository-only assets.

The result is one portable stylesheet, no build step, that turns plain semantic markup into a polished blueprint.

Host decision integration

<bp-choice> keeps its author-facing HTML contract while exposing a semantic runtime boundary for Obvious. A connected choice emits bp-choice-ready with its stable manifest. Committing and reconsidering emit the bubbling, composed bp-choice-commit and bp-choice-reconsider events. Tab preview emits nothing.

Hosts apply ephemeral owner state through choice.applyDecisionState(state):

choice.applyDecisionState({ status: 'locked', value: 'phased', busy: false })
choice.applyDecisionState({ status: 'open', value: 'phased', busy: false })
choice.applyDecisionState(null) // restore the authored open/resolved state

The component retains its authored option model and owns every rerender. Hosts must use the manifest, events, and method instead of querying generated .bp-choice__* markup. Applying host state never mutates the authored resolved attribute, so a host can present an owner-only reconsideration while the stored document remains at its last committed decision.

Streaming document root

<bp> is a streaming document root: on connect it paints a deterministic skeleton at once, then fills append-only as content blocks arrive, so a reader sees structure instantly and the document grows in place without a whole-page re-render (focus and scroll survive). It is host-driven, taking content over four channels in priority order:

root.content = { type: 'bp:content', blocks: [{ html: '<h2>…</h2>' }], done: true }
window.__BP_CONTENT__ = { blocks: […] } // read once on connect unless `ignore-global`
frame.postMessage({ type: 'bp:content', blocks: [{ html }], done: true }, '*')
// or a `src` URL the element fetches (AbortController-guarded)

Each block is a raw HTML string appended to the persistent root; nested bp-* elements upgrade themselves on insertion. appendBlock(html) and finalize() expose the same behavior imperatively — done: true or finalize() drops the skeleton and stamps the done state.

Char-level streaming (v1)

For a token-level reveal, the same bp:content channel also accepts three additive ops that stream a single live "tail" block:

root.content = { type: 'bp:content', op: 'open-block', id: 'b1' }
root.content = { type: 'bp:content', op: 'append-text', id: 'b1', text: 'Hello ' }
root.content = { type: 'bp:content', op: 'commit-block', id: 'b1', html: '<h2>Hello world</h2>' }

open-block starts an empty tail; append-text buffers tokens that a velocity-adaptive reveal drains into the tail — as textContent only, never live HTML, so a half-arrived tag is inert and there is no mid-stream XSS surface. The reveal rate is bound to buffer depth (chars/sec proportional to the backlog, clamped to 40–1200), so it catches up a burst without a visible jump and idles the caret honestly through a pause instead of running a fixed-cadence metronome that overruns bursts and stalls into an empty buffer. commit-block swaps the tail for the block's sanitized final HTML in one shot and locks it (append-only from there), ready for the next open-block. A blinking caret marks the write head while a block is open. The ops are backward-compatible: they never carry blocks[], a receiver that only understands blocks[] ignores an unknown op, and commit-block is equivalent to appending one whole block. Under prefers-reduced-motion the reveal collapses to a synchronous drain so output stays deterministic (the fixed synchronous path is the reduced-motion fallback).

Because blocks are inserted as live HTML, the postMessage channel is accepted from the same origin only by default. A trusted cross-origin host opts in with an allow-origin allowlist attribute (space-separated origins, or *). Every appended block is also sanitized before insertion as defense-in-depth — script/iframe/object/embed elements, the document-scoped base/meta[http-equiv=refresh] elements, on* event-handler attributes, and javascript:/data: URLs in href/src are stripped — matching the server-side sanitization that runs when the artifact commits. The one-shot seed channels — the pre-upgrade .content preset, the global, and the initial src fetch — each apply once per element, so reattaching a <bp> never re-seeds the stream; assigning .content after upgrade is a live append (like appendBlock), not a one-shot seed. See streaming.html for a live real-time demo (skeleton → append → finalize()) with playback controls.

Keyed node/patch (v1)

Alongside the append-only path, two keyed ops give in-place reconciliation — a node is addressable by a stable id, so a later patch re-renders the same element instead of appending a duplicate:

root.content = { op: 'node',  id: 'card-7', type: 'bp-card', props: { eyebrow: 'Option A' } }
root.content = { op: 'patch', id: 'card-7', props: { title: 'Adopt the typed stream' }, html: '<p>…</p>' }

.content is a direct property setter that dispatches on op alone — it never inspects a type envelope field, so keyed ops assigned this way skip the { type: 'bp:content', … } wrapper used elsewhere in this doc. That wrapper matters only for the postMessage channel, which does gate on data.type === 'bp:content'; because a node op's own type names the element's tag, the two meanings collide on one JS object key over postMessage (the tag silently overwrites the envelope discriminator, and the message is dropped). Send node/patch ops through .content or window.__BP_CONTENT__ rather than raw postMessage when the op carries a custom type.

node paints a typed element: type is the tag (validated and denied against executable/document-scoped tags, else a div), props are reflected as sanitized attributes (on* handlers and javascript:/data: URLs stripped, just like block content), html is its sanitized inner content, and slot names a keyed parent to nest into (else the stream root). A new id appends; a re-sent id updates in place — never duplicates. patch re-renders the same keyed element in place; an unknown id is a no-op, so a superseded or out-of-order op can never duplicate or corrupt the tree. This reconciler is deliberately minimal (create on a new key, mutate on an existing one — no signals, no whole-tree diffing). A pre-rendered typed skeleton (children authored inside <bp> before upgrade) is adopted into the stream on connect rather than wiped, preserving the skeleton-first paint; its keyed nodes are indexed so later patch ops target them.

Runtime chrome is reconciled from the current data-sidebar headings when finalize() completes, so a streaming <bp> that streams in headings gets the sidebar, reading progress, doc switcher, and <bp-toc> after it finalizes; block/tail reconciliation is append-only (committed blocks are never rewritten). The keyed node/patch reconciler ships as v1, and the per-component reactivity retrofit is done — every bp-* component with patchable props re-renders in place from a captured #source on attributeChangedCallback, so a patch op actually repaints instead of being a no-op.

Producer idiom — frame-first: paint a section or card's frame immediately with a node op carrying null or placeholder props, then patch the same id as real values resolve. Each patch repaints only that element — siblings keep their focus and scroll position. Reserve the char-level ops above for prose that should read as a live token tail; use node/patch for typed structure whose values resolve once rather than stream character by character.

Learn more

Blueprints are created and rendered through Obvious. To see what they are and how to use them, go to obvious.ai.

License

MIT. See LICENSE and THIRD_PARTY_NOTICES.md.