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

@elastic/distillate

v0.2.0

Published

Theme-agnostic CSS style engine: typed authoring with dependency capture, render-driven reachability collection, and dual readable/compact output targets. One runtime dependency: stylis.

Readme

A small, theme-agnostic engine for portable component-library artifacts where render-reachable CSS matters.

Distillate is a theme-agnostic CSS style engine for component library authors who need one authored style system to emit two forms: a readable stylesheet for host applications, and a compact, tree-shaken, render-reachable CSS payload for self-contained artifacts such as emails, SVG renders, agent replies, and exported HTML. It has one runtime dependency, stylis 4.4.0, and no brand assumptions: prefixes, theme tokens, and scope selectors all come from the environment you provide.

If you are styling an application directly, or need per-render dynamic class names, styled.*, keyframes, or object styles, see the comparison guide to decide whether Distillate fits.

Install

npm install @elastic/distillate

Requires Node.js 20 or later. The package ships as ESM (import) with a parallel CommonJS build (require) for consumers that cannot load ESM directly — see Installation.

Quick start

Bind the engine to your library with createDistillery. The snippets use an EUI-flavored environment to show that the engine carries no brand of its own.

import { createDistillery, cq, lightDark } from '@elastic/distillate';

const distillery = createDistillery({
  prefix: 'eui',
  themeScope: '.eui-view',
  theme: {
    colors: {
      ink: lightDark('#111', '#eee'),
      accent: lightDark('#06c', '#8cf'),
    },
    gap: cq('8px', '2cqi'),
  },
});

Author modules against the distillery. The derived token tree is tokens, typed by TokensOf:

const demo = distillery.createStyleModule('demo', ({ css, tokens }) => ({
  root: css`
    color: ${tokens.colors.ink};
    padding: ${tokens.gap};
  `,
}));

Emit a compact artifact payload (only what a render reached) or the full readable stylesheet:

const collector = distillery.artifactCollector('compact');
collector.use(demo.handles.root);
distillery.renderStyles(collector);
// => '.eui-view{--a:light-dark(#111,#eee)}.a{color:var(--a);padding:8px}'

distillery.renderStyles(distillery.stylesheetCollector());
// => '.eui-view{--eui-colors-ink:light-dark(#111,#eee)}.demo-root{color:var(--eui-colors-ink);padding:8px}'

Output matrix

The same source emits four kinds of CSS. Pick a target and a name mode:

| | readable | compact | | ---------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | | stylesheet | Public stylesheet: .demo-root, --eui-colors-ink. Use stylesheetCollector(). | Minified names for the full sheet. Rare; compact names only pay off when HTML and CSS ship together. | | artifact | Readable names, tree-shaken to the handles a render collected. | Minimal payload: .a, --b, unused variants and tokens dropped. Use artifactCollector('compact'). |

Collection is a side effect of rendering: resolving a handle pulls it in, which auto-activates any rule / media / container whose selector dependencies are all collected. Variant entries stay off always-on collection until a renderer names them. See Naming and output.

Entry points

| Import | What it is | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | @elastic/distillate | Engine: createDistillery, authoring (css, rule, media, container, variants, mapDomain), tokens, collector, renderer. | | @elastic/distillate/emotion | @emotion/css-shaped css / cx / injectGlobal over the same registry, plus createDomSink. | | @elastic/distillate/testing | assertVarRefsHaveDeclarations / findVarRefViolations. No third-party dependency. |

./testing is the only entry that does not reach stylis. Nested css templates walk stylis compile() output; the pin is 4.4.0 exactly.

Emotion-style authoring

import { createEmotion } from '@elastic/distillate/emotion';

const { css, cx, injectGlobal } = createEmotion(distillery);

const card = css`
  color: var(--eui-colors-ink);
  &:hover {
    color: var(--eui-colors-accent);
  }
  @media (min-width: 600px) {
    padding: 8px;
  }
`;

// <div className={cx('surface', card)} />

String(card) is the readable class name and does not collect. Passing the same handle through a collector's resolveClassName collects and compacts like a native handle. createDomSink manages one <style> element for the readable stylesheet.

To override, compose by interpolating a base handle into a new tagged template so declarations re-target the composing class (last wins). Render order is sort order, not registration order. Object styles, keyframes, and @container inside a css template are not supported; use the container(...) factory for container queries.

Single-copy requirement

The engine keeps module-scope caches (variant markers, dependent-entry maps, and the emotion per-registry state), so exactly one copy of @elastic/distillate may load at runtime. Consumers that bundle must externalize the package.

A second copy with a different identity token registers a console warning at import (Symbol.for('elastic.distillate.instance')). It warns rather than throws because two copies degrade output — variants(...) tree-shaking silently fails — without crashing. The sideEffects field lists **/instance.ts and **/instance.js so the guard survives both source and built consumption. HMR and a fresh test registry can also warn, because the token is regenerated on every load.

Public contract

  • prefix, module names, handle-path keys, and vars(...) group and key names must each be a CSS identifier segment: a letter or underscore, then letters, digits, hyphens, or underscores. Theme-tree keys omit hyphens: /^[A-Za-z_][A-Za-z0-9_]*$/.
  • Readable class names are ${moduleName}-${path.join('-')}. Readable custom properties are cssVarName(prefix, path). Distinct authored paths that join to the same string throw at createDistillery or registerModule.
  • Template interpolations are spliced into CSS verbatim. Authored CSS must never be bound to untrusted input.
  • Differing lightDark values fold into light-dark(...).

Docs and examples