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

mint-ds

v0.2.1

Published

Audit legacy CSS, curate the chaos, and generate a clean design system. CLI + web playground.

Readme

Mint 🎨

Mint audits your legacy CSS and generates a clean, exportable design system from the chaos.

It ships in two flavors:

  • CLInpx mint-ds audit ./src/styles && npx mint-ds export --target tailwind. Run it against a whole directory, scriptable, no UI.
  • Web playground — paste a snippet, walk through the 3-step wizard, preview tokens visually before exporting.

Both share the same prompts and Claude pipeline.

How it works

CSS / SCSS / HTML  →  Claude Audit  →  Review & curate  →  Clean tokens  →  Export
  1. Audit — Claude analyzes your CSS, groups near-duplicate colors into clusters, detects fonts, flags spacing values that don't fit a 4px grid, and identifies duplicate transition/animation declarations.
  2. Curate — Review each cluster. Pick the canonical color, rename tokens, include or exclude entries, and select which fonts to keep. (CLI applies sensible defaults: include every cluster, keep non-system fonts, use the suggested 4px scale.)
  3. Export — Generate production-ready output in any format.

Example — Frankenstein

A bundled example of the kind of CSS that grows organically over a few years: the same blue declared in 6 aliases, the same Arial family duplicated across 5 selectors, spacing values like 7px, 11px, 13px, 17px, 19px mixed with rem and em, and !important everywhere. Full input in examples/frankenstein/styles.css.

Before — examples/frankenstein/styles.css

:root {
  --color-primary:       #1976d2;
  --color-primary-caps:  #1976D2;              /* same color, caps */
  --color-primary-rgb:   rgb(25, 118, 210);    /* same color, rgb */
  --color-primary-rgba:  rgba(25,118,210,1);   /* same color, rgba */
  --color-primary-hsl:   hsl(211, 79%, 46%);   /* same color, hsl */
  --color-blue:          #1976d2;              /* extra alias */
  /* …same pattern for danger, bg, text, border */
}

body         { font-family: 'Arial', sans-serif !important; }
.app-root    { font-family: Arial, Helvetica, sans-serif !important; }
h1, h2, h3   { font-family: "Arial", "Helvetica Neue", Helvetica, sans-serif !important; }
p, span, li  { font-family: Arial, sans-serif; }
.card        { font-family: 'Arial', Helvetica, sans-serif !important; }

.card        { padding: 13px; }            /* odd */
.card-inner  { padding: 1rem; }            /* unit mixed */
.table-cell  { padding: 7px 11px; }        /* primes */
.metric-card { padding: 19px; }            /* odd */
.btn-lg      { padding: 11px 20px; }       /* 11 instead of 12 */
.sidebar     { width: 13rem; }
.modal       { padding: 1.5em; }
.modal-footer{ margin-top: 17px; }
.tooltip     { padding: 5px 9px; }

What Mint sees

| Signal | What's in the source | What Mint outputs | | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | Colors | 6 aliases for #1976d2, 3 for #e53935, 3 for #f5f5f5, 3 for #212121, 3 for #dddddd — all formatted differently (hex caps, hex lower, rgb, rgba, hsl, #DDD shorthand) | 7 named colors (primary, error, background, text, border, surface, muted), each with a 50–900 scale | | Fonts | 5 font-family declarations, all Arial-family permutations | 1 body family | | Spacing | 20+ values mixed across px / rem / em, including primes 7, 11, 13, 17, 19 | 5-step scale snapped to 4px: 4 / 8 / 12 / 20 / 24 | | Border radius | 4px and 8px scattered with !important | sm: 4px, md: 8px | | Shadows | .shadow and .shadow-md define the same value twice | one sm token | | Weights | bold and 700 declared as separate utilities (same thing) | bold: 700, extrabold: 800 | | Motion | 8+ selectors mixing transition/animation with conflicting units (200ms vs 0.2s), competing easings (ease-in-out vs cubic-bezier(…)), and properties written both as shorthands and individual declarations | Duration scale (fast: 150ms, base: 200ms, slow: 300ms) + easing scale (standard, emphasized) |

After — examples/frankenstein/mint-ds.tokens.json

{
  "brand": "frankenstein",
  "colors": [
    {
      "name": "primary",
      "value": "#1976d2",
      "scale": {
        "50": "#e3f2fd",
        "100": "#bbdefb",
        "200": "#90caf9",
        "300": "#64b5f6",
        "400": "#42a5f5",
        "500": "#1976d2",
        "600": "#1565c0",
        "700": "#0d47a1",
        "800": "#0a3f8f",
        "900": "#08357d"
      }
    },
    {
      "name": "error",
      "value": "#e53935",
      "scale": { "50": "#ffebee", "...": "...", "900": "#a01818" }
    },
    {
      "name": "background",
      "value": "#f5f5f5",
      "scale": { "50": "#fefefe", "...": "...", "900": "#a8a8a8" }
    },
    {
      "name": "text",
      "value": "#212121",
      "scale": { "50": "#f5f5f5", "...": "...", "900": "#141414" }
    },
    {
      "name": "border",
      "value": "#dddddd",
      "scale": { "50": "#f9f9f9", "...": "...", "900": "#6c6c6c" }
    },
    {
      "name": "surface",
      "value": "#ffffff",
      "scale": { "50": "#ffffff", "...": "...", "900": "#999999" }
    },
    {
      "name": "muted",
      "value": "#666666",
      "scale": { "50": "#f2f2f2", "...": "...", "900": "#333333" }
    }
  ],
  "typography": {
    "fontFamilies": { "body": "Helvetica Neue" },
    "fontWeights": { "bold": 700, "extrabold": 800 }
  },
  "spacing": { "1": "4px", "2": "8px", "3": "12px", "5": "20px", "6": "24px" },
  "borderRadius": { "sm": "4px", "md": "8px" },
  "shadows": { "sm": "0 2px 4px rgba(0,0,0,0.1)" }
}

Generated exports

The same tokens were exported in two flavors and committed alongside, so you can see the full pipeline without running it:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './src/**/*.{html,js,jsx,ts,tsx,vue}',
    './components/**/*.{html,js,jsx,ts,tsx,vue}',
    './pages/**/*.{html,js,jsx,ts,tsx,vue}',
    './app/**/*.{html,js,jsx,ts,tsx,vue}',
  ],
  darkMode: 'class',
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#e3f2fd',
          100: '#bbdefb',
          /* …300–800 */ 900: '#08357d',
          DEFAULT: '#1976d2',
        },
        // error, background, text, border, surface, muted — same shape
      },
      fontFamily: {
        display: ['Helvetica Neue', 'sans-serif'],
        body: ['Helvetica Neue', 'sans-serif'],
      },
      spacing: { 1: '4px', 2: '8px', 3: '12px', 5: '20px', 6: '24px' },
      borderRadius: { sm: '4px', md: '8px' },
      boxShadow: { sm: '0 2px 4px rgba(0,0,0,0.1)' },
    },
  },
  safelist: ['bg-primary-500', 'text-error-500', /* … */ 'font-extrabold'],
  plugins: [],
}

Each color collapses 3–6 source aliases into one named scale; safelist covers the utilities the source CSS used so Tailwind's JIT keeps them.

A single file with four ready-to-use Astro components — Button, Card, Badge, Input — wired to the design tokens via CSS variables. Excerpt:

---
interface Props extends HTMLAttributes<'button'> {
  variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
  size?: 'sm' | 'md' | 'lg';
}
const { variant = 'primary', size = 'md', ...rest } = Astro.props;
---

<button class:list={['btn', `btn--${variant}`, `btn--${size}`]} {...rest}>
  <slot />
</button>

<style>
  .btn--md      { padding: var(--spacing-3) var(--spacing-5); }
  .btn--primary { background-color: var(--color-primary-500); color: white; }
  .btn--primary:hover:not(:disabled) { background-color: var(--color-primary-600); }
  /* secondary, ghost, danger variants follow the same token references */
</style>

Notice how every value references a token (var(--color-primary-500), var(--spacing-3)) — there are no hardcoded hexes or magic numbers in the generated components.

Reproduce it locally

# 1. Audit the bundled example
npx mint-ds audit examples/frankenstein

# 2. Pick the export your stack needs
npx mint-ds export --target tailwind   # → tailwind.config.js
npx mint-ds export --target astro      # → components.astro
npx mint-ds export --target css        # → variables.css
npx mint-ds export --target react      # → components.tsx

The committed artifacts (mint-ds.tokens.json, tailwind.config.js, components.astro) are what the audit and exports produced on the maintainer's machine — your run will land in the same shape, with minor variation in scale stops if Claude picks slightly different intermediate values.

CLI

# Analyze every CSS/SCSS/HTML file in a directory and write mint-ds.tokens.json
npx mint-ds audit ./src/styles

# Generate exports from the resulting tokens
npx mint-ds export --target tailwind     # → tailwind.config.js
npx mint-ds export --target react        # → components.tsx
npx mint-ds export --target css          # → variables.css

Authentication

Every command needs an LLM provider API key. You have three options — pick whichever fits your workflow:

Option 1 — pass it per-command with --api-key:

npx mint-ds audit ./src/styles --api-key sk-ant-...

Useful for one-off runs, CI jobs, or when you don't want the key persisted in your shell.

Option 2 — set a per-provider env var (recommended when you use multiple providers):

export ANTHROPIC_API_KEY=sk-ant-...
export OPENROUTER_API_KEY=sk-or-...
export OLLAMA_API_KEY=sk-ollama-...    # optional — Ollama doesn't require a key

Option 3 — set the generic API_KEY env var (works with any provider):

| Shell | Command | | ----------------------------------- | ----------------------------- | | bash / zsh / sh (macOS, Linux, WSL) | export API_KEY=sk-ant-... | | fish | set -gx API_KEY sk-ant-... | | PowerShell (Windows / pwsh) | $env:API_KEY = "sk-ant-..." | | Windows CMD | set API_KEY=sk-ant-... |

These commands set the key only for the current shell session. To persist it, add the line to your shell rc file (~/.bashrc, ~/.zshrc, ~/.config/fish/config.fish, your PowerShell $PROFILE, etc.) or use the system Environment Variables dialog on Windows.

--api-key always wins over env vars. Per-provider env vars (ANTHROPIC_API_KEY, etc.) win over the generic API_KEY.

Where to get a key:

LLM provider

Mint talks to an LLM for audit, resolve, and export. By default it uses Anthropic Claude; you can swap to a local backend with --provider.

| Value | Description | | --------------------- | --------------------------------------------------------------------------------------------------------------------- | | anthropic (default) | Anthropic Claude API. Default model claude-sonnet-4-20250514. | | ollama | Local Ollama server. No API key required. Defaults to http://localhost:11434/api/chat, model gemma4. | | openrouter | OpenRouter API. Default model deepseek/deepseek-v4-flash, endpoint https://openrouter.ai/api/v1/chat/completions. |

Override model or URL with --model/--url or the corresponding env vars. See Environment variables for the full precedence chain.

# Run the audit against a local Ollama instance
npx mint-ds audit ./src/styles --provider ollama

# Generate exports with a local LLM
npx mint-ds export --target tailwind --provider ollama

--provider works on both audit and export. Passing an unknown name exits with Unsupported LLM provider: <name>.

All commands

| Command | Description | | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | mint-ds audit <dir> | Walk <dir> for .css, .scss, .sass, .less, .html files, audit them with Claude, and write mint-ds.tokens.json | | mint-ds export --target <name> | Read mint-ds.tokens.json and generate the chosen format | | mint-ds validate <file> | Validate tokens.json against DTCG v1 — structure, references, cycles, naming consistency | | mint-ds cache --clear | Delete the local mint-ds.cache.json cache file | | mint-ds --help | Show full usage |

Validate options

| Flag | Description | | --------------- | ---------------------------------------------------------- | | --spec <name> | Spec to validate against (default: dtcg) | | --json | Emit machine-readable JSON to stdout | | --no-semantic | Skip semantic checks (refs, cycles, naming, type mismatch) |

validate runs two layers of checks:

  • Structural — every node must be a valid DTCG token (with $value) or a group; $type is required and validated against the value.
  • Semantic — broken references, circular references, naming-convention drift, and reference type mismatches.

Exit codes: 0 valid · 1 warnings only · 2 errors. Structural violations and broken/circular references are errors (exit 2); naming drift and type mismatches are warnings (exit 1). Ready-made CI templates (GitHub Action + pre-commit hook) live in templates/dtcg/.

Audit options

| Flag | Description | | ------------------- | ------------------------------------------------------------------------- | | --out <file> | Tokens output path (default: mint-ds.tokens.json) | | --report <file> | Also write the raw AuditReport JSON for inspection | | --provider <name> | LLM backend: anthropic (default), ollama, or openrouter | | --api-key <value> | LLM provider API key (overrides all API key env vars) | | --model <name> | Model name (overrides all model env vars) | | --url <url> | API endpoint URL (overrides all URL env vars) | | --quiet | Skip the chaos summary printout | | --no-cache | Skip the cache lookup and overwrite any existing cache entry for this CSS |

Cache

mint-ds automatically caches audit results in mint-ds.cache.json (keyed by a SHA-256 hash of the preprocessed CSS). Subsequent runs on unchanged CSS skip the Claude API call entirely.

# Force a fresh audit, ignoring the cache
mint-ds audit ./src/styles --no-cache

# Inspect or clear the local cache
mint-ds cache           # list cached entries with their timestamps
mint-ds cache --clear   # delete mint-ds.cache.json

Add mint-ds.cache.json to .gitignore if you don't want to commit it.

Export options

| Flag | Description | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | --target <name> | Required. Accepts: tailwind, react, vue, svelte, astro, css, scss, ts, css-modules, styled, emotion (full names like tailwind-config, react-component also work) | | --tokens <file> | Tokens input path (default: mint-ds.tokens.json) | | --out <file> | Override the default output filename | | --provider <name> | LLM backend: anthropic (default), ollama, or openrouter | | --api-key <value> | LLM provider API key (overrides all API key env vars) | | --model <name> | Model name (overrides all model env vars) | | --url <url> | API endpoint URL (overrides all URL env vars) | | --stdout | Print to stdout instead of writing a file |

Local development without publishing

The CLI runs straight from a clone:

git clone https://github.com/nujovich/mint.git && cd mint
export API_KEY=sk-ant-...
node bin/mint-ds.mjs audit ./examples/site
node bin/mint-ds.mjs export --target tailwind
# or use a local LLM via Ollama — no API key needed
node bin/mint-ds.mjs audit ./examples/site --provider ollama
# or `npm link` to expose `mint-ds` globally for testing.

Export formats

| Category | Formats | | ---------- | -------------------------------------------------------------- | | Tokens | CSS Custom Properties, SCSS Variables, JS/TS Object | | Frameworks | Tailwind Config, Styled Components, Emotion Theme, CSS Modules | | Components | React + TypeScript, Vue 3 SFC, Svelte, Astro |

Stack

  • Next.js 15 — App Router, API routes
  • React 18 — Client components
  • TypeScript
  • Claude APIclaude-sonnet-4-20250514 for audit, resolve, and export generation by default; Ollama and OpenRouter are also supported as alternatives via --provider ollama or --provider openrouter

Getting started

Prerequisites

Setup

git clone https://github.com/your-org/mint.git
cd mint
npm install
cp .env.local.example .env.local
# Add your key: API_KEY=sk-ant-...
npm run dev

Open http://localhost:3000.

Environment variables

Mint resolves configuration through a precedence chain: CLI flag > per-provider env var > generic env var > provider default.

API keys

| Variable | Description | | -------------------- | --------------------------------------------- | | ANTHROPIC_API_KEY | Anthropic API key | | OPENROUTER_API_KEY | OpenRouter API key | | OLLAMA_API_KEY | Ollama API key (optional — Ollama needs none) | | API_KEY | Universal fallback for all providers |

Precedence: --api-key > {PROVIDER}_API_KEY > API_KEY > provider default (none for Anthropic/OpenRouter, undefined for Ollama).

Model name

| Variable | Description | | ----------------------- | ------------------------------------ | | ANTHROPIC_MODEL_NAME | Model name for Anthropic | | OPENROUTER_MODEL_NAME | Model name for OpenRouter | | OLLAMA_MODEL_NAME | Model name for Ollama | | LLM_MODEL_NAME | Universal fallback for all providers |

Precedence: --model > {PROVIDER}_MODEL_NAME > LLM_MODEL_NAME > provider default.

API URL

| Variable | Description | | -------------------- | ------------------------------------ | | ANTHROPIC_API_URL | API endpoint for Anthropic | | OPENROUTER_API_URL | API endpoint for OpenRouter | | OLLAMA_API_URL | API endpoint for Ollama | | LLM_API_URL | Universal fallback for all providers |

Precedence: --url > {PROVIDER}_API_URL > LLM_API_URL > provider default.

Project structure

bin/
  mint.mjs             — CLI entry point (audit + export commands)
app/
  api/
    audit/route.ts     — POST /api/audit   → AuditReport
    resolve/route.ts   — POST /api/resolve → DSTokens
    export/route.ts    — POST /api/export  → generated code string
  page.tsx             — 3-step playground wizard + CLI promo
  layout.tsx
  globals.css
components/
  CssInput.tsx         — Step 1: paste or upload CSS
  AuditView.tsx        — Step 2: review clusters, fonts, spacing
  TokenPreview.tsx     — Step 3: visual token preview
  ExportPanel.tsx      — Step 3: format picker + code viewer
  CliPromo.tsx         — "Try the CLI" block on the playground root
  StepBar.tsx          — Progress indicator
  CoffeeLoader.tsx     — Full-screen loading overlay
  CodeViewer.tsx       — Syntax-highlighted code output
lib/
  types.ts             — DSTokens, AuditReport, ExportTarget and all shared types
  prompts.mjs          — Prompt builders + Claude helper, shared by API routes and CLI

Contributing

See CONTRIBUTING.md.

License

MIT