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

tailwind-token-extractor

v0.2.0

Published

Extract fully-resolved Tailwind CSS v4 design tokens from a CSS entry point — including @import-ed design systems, Tailwind defaults, var() chains and light/dark — into typed TypeScript.

Readme

tailwind-token-extractor

Extract fully-resolved Tailwind CSS v4 design tokens from a CSS entry point into typed TypeScript.

Tailwind v4 keeps design tokens in CSS (@theme), so there is no first-class way to read the resolved token set back in TypeScript — which makes it painful to render token tables in docs, share values with non-Tailwind code, or build a design-system reference. tailwind-token-extractor reverses the direction: point it at the CSS your app actually loads, and it gives you the complete token set as a typed .ts module.

"Complete" means it follows the entire @import graph the way Tailwind itself does:

  • Tailwind defaults — pulled from the actually-installed Tailwind via @import 'tailwindcss' (so container, blur, ease, … come through, version-accurate, and --namespace-*: initial clears are honored).
  • Imported design systems@import '@your-scope/design-system/styles.css' is resolved from node_modules.
  • Consumer additions — anything you add in your own globals.css wins, per the cascade.
  • Resolved valuesvar() chains are followed to literal oklch(...)/lengths, with separate light and dark values.

How it works

A hybrid of the Tailwind engine and a CSS AST pass — each job handled by the source that actually carries the data:

| Need | Source | | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | Authoritative, merged keyspace (never tree-shaken); surviving defaults; initial clears | @tailwindcss/node __unstable__loadDesignSystem().theme.entries() | | Literal oklch/length values; the raw :root var layer; light and dark | compile().build([]) output, parsed by selector class (:root/:host vs .dark) | | @import graph resolution (incl. node_modules) | the Tailwind engine's own resolver |

var() chains are then dereferenced against the light map and the dark map separately. Unresolvable chains (e.g. a host-injected --font-*: var(--font-*)) are kept verbatim and reported in meta.unresolved rather than crashing.

Install

pnpm add -D tailwind-token-extractor

tailwindcss (>=4.2) and @tailwindcss/node are peer dependencies — the tool extracts against the version your project already has installed.

CLI

# Write a typed tokens module
tailwind-token-extractor src/app/globals.css -o src/generated/tokens.ts

# Watch the @import graph and regenerate on change
tailwind-token-extractor src/app/globals.css -o src/generated/tokens.ts --watch

# Print to stdout
tailwind-token-extractor src/app/globals.css --stdout

# CI guard: fail (exit 1) if the committed file is stale
tailwind-token-extractor check src/app/globals.css -o src/generated/tokens.ts

Options: --base <dir> (node_modules resolution root, defaults to the entry's directory), --dark <selector> (default .dark), --no-resolve-vars.

Exit codes: 0 ok · 1 extraction error / drift · 2 incompatible Tailwind internals (prints the detected version).

Programmatic API

import { extractTokens, emitTypeScript, generate, watch } from "tailwind-token-extractor";

const tokens = await extractTokens({ entry: "src/app/globals.css" });
const code = emitTypeScript(tokens);

// or in one step
await generate({ entry: "src/app/globals.css", outFile: "src/generated/tokens.ts" });

Generated output

export const tokens = {
  theme: {
    color: {
      "fg-base": { light: "oklch(0.25 0.0015 235)", dark: "oklch(0.975 0.001 235)" },
      // cleared defaults (e.g. red-500 under `--color-*: initial`) are absent here
    },
    text: {
      md: { value: "1rem", lineHeight: "calc(1.5 / 1)", lineHeightNumber: 1.5 },
    },
    radius: { md: "0.5rem" },
    container: { md: "28rem" }, // surviving Tailwind default
    spacing: "0.25rem",
  },
  vars: {
    // the raw :root/.dark layer the theme references
    "gray-50": "oklch(0.975 0.001 235)",
    "fg-base": { light: "oklch(0.25 0.0015 235)", dark: "oklch(0.975 0.001 235)" },
    "z-overlay": 1000,
  },
  refs: {
    // the symbolic var() target each aliased var points at, per mode
    "fg-base": { light: "gray-900", dark: "gray-50" },
  },
} as const;

export type ColorToken = keyof typeof tokens.theme.color;
export type VarName = keyof typeof tokens.vars;
export type RefName = keyof typeof tokens.refs;
  • A namespace with a single bare token (--spacing) collapses to a scalar.
  • A token that changes in dark mode becomes { light, dark }; otherwise it stays a scalar.
  • Purely numeric values (z-index, unit-less line-height) are emitted as number.
  • refs records the immediate var() target of each var defined as var(--x), per light/dark mode — the symbolic link (fg-basegray-900/gray-50) that the resolved vars literals discard. Literals and one-mode-only references are omitted; entries are always a { light, dark } mapping.

Scope (v1)

Out of scope for now: consumer @utility/@custom-variant declarations (only resolved token values are emitted), generated-utility metadata, and dark modes expressed via @media (prefers-color-scheme) or arbitrary variants (the .dark class form is supported; --dark is configurable).

License

MIT © k8o