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

@siphon_npm/ui

v0.7.0

Published

Shared terminal presentation defaults for siphon CLIs.

Readme

@siphon/ui

The shared terminal floor for Siphon CLIs: a readable dark-first semantic palette, Unicode- and ANSI-safe text primitives, responsive tables, and explicit emphasis rules. It does not choose a site's information hierarchy or rows.

import { createTheme, renderTable } from "@siphon/ui";

const text = renderTable({
  theme: createTheme(),
  columns: [
    { key: "symbol", header: "SYMBOL", priority: 100 },
    { key: "price", header: "PRICE", align: "right", priority: 90 },
    { key: "volume", header: "VOLUME", align: "right", priority: 10 },
  ],
  rows,
});

priority keeps the most useful fields as the terminal narrows. A minWidth allows a text column to shrink before a field is dropped. Mark stable identity with required, remove genuinely empty fields with omitWhenEmpty, provide compact variants; every dropped non-empty value moves into a continuation under its header, while overflowLabel customizes that label. layoutTable() returns the active/dropped column receipt; renderTable() returns its text compatibility view. Colour follows the TTY/NO_COLOR contract and can be overridden with createTheme({ colors }).

For an existing typed record, columns may use value(row, index) instead of a key. flex shares the remaining terminal width, while detail(row, index) adds one bounded continuation line under the first flex column. Compact counts and relative timestamps use formatCompactNumber and formatRelativeAge so every listing speaks the same scan vocabulary. formatTriState renders source availability as Yes, No, or ; null is never guessed false.

For a site-native renderer, use the semantic palette directly rather than inventing colours per table:

import { createPalette } from "@siphon/ui";

const ui = createPalette();
ui.id("3");
ui.num("1.1k");
ui.warn("2y");
ui.tag("r/anime");
ui.asset("img:3");
ui.title("A row's primary field");
ui.highlight("standout");

tag() hashes a category to a stable terminal hue. selection() uses the ref role and highlight() the standout role. Styling only adds ANSI: NO_COLOR, piped, and explicit structured output remain plain, and every style closes only the attribute it opened so an outer dim line survives an inner coloured field.

Columns can opt into shared rule vocabulary without giving the package any product judgment: magnitude applies configured numeric tiers, threshold highlights a configured bound, and frequency highlights uncommon values.

renderRows is the compatibility seam for existing CLIs: it preserves an established row layout while gaining correct ANSI/emoji/CJK cell widths. New CLIs should start with renderTable.

renderComparison({ entities, metrics }) is the shortlist seam. It emits one transposed decision model as width-safe TTY, durable Markdown, or text; tied winners are preserved and a metric with only one known value awards nobody. Targets retain control of metric labels, values, proxies and whether lower or higher is better.

renderDocument(document) is the detail-view twin. It consumes the portable @siphon/core/document shape and applies the same palette and width engine to headings, nested lists, data/infobox tables, figures with captions, TeX math, quotes and code. Pass image(figure) or code(block) callbacks to replace a structured block with a target renderer without moving downloads, protocols or domain-specific diagrams into UI.

renderDocument(record.document, {
  width: process.stdout.columns,
  theme,
  image: (figure) => renderFocalImage(figure.media),
  code: (block) => block.language === "go-board" ? renderBoard(block.code) : undefined,
});

Run bun test for the package contracts and bun run gallery to write the review artifact at packages/ui/.ui/default-surfaces.html.