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

@untheme/css

v0.2.1

Published

Renders a theme's tokens to CSS custom properties.

Readme

@untheme/css

Renders a theme's tokens to CSS custom properties.

defineRenderer(source) returns a Renderer bound to a Source — an object exposing config.theme (the active theme, whose slots declare each token's type and whose modifier contexts back the static cascade) and tokens() (the active flat bindings, references intact). The core service already has this shape, so the common case is passing it straight through: defineRenderer(untheme). Every read happens lazily at render time, so a renderer over a reactive container re-renders when the state it read changes.

Usage

import { defineUntheme } from "@untheme/core";
import { defineRenderer } from "@untheme/css";

const untheme = defineUntheme(config, themes);
const renderer = defineRenderer(untheme);

renderer.root();
// :root {
//   --color-bg: #ffffff;
//   --color-accent: var(--color-ink);
// }

renderer.sheet();
// :root { --color-bg: #ffffff; ... }
// [data-color="dark"] { --color-bg: #111111; ... }

root() renders a single :root block over the renderer's active declarations (empty string if the contract holds no tokens). variables() gives the same declarations as data — a record of custom property name to CSS text, spreadable into a style object. property(token) and var(token) give the bare custom property name and its var() accessor for one token; value(token) gives one token's active binding as CSS text.

Rendering a static set

root(set) and variables(set) render a fixed snapshot instead of the source's live bindings. The set is keyed by token; each value is either a token name — emitted as a var() alias to that token's custom property, the bare form of a {token} reference — or a binding of the token's own type, exactly as the live accessor supplies.

renderer.root({
  "color.paper": { colorSpace: "srgb", components: [0, 0, 0] },
  "color.accent": "color.white", // bare token name → var(--color-white)
  "type.display": "type.body", // typography alias, sibling and all
});
// :root {
//   --color-paper: color(srgb 0 0 0);
//   --color-accent: var(--color-white);
//   --type-display: var(--type-body);
//   --type-display-letter-spacing: var(--type-body-letter-spacing);
// }

The set is partial: a token it omits emits no declaration, so a snapshot may cover any subset of the contract. The contract still supplies each token's $type — the set supplies only values. A token-name value serializes through the same path as an authored {token} reference, so a typography alias stays pair-wise. Omit the argument for the live behavior above.

References always emit as var()

A token bound to a {other.token} reference never inlines the target's value — it emits var(--other-token), whole-value or nested inside a composite value (a border's color, a shadow's offset, a gradient stop's position). A gradient stop position that references a number token scales through calc(var(--other-token) * 100%), since the position slot needs a percentage and the referenced custom property holds a unitless number.

This keeps a rebind of the target cascading through the stylesheet: change the referenced custom property once and every dependent picks it up, instead of each dependent baking in a copy of the value it had at render time.

A typography token carries a second declaration alongside its own, since the CSS font shorthand cannot hold letter spacing: --type-body-letter-spacing. A typography reference stays pair-wise — it points its own letter-spacing property at the target's, not at the target's font value.

The static cascade

sheet() renders the full cascade for a theme: the base bindings under :root, then each modifier's contexts as [data-<modifier>="<context>"] blocks, in the theme's composition order. Selecting a context becomes a data-attribute flip on the document root — every block shares the root's specificity, so later blocks in the theme's order win, mirroring how the service composes them at read time. A context with no overrides emits no block, since the base bindings already are that default context; an empty contract yields an empty string.

Values serialize by each token's declared $type: color, dimension, duration, fontFamily, fontWeight, number, cubicBezier, strokeStyle, border, transition, shadow, gradient, and typography each have their own CSS rendering (colors prefer a hex fallback and fall back to the color-space function otherwise; borders and transitions render as their shorthand; and so on). This package trusts its input — bindings arrive already validated against the contract by @untheme/schema, so serialization does not re-check them.

Types

  • Source<T> — what defineRenderer reads: { config: { theme }, tokens }.
  • Renderer<T>defineRenderer's return: property, var, value, variables, root, sheet.
  • Variable<Tok> — a token's custom property name, e.g. --color-bg.
  • Variables<Tok> — the record variables() returns.
  • Bindings<T> — a static set for root(set)/variables(set): each token mapped to a token-name alias or a binding.
  • Dashed<S> — a token name with dots replaced by dashes, the ident-safe form a custom property name is built from.
  • Inputs — the serializable input per token type: the type's own value shape, or a reference in its place.

The lower-level pieces defineRenderer is built from are also exported: serialize(type, value) and emit(type, value) for one token's CSS text, property(token) and dashed(token) for the custom property name, indirection(reference) for a var() accessor over a {token} reference, and FONT_WEIGHT_NUMBERS for the numeric weight behind each named fontWeight keyword.

Related

  • @untheme/core — the runtime theme service; its shape satisfies Source directly.
  • @untheme/schema — token contract types and the validation renderer input is trusted to have already passed.
  • untheme — umbrella package; re-exported at untheme/css.