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

okchroma

v0.7.0

Published

Color-system engine: one brand hex in, a light and dark set of color primitives out, with the contrast requirements solved during generation; emitted as CSS custom properties, DTCG documents, and Figma variables

Readme

OKChroma

A color-system engine. Give it one brand hex, or two, and it resolves a complete set of light and dark color primitives whose contrast requirements are solved during generation, then emits it as CSS custom properties, as DTCG documents, and as Figma variables, every token spelled the same way in each.

Every family (neutral, brand, brand-alt, critical, warning, positive, info) carries the same scale: 3 papers, 4 chalks, 1 highlighter, 1 pencil, 2 pens, plus the stamp (the solid fill with its hover and pressed states, its edge, and its text). The neutral adds the two poles. Two link trios and the two brand seeds complete the set. Light and dark resolve together and ship on the same names, so a token is mapped once and holds for any brand.

Contrast is built into the math, not checked afterwards: highlighter reads on every paper at 3:1 (the non-text bar), pencil at 4.5:1, pen at 4.5:1 on every paper and chalk in both directions; the stamp's text passes 4.5:1 on its fill. APCA is used once, as a booster that nudges the stamp fill until its text reads at Lc 65. Each claim, its scope, and the audit that proves it: Guarantees.

The engine emits primitives only: values it calculates from the seed. Elevation planes, shadows, opacity ladders and state layers are a design system's to author on top of these, and docs/agents.md says which primitive each of those decisions reads.

The reserved-role-per-stop model is a conceptual nod to Radix Colors. It is not a dependency and does not touch the math; the color computation is original.

Install

npm install okchroma

ESM and CommonJS builds with TypeScript declarations; no runtime dependencies (the one perceptual-distance library the engine uses is bundled in). Node 18 or later; no DOM.

import { resolveTheme, brandCss, signalsCss, themeTokens, tokensToDtcg, neutralTintHue } from 'okchroma'

const theme = resolveTheme({ primaryHex: '#E93D82', name: 'acme', deriveSecondary: true })
const neutralH = neutralTintHue(theme.themed.scale.brandH)

// CSS custom properties: a light and a dark block per brand, the signal block once
const css = brandCss('acme', 'Acme', theme.themed, theme.secondary?.scale ?? null,
  '', 'default', undefined, theme.secondary?.style, false, null, true, neutralH)
  + '\n' + signalsCss()
// put css in a stylesheet; set data-brand="acme" on the themed root,
// and data-theme="dark" on it for dark mode

// the same values as one object per mode, every var() resolved
const tokens = themeTokens({ slug: 'acme', brand: theme.themed, secondary: theme.secondary?.scale ?? null, secondaryStyle: theme.secondary?.style, neutralH })

// the DTCG documents: one per mode, every token with its description
const { light, dark } = tokensToDtcg(tokens)

The Figma tree comes from themeToFigma. Signatures, the full input, and an end-to-end example: Install and API.

What it emits

Every token has one path, and every output spells it: brand/stamp/fill, neutral/paper-0, link/default/enabled, absolute/brand. Joined with hyphens it is the CSS custom property (--brand-stamp-fill); joined with slashes it is the Figma variable and the DTCG token path.

| Group | Tokens | |---|---| | neutral | paper-0, paper-1, paper-3, paper-5, chalk-8, chalk-11, chalk-15, chalk-20, highlighter-26, pencil-47, pen-58, pen-70, pen-100, and the stamp: stamp/fill, stamp/fill-hover, stamp/fill-pressed, stamp/edge, stamp/on | | brand, brand-alt, critical, warning, positive, info | the same eleven stops and the same five stamp tokens | | link | default/enabled, default/hover, default/pressed for text on the papers; inverse/enabled, inverse/hover, inverse/pressed for text on the pen ground | | absolute | brand, brand-alt: the seeds as given, reference values |

What each name means, which stop to read for which job, and the rules for an agent writing UI against these: docs/agents.md. The scale and its declared targets: docs/scale.md.

The stops. The instrument word is the job, the number is 100 minus the light-mode lightness target, and bigger is stronger. Papers are backgrounds; chalks are decorative borders and illustration, never text; the highlighter is the non-text contrast stop (focus rings, icons, large text, and the one stop to build translucent state layers on); the pencil and the pens are text, and the pencil doubles as the emphasis fill.

The stamp. stamp/fill is the call-to-action fill, re-solved per family and theme; fill-hover and fill-pressed are its states; stamp/on is the text over it, the pole that passes (the pole at alpha on a quiet fill); stamp/edge is a stroke that resolves to a visible offset only where the fill sits close to the page, else transparent, so a component renders the border unconditionally and layout never shifts.

The outputs.

  • CSS: brandCss writes [data-brand="acme"] and [data-brand="acme"][data-theme="dark"] blocks, signalsCss the brand-independent signal block at :root. Values are sRGB hexes (or rgba() where a token carries alpha); stops whose chroma exceeds sRGB get a color(display-p3 …) override behind a browser gate. The repo's npm run generate writes the signal block to dist/signals.css.
  • The structured emit: themeTokens reads the CSS emission back into one object per mode with every reference resolved, for consumers with no cascade.
  • DTCG: tokensToDtcg returns one Design Tokens Format Module 2025.10 document per mode, every token with $type, an sRGB $value whose components and hex are the same 8-bit color (or an alias where the CSS writes a reference), and a $description. The two join through a resolver document. The format: docs/schema.md. The repo's npm run tokens:emit -- '#E93D82' acme writes both files.
  • Figma: themeToFigma returns a light and a dark group tree on the same paths; the extended plugin writes it into a file.

Run from source

npm install
npm run demo:build      # writes dist/signals.css and bundles the demo
npx serve .             # open http://localhost:3000/demo/index.html
npm run dev             # watch mode

npm run typecheck runs the compiler; the audit gates (npm run req:audit, npm run audit:guarantee, npm run audit:dtcg, and the rest of package.json) each sweep agnostic seeds and fail on the worst case. What each proves: How it is verified.

Documentation

The docs site renders the mechanisms with live values from the engine: egerrity.github.io/okchroma/#/docs.

In the repo: docs/agents.md (the consumer contract: what every token means and the rules for using it), docs/schema.md (the DTCG documents), docs/scale.md (the scale and its declared targets), docs/architecture.md (the maintainer's map: modules, pipeline stages, data structures, the extended plugin's write path), and CHANGELOG.md.

The Figma plugins

  • OKChroma Extended (plugin-ext/) is the shipped Figma front-end. It requires the Figma desktop app and a Figma Enterprise plan: it writes extended variable collections, one base collection with light and dark modes plus one extension per brand that overrides only what differs. Download and install steps: install page. Build from source with npm run plugin-ext:build; see plugin-ext/README.md.
  • OKChroma (plugin/, the community plugin) is withdrawn from download until it carries the rename table that migrates an existing file across the scale change of July 2026. It still builds from source with npm run plugin:build and imports from plugin/manifest.json; a fresh file gets the current shape.

The demo and the plugins are front-ends. The product is the engine and what it emits.

License

MIT