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

@calibrate-ds/dna

v0.2.17

Published

Design-system conformance: extract a codebase's design identity (.dna/identity.yaml, with per-rule provenance), then keep source AND rendered output conformant to it. Framework-agnostic: resolvers → normalized IR → generic rules.

Readme

@calibrate-ds/dna

Design-system conformance for codebases that have a design identity — or need one extracted. dna reads what your codebase already believes (tokens, type scale, spacing grid, observed colours), records it as .dna/identity.yaml with per-rule provenance ({ value, from, at } — which side held authority, and when), and then keeps both your source and your rendered output conformant to it.

Requirements

Node.js >= 18.17.

Installation

npm install -g @calibrate-ds/dna

Or run it one-off with no install, via npx — note the full package name (dna alone resolves an unrelated package on npm):

npx @calibrate-ds/dna check

Once installed globally, the dna command is available directly:

dna check     # → "No identity found — nothing to check. Run `dna start` …"
dna start     # extract the identity + a starter config from this codebase
dna check     # now it checks — and it is clean on the code it extracted from

Starting from a Figma design system

If your design tokens live in Figma (via the CalibrateDS plugin or otherwise), dna doesn't import a token file directly — it only ever extracts from real, checkable source in your own codebase, the same as everything else it does. The reliable path: export the plugin's .tokens.css format (real CSS custom properties, including var() aliases), commit it into your repo, and either add it to tokenSources in ds-lint.config.json or just run dna start with it present under an includePaths directory — dna reads it through the same postcss-based extractor it uses for any other CSS, with the same tier inference, alias resolution, and fabrication guards. Verified directly against a real, 200+-variable plugin export: colors, aliases, and tiers all extract correctly with zero manual cleanup. Length-valued semantic tokens (--font-size-*, --spacing-*, --border-radius-*/--radius-* aliasing a raw scale via var(), e.g. --font-size-body-md: var(--scale400)) are classified by what they actually RESOLVE to, not by reference syntax — they route into scales.type, scales.spacingObserved, and scales.radius respectively, the sections that actually enforce type/spacing/radius membership, rather than landing in palette or a generic declaredScale bucket. A plain (non-semantically-named) length custom property still falls back to scales.declaredScale, used by off-grid-spacing's scale mode. All four axes — palette, type, spacing, radius — are enforceable from a .tokens.css import with zero manual cleanup. The CSV/JSON/JS export formats aren't a supported input today; .tokens.css is the one that goes through dna's real, hardened parser rather than a bespoke importer.

Commands

| Command | What it does | |---|---| | dna start | Extract an identity → .dna/identity.yaml (palette with tiers inferred from the resolution graph, type scale, observed-in-usage values annotated for review — the extractor surfaces, never silently drops). Writes a starter config in bare projects. | | dna check | The gate, diff-default: lint (tokens/literals/grid/scale) · cn-merge guard · rendered conformance (headless Chrome, needs puppeteer-core) · structural manifests. --audit for the whole tree, --only=lint,cn,render,manifest. | | dna resolve <value> | Reverse-lookup a raw value → identity token. Given a colour (#2E7D32, #2e7d32, or the equivalent rgb(...) — all resolve identically) or a length (16px/1rem), prints the matching identity token name(s) labeled by axis; a value on multiple scales (type/spacing/radius) reports all of them. The proactive counterpart to dna check — author in the token vocabulary first pass instead of writing a literal and getting it flagged after the fact. Tri-state and honest: a well-formed value that isn't in your identity prints "not a token" (no green tick), no identity prints the dna start prompt, and malformed input is a clean error. Detector only — reads the identity, writes nothing. | | dna allow <axis> <value> --why "<reason>" | Adopt a deliberate arbitrary value (type/radius/color/spacing) directly into identity.yaml's enforced section — immediate effect on the next dna check, no dna start re-run, no hand-editing YAML. --as <name> names it (required for radius/color). Stamps provenance and appends to .dna/decisions.jsonl. type/spacing adoptions survive dna start --force; radius/color do not unless the value is also present in source — dna allow tells you which applies when you run it. | | dna inventory | The component vocabulary, parsed fresh from code: name · props · use-when · path. Read it at session start — the component failure mode is ignorance, not defiance. | | dna similar | Two advisory duplication checks over the same scorer (role-normalized shape + class shape, asymmetric containment), diff-default (--audit for the whole tree). (1) Rebuild of an existing component — changed JSX scored against the component library ("you rebuilt <Metric> by hand"). (2) Clone of a net-new shape — near-identical subtrees clustered across the scanned files themselves, reporting a shape repeated ≥2× with no backing component ("extract one and reuse it") — the case rebuild-matching is structurally blind to, since neither copy has a library entry to match against. Both are advisory (exit 0), never gating — see MAINTAINERS.md non-negotiable #2. | | dna shapes | Derive a component set from committed design-frame dumps (distribution-ranked, rule of three). | | dna gen-manifest | Structural screen manifests from design frames (Sec-* children, y-order). | | dna figma diff --fresh=<dir> [screen] | Advisory, optional, hermetic structural change detector. Diffs a freshly-produced dump against the committed baseline dump and reports the Sec-* section delta per screen — sections added, removed, and reordered (same set, new y-order — a distinct category). See below. | | dna hook install | Pre-commit hook running dna check. Refuses to overwrite an existing .git/hooks/pre-commit it didn't write itself — pass --force to replace a foreign hook (its content is lost once you do, so back it up first if you need it). Re-running over its own prior install always proceeds without --force. |

dna figma diff — structural change detector

dna figma diff --fresh=<dir> answers "what changed structurally in the frames since I last looked?" without touching the network. It's optional and advisory — most useful to a team still treating Figma as a source of truth. dna is code-first, so a frame change is information, not automatically a code violation; this ships as a reporting command, never a gate.

  • Baseline vs fresh. The baseline is the committed dump gen-manifest already reads, <cacheDir>/<screen>.figma.json — the last frame state you accepted into git. The fresh dump is one you just produced (the identical extractor step gen-manifest uses, aimed at a scratch directory) and pass via --fresh=<dir>; per screen the fresh file is <dir>/<screen>.figma.json. One invocation diffs every configured screen; pass a screen name to diff just one.
  • Hermetic. Pure local dump-vs-dump. dna never fetches a dump, reads no credentials, and has no network-capable import — same isolation guarantee as dna check.
  • Structural only. Reports the Sec-* section delta (added / removed / reordered), reusing the exact prefix-filter + y-order + schema-validation path gen-manifest uses. A malformed baseline or fresh dump fails loud at validation (naming the file and the bad node), never a silent wrong or empty delta. Token-value and shape deltas are deliberately out of scope for now (future, separate axes).
  • Tri-state, middle never green. pass (✓, both dumps present and structurally identical) · no-op (•, no baseline — or no fresh — dump to diff against, naming the reason) · delta (Δ, a real structural change). A no-op never renders as a green tick.
  • Exit convention. Advisory: a real structural delta reports and exits 0 (like dna similar). The only non-zero exit is a hard error — a malformed dump (schema validation) or a missing --fresh argument — i.e. "couldn't compute a trustworthy answer", not "the frames changed."

MCP server

dna mcp [--cwd=dir] runs a standalone MCP server that exposes dna's findings as structured data to an MCP client (an AI IDE/chat), instead of a human reading CLI output. --cwd sets the project root to operate on; it defaults to the MCP client's workspace root, else the server process's own cwd. Independent of the @calibrate-ds/ cli MCP server — no shared code, no shared process.

| Tool | What it does | |---|---| | dna_check | Run dna's design-system conformance check and return the findings as structured data (rule, severity, file, line, value, and an optional "what would satisfy this" hint) — the same findings dna check prints to stdout and logs to .dna/log.jsonl. Detector only: this never edits source, config, or anything outside .dna/ — use the returned findings to make your own edits with your own tools. | | dna_inventory | Return dna's current component inventory (name, props, one-line "use when" description, file path) as structured data — the same data dna inventory --json prints. Check this before building a new component: a component that looks missing may already exist under a different name. Read-only — this tool never writes anything. | | dna_similar | Run dna's structural duplication check and return the findings as structured data, each tagged with kind: "rebuild" (a hand-rebuild of an existing component — file, line, matched component name/path, score) or "clone" (a net-new shape repeated across files with no backing component — size and the list of occurrences to extract). Same findings dna similar/dna similar --audit prints. Call this (alongside dna_inventory) before writing new component code: it catches reinvention that slips past a name-only inventory check, and net-new shapes being cloned across files. Detector only. | | dna_resolve | Reverse-lookup a raw design value to the project's identity token(s): given a colour (hex like #2E7D32, or rgb()/rgba()) or a length (e.g. 16px, 1rem), returns the matching token name(s) labeled by axis — a value on multiple scales (e.g. both a spacing step and a radius step) reports all of them, never one. The same lookup dna resolve <value> prints. Call this before writing a raw literal pulled from a design/frame, so you author with the token instead of a hardcoded value — the proactive counterpart to dna_check. Tri-state and honest: resolved, not-a-token, no-identity, or invalid. Detector only — never writes. | | dna_start_preview | Preview what dna start would extract and write — the palette (declared + observed-in-usage), type scale, spacing grid/scale, radius scale/idioms, Tailwind color families, and per-rule provenance annotations — as structured data, WITHOUT writing anything to disk. Use this to show a user what dna start is about to set up before they run it themselves. Preview only: dna start itself (CLI, user-initiated) is the only thing that writes. |

Every tool is detector-only — none of them ever write source, config, or anything outside .dna/; a calling agent uses the returned findings to make its own edits with its own tools. dna_check's payload also folds in the optional render/figma sub-checks (rendered conformance, structural manifests) when those peers are installed and configured, matching the CLI's own dna check behavior.

To make this self-serve for an agent working in a consumer project, dna start (and dna start --force) auto-writes a short, delimiter-scoped instruction block into that project's own CLAUDE.md/AGENTS.md, telling a coding agent to call dna_inventory/ dna_similar before creating a new component, dna_resolve before writing a raw value pulled from a design/frame (to author with the token instead of a literal), and dna_check after editing a design-relevant file — on its own initiative, no manual step required from the human, as long as dna mcp is configured as an MCP server in the agent's client. This is a cooperation convention, not an enforcement guarantee — dna hook install's pre-commit hook remains the real backstop regardless of whether an agent follows it.

CI / Team Usage

What to commit

.dna/ holds three files with genuinely different commit semantics — treat them asymmetrically, not as one directory to gitignore wholesale:

| File | Commit? | Why | | --- | --- | --- | | .dna/identity.yaml | Commit | The shared design-system record — the whole team reads the same palette/scales/families. Not derived output; it's a record, never overwritten without --force. Hand-maintain it after the first dna start. Promoting a value (e.g. moving a size out of an observed/review-annotated entry into a real scale) is durable only if you don't re-run dna start --force — a full re-extraction recomputes palette/type/etc. purely from a fresh source scan and does not merge with your prior edits, so it silently wipes anything hand-promoted that isn't also present in source. Treat --force as an explicit, understood full re-derivation, not a routine refresh. | | .dna/decisions.jsonl | Commit | The append-only trust-decision ledger: every severity change/rule override, with a reason. A new teammate reads this to understand why a rule was disabled — without it, that context only exists in one person's local .dna/ and is invisible to everyone else. | | .dna/log.jsonl | Gitignore | Per-run violation noise, rewritten on every dna check. Local scratch, not a record. | | .dna/state.json | Gitignore | A severity cache read/written every dna check — derived, not authoritative. |

dna start sets this up automatically: it writes or appends to your project's .gitignore, ignoring exactly .dna/log.jsonl and .dna/state.json (never the whole .dna/ directory), so identity.yaml and decisions.jsonl are committed by default. Re-running dna start/dna start --force is idempotent — it won't duplicate entries that are already there, and it never touches unrelated .gitignore content.

Recommended: add a CODEOWNERS entry for .dna/identity.yaml. Changing the design system should go through a reviewed PR, not land as a silent commit buried in an unrelated change:

# CODEOWNERS
.dna/identity.yaml  @your-design-system-owners

CI

Gate PRs on dna check in GitHub Actions by diffing against the PR's base commit, so the check only flags lines actually touched by the PR (not pre-existing issues elsewhere in the repo). dna check defaults to --base=HEAD, which isn't useful in CI — pass the PR's base SHA explicitly so the diff is computed against the branch being merged into, not the last commit on the PR branch itself.

--base takes the ref as --base=<ref> (equals sign, no space) — the flag parser only recognizes the --name=value form; a space-separated --base <ref> is silently ignored and the diff falls back to the HEAD default.

name: dna check

on:
  pull_request:

jobs:
  dna-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # full history so the base SHA below is resolvable

      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npx @calibrate-ds/dna check --base=${{ github.event.pull_request.base.sha }}

fetch-depth: 0 matters here: with the default shallow checkout, the base commit often isn't present locally. dna check verifies base resolves before diffing and exits non-zero with cannot resolve base ref '<ref>' — is the history shallow? (actions/checkout needs fetch-depth: 0) when it doesn't — it will never silently report "nothing to check" for an unresolvable ref.

Troubleshooting

dna mcp seems to hang / print nothing when I run it in a terminal.

This is expected — it's not stuck or broken. dna mcp starts a standalone MCP server that holds the process open on stdio, waiting for an MCP client to send it JSON-RPC messages. Run directly in a terminal with no client attached, there's nothing to respond to, so it just sits there until you Ctrl+C. It isn't meant to be run standalone to "see output" — the real usage path is registering it as an MCP server in an IDE or chat client, e.g.:

claude mcp add dna -- dna mcp

Once a client is talking to it, dna_check / dna_inventory / dna_similar / dna_resolve / dna_start_preview become available as tools it can call — see MCP server above.

Cannot find package '@modelcontextprotocol/sdk' / ERR_MODULE_NOT_FOUND when running dna mcp.

This means dependencies haven't been installed for packages/dna specifically. It only happens on a local/dev clone of this monorepo, not on a real npm install -g @calibrate-ds/dna or npx @calibrate-ds/dna@modelcontextprotocol/sdk is a regular dependencies entry in packages/dna/package.json, so a real npm install pulls it in automatically. If you're seeing this, you don't need to worry about it as a published-package user.

If you're working from a git clone, packages/dna has its own package-lock.json, separate from any install step at the monorepo root — fix it with:

cd packages/dna
npm install

Rules

  • off-grid-spacing checks grid divisibility, not scale membership, by default: a value that's a multiple of the configured grid (scale, default 4) passes even if it was never one of the actual declared steps — e.g. at grid: 4, p-11 (44px) passes because 44 % 4 === 0, even though 44 isn't in a scale like [4, 8, 12, 16, 24, 32]. This is deliberate: a literal-membership check false- positived on legitimate section spacing (py-8, py-12) that wasn't in an initially-declared scale, so grid divisibility is the permissive default for every existing user. Opt in to stricter checking with mode: "scale". In this mode, a value must be an exact member of a declared scale array — p-11 (44px, on-grid but not a real step) is flagged; p-8 (32px, in the array) is not. mode unset, or "grid", keeps the default divisibility-only behaviour unchanged.

    The declared array can come from two places, most specific wins:

    1. an explicit override in the rule config itself:
      "off-grid-spacing": { "severity": "error", "mode": "scale", "scale": [4, 8, 12, 16, 24, 32] }
    2. failing that, .dna/identity.yaml's own scales.spacingScale — written automatically by dna start when a project's own tailwind.config.* declares a real theme.spacing/extend.spacing (the project's genuine declared scale, not a guess), or hand-authored the same way. Absent from the identity for every project that hasn't declared one — no behaviour change for them.

    If mode: "scale" is set but neither source resolves an array, the rule falls back to the grid check rather than silently going permissive or over-flagging.

Principles

  • A ✓ is a claim that something was verified. Three outcomes everywhere: pass (with counts) · nothing-to-check (named reason + fix, never green) · fail.

  • Coverage is accounted for, not assumed. Every file matched by includePaths in dna check (and every file walked by dna start) lands in exactly one bucket: examined, or skipped with a specific reason — no-resolver (unsupported file type, e.g. .vue/.svelte), resolver-unavailable (e.g. incompatible/absent typescript), parse-error (a file of a supported type that failed to parse), or unreadable (permission denied or similar). The headline reconciles the totals when any skips occurred — N examined · M skipped (reason breakdown) — so a clean or passing result never gets misread as full coverage when part of the codebase was silently unexamined. The same standard applies one layer up, at scope resolution itself: an unresolvable --base git ref (e.g. a shallow clone missing the PR's base SHA) fails loudly with a specific message instead of silently reporting zero changed files, and a nonexistent includePaths/excludePaths entry is reported as a named warning instead of reading as an empty project.

  • Information is never invented, either. The inverse of the law above: a value entering .dna/identity.yaml, dna inventory, or any other artifact is validated as well-formed for its type before it's written — a parser's error-recovery output on a broken file, or a regex's malformed match on unusual-but-valid input, is not data. A file that fails this validation is a parse failure, reported the same way any other skip is, never silently harvested and never silently dropped.

  • Information never disappears silently — see CLAUDE.md for the design law and the tests every feature must pass.

  • Extract, don't impose. Tiers (primitive/semantic) are inferred from the alias graph, never required; flat one-tier projects see no mention of tiers anywhere. Naming conventions are read as signals, never demanded.

  • Isolation. Runs in a bare directory: no config, no git, no network, no credentials. Owns .dna/ and writes nowhere else. typescript (~20MB, JSX/TS resolution) and puppeteer-core (rendered checks) are optional peers — the core installs with one dependency (postcss) and degrades cleanly, stating what it skipped.

  • Each axis picks the guarantee that fits it — and says so explicitly, not left for users to discover by diffing behaviour. Four axes, four independently-justified choices:

    | Axis | Default guarantee | Opt-in / escape hatch | |---------|-----------------------------------|------------------------| | Color | Membershipfamilies.allowed/palette | — (color either is or isn't yours; no useful middle ground) | | Type | Membershipscales.type | — (a font size either is a real step or it isn't) | | Radius | Membershipscales.radius + idioms | — (see below: idioms are a categorical exemption, not a config toggle) | | Spacing | Divisibility — grid multiple of scale (default 4) | mode: "scale" → membership, same as the others |

    Color and type are enforced as identity — exact membership — because a color either is or isn't yours, and a font size either is a declared step or it isn't; there's no useful "close enough" for either. Spacing defaults to hygiene (grid divisibility only — off-grid-spacing) because "any multiple of the grid" is a real, permissive design position many codebases hold on purpose; opt into identity-level strictness with off-grid-spacing's mode: "scale" (below). Radius (#135) is membership like color/type, NOT divisibility like spacing's default — a radius value either matches a reviewed scale step or it's flagged, full stop, no permissive "any multiple of 4px" fallback — except for a first-class idiom exemption: pill/circle shapes (999px, 9999px, 50%) are categorically different from "a scale step that happens to be large," so they're always legal regardless of scale membership, via .dna/identity.yaml's scales.radiusIdioms (seeded with the three industry-standard values by default; dna start also auto-detects likely idioms from your own source — any value ≥100px, or any %-based value, since a real radius scale never has steps that large). This is why radius isn't simply "spacing's mode: 'scale' twin": it needed its own categorical exemption that spacing's membership opt-in doesn't have a parallel for.

    Spacing: if you want spacing enforced as identity too — exact membership in a reviewed set of real steps, not just anything grid-divisible — opt in explicitly with off-grid-spacing's mode: "scale". dna start gives that opt-in something real to point at: it extracts a frequency-ranked, review-annotated candidate scale from actual usage (CSS padding/margin/gap declarations, merged with Tailwind class usage) into .dna/identity.yaml's scales.spacingObserved, for you to promote into scales.spacingScale. Once mode: "scale" is active, an on-grid value that isn't in that promoted scale gets a prefer-scale-step nudge (warn) naming the nearest real step, distinct from off-grid-spacing's hard error for values that are off-grid outright.

    Radius: dna start extracts a frequency-ranked, review-annotated scales.radius from real border-radius:/border-*-radius: CSS declarations, merged with Tailwind rounded-[Npx] class usage — the same extract-then-review shape spacing/type use, written directly as the enforced scale (no separate observed→promoted staging step, since radius has no curated Tailwind-config source the way spacing does). A CSS declaration on the scale (or in radiusIdioms) is legal outright; a Tailwind class on the scale downgrades to prefer-token-class (swap the arbitrary class for the real token); neither → arbitrary-values.

Layout

core/ model · resolvers · rules · lint · identity · start · inventory · similar · check-cn — render/ rendered conformance — figma/ manifests + shape extraction — bin/dna.mjstest/ the fixture suite (npm test) — scripts/check-generic.mjs keeps this repo project-agnostic (no consumer names in tooling logic, ever).