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

@newtone-dev/ui-tokens

v0.12.0

Published

Framework-agnostic design tokens for Newtone: core scales and component-scoped semantic tokens, emitted for every platform.

Downloads

747

Readme

newtone_ui_tokens

Newtone's visual language: colour, typography and spacing as named values, derived from a handful of seeds and emitted for every platform Newtone targets.

Zero dependencies. node build.mjs is the whole toolchain. dist/ is committed, so a consumer with no Node at all can just take the file it needs.

No network, ever. Which is why families.json — what each typeface can actually do — is authored and committed rather than fetched. scripts/inspect-family.mjs regenerates an entry and is run by hand.

The authoritative description of this repo is ~/source/blueprints/newtone/repos/newtone_ui_tokens.md. This README is a summary; if the two disagree, the blueprint wins.

Layers

seed  <  core  <  role  <  semantic
  • seed — the inputs colour and typography are computed from. Two keys, color and font, each named after the core group it computes. A colour palette is a hue and a saturation and nothing else; saturation is a ratio of the chroma reachable at that hue, so one number means the same thing for a blue as for a yellow. A font role names a family from families.json, four weights, and optionally axis positions and features.
  • core — scales. color.system.accent.25, space.4, font.body.family, breakpoint.compact. The color and font groups are computed; there is no color.json and no font.json. A source file says what kind of number it holds — "$type": "length" or "ratio", inherited by everything under it — and CSS emits rem or em accordingly. breakpoint is the one group CSS cannot use where it matters: a media query condition cannot read a custom property, so the value is published and the hand-written copies are checked by tools/check-breakpoints.mjs in ~/source.
  • role — colours for things whose area is fixed by what they are. role.surface.primary, role.divider, role.text.secondary. Eleven of them.
  • semantic — component tokens. button.primary.rest.fill. A component never reads a role: contrast that reads as equal depends on the area carrying it, so a button's label and a paragraph cannot share a value.

Above core, a colour is a clamp — color(accent, 13, 23) — not a palette index. It names a contrast distance, and mode and elevation resolve it.

One exception, and it has to say so out loud. fixed(accent, 2) names an absolute step and resolves the same in every context. It is for a component that stands in for a photograph, which has no contrast question to ask; it is legal only in the semantic layer, and a bare {color.system.accent.2} above core is still a build failure. media-plate.fill is its one use.

A reference points at a strictly lower layer, so cycles are impossible and there is no cycle detection anywhere in the build.

Facets

| Facet | Coordinates | Where it lives | | --- | --- | --- | | identity | open — an end user's colour | seed | | gamut | oklch, srgb | every core colour leaf | | mode | light, dark | resolved from a clamp | | theme | default, tinted, emphasis, strong | authored, role + semantic | | elevation | high, base, deep | resolved from a clamp |

A facet never appears in an emitted token path. theme is written down — one expression per cell, because a theme replaces the ground and sometimes the palette with it, and no clamp says that. mode and elevation are never authored: one clamp covers both modes, and all three elevations, which is what a contrast axis buys.

Eleven roles × four themes is 44 expressions; Button is 39 colour slots × four themes = 156. Everything resolved in a context takes the same elevation offset, so two tokens hold their step distance whatever elevation they are on.

theme is surface intent, not dark mode — strong is an inverted default, and both modes have all four themes. Since 0.8.0 the inversion is exact: every strong clamp is its default clamp mirrored, color(p, 23 − max, 23 − min), so strong in light mode is default in dark mode.

Build

node build.mjs        # or: npm run build

Five files land in dist/:

| File | For | | --- | --- | | newtone.css | the web — custom properties, one block per context | | newtone.json | canonical; what a runtime parses to re-derive colour | | newtone.resolved.json | consumers that can do nothing — react-email, static export | | Newtone.swift | Apple, via newtone_ui_swift | | Newtone.kt | Android, via newtone_ui_android |

Using it on the web

<link rel="stylesheet" href="dist/newtone.css">

<div data-newtone-surface data-newtone-theme="emphasis">
  <button style="background: var(--newtone-button-primary-rest-fill);
                 color:      var(--newtone-button-primary-rest-text)">Primary</button>
</div>

Mark a surface with data-newtone-surface, set data-newtone-theme / -elevation on it and data-newtone-mode anywhere above. Never name a colour. The button above inverts on an emphasis surface without being told anything.

Changing the look

Edit src/seed/seed.json and rebuild. A hue lives in exactly one place; the ramp, every role that reads it and every component below all follow.

To re-theme at runtime — a tenant's colours, one page under a collaborator's brand — set the --newtone-color-* properties on the scope element: fifty-one per palette you are replacing, and everything downstream follows because it is all references. example/index.html does exactly this, live, by importing compute/ in the browser — the same generator the build runs, not a second copy of it.

Studio

npm run studio        # then open http://127.0.0.1:4257

A local page for editing the clamps in src/semantic/*.json and adding Google families to families.json. Every save rebuilds dist/, and a value the build refuses is put back with the build's own message. It commits locally and never pushes. It does not ship in the package.

Example

python3 -m http.server 8000   # then open example/index.html

Seed sliders, all four themes, elevation nesting, and a scoped identity.

Colour derivation

Specified in ~/source/blueprints/newtone/contracts/color-derivation.md, including test vectors. compute/ holds the only implementation; build.mjs contains no colour maths at all. Verified: the neutral and accent palettes re-derive exactly, all fifty-one steps each, against the contract's tables.

Fourteen palettes of fifty-one steps, from fourteen hues and fourteen saturations. There is no ramp table anywhere. A step's lightness is found by searching for the OKLCH L whose CIE L* is 100 - 2 × step, and its chroma is the seed's ratio of the Display P3 ceiling at that lightness — so the chroma curve is the gamut boundary, and a seed cannot ask for more colour than exists.

Every colour leaf carries a wide-gamut OKLCH value and a precomputed sRGB hex. They are two derivations, not one value encoded twice, and they do not round-trip — one is what a P3 display shows, the other what an sRGB display shows, and they agree wherever both can.

compute/vector.mjs and compute/scale.mjs are a deliberately diffable copy of the prototype they came from. Do not tidy them; the contract specifies them to the iteration count.