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

@stealthscale/testing-theme

v0.2.0

Published

Lists every part of the theme contract a theme, a recipe or a preset breaks, and reads what a recipe declares and what a rendered component drew.

Readme

@stealthscale/testing-theme

@stealthscale/testing-theme checks a theme, a recipe and a preset against the theme contract, and reads what a recipe declares and what a rendered component drew. Each gate returns a list of sentences rather than a verdict, so one assertion reports which role, pair, token or file broke the contract.

Install

pnpm add -D @stealthscale/testing-theme

The package peers on @stealthscale/theme and vitest. Install both.

Usage

A theme package runs its theme through the gate, naming the preset it is layered on and the recipes the workspace publishes:

import { describe, expect, it } from "vitest";

import actions from "@acme/actions/theme";
import { publishedRecipes, violations } from "@stealthscale/testing-theme";
import foundation from "@stealthscale/theme/theme";

import { fathom } from "#index.ts";

describe("fathom", () => {
  it("keeps the theme contract", () => {
    expect(
      violations(fathom, {
        at: import.meta.dirname,
        base: foundation,
        recipes: publishedRecipes(actions),
      }),
    ).toStrictEqual([]);
  });
});

A component package runs each recipe and its preset through the gate:

import { presetViolations, recipeViolations } from "@stealthscale/testing-theme";

import { recipe } from "#button/button.recipe.ts";
import preset from "#theme.ts";

expect(recipeViolations(recipe, { parts: anatomy.keys() })).toStrictEqual([]);
expect(presetViolations(preset, { at: import.meta.dirname })).toStrictEqual([]);

A specification about a rendered component reads the classes on each element:

import { recipeClasses, variantClass } from "@stealthscale/testing-theme";

expect(recipeClasses(container, "button")).toContain(variantClass("button", "variant", "solid"));

Reference

violations(theme, options)

| Check | Reports | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | name.attribute | A name outside [a-z][a-z0-9-]*, which a page cannot write as the theme attribute | | contract.roles | A palette that leaves one of the twelve roles out, and a family that leaves one of its members out | | contract.modes | A color stated in one mode and not the other | | contract.references | A reference that points at a token nothing defines, or at itself | | contract.extensions | An extension naming a recipe key the workspace does not publish, or naming className or slots | | contract.variants | A variant on an axis the recipe does not offer, a value the axis does not offer, or a part the recipe's value does not style, where recipes maps each key to its recipe | | contract.compounds | A compound for a selection the component's recipe declares no compound for, or styling a part the recipe's compound does not, where recipes maps each key to its recipe | | contract.listed | A file under recipes/ or slot-recipes/ exporting extension that the theme does not list, where at is given | | contract.styles | A text, layer or animation style that states nothing, and a text style without a fontSize | | contrast.text | A text pair below 7:1: every ink on every surface, and each palette's contrast on its solids and its inks on its fills | | contrast.boundary | A boundary pair below 3:1: the emphasized line and the subtle ink on every surface, and each palette's solid and lines on the page | | contrast.focus | A palette's focusRing below 3:1 on any surface | | fonts.installed | A font package the theme names that does not resolve from at, and nothing where at is not given |

options.recipes lists the recipe keys the workspace publishes, or maps each key to its recipe, which adds the variants check and the compound check. publishedRecipes(...presets) builds that map out of the presets the component packages publish, so the list is the one an application installs rather than one written out by hand. options.base names the preset the theme is layered on, which the resolver follows a reference into. options.thresholds changes any of the three ratios. options.skip leaves a check out, each with a reason.

recipeViolations(recipe, options)

| Check | Reports | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | recipe.className | A class name outside [a-z][a-z0-9-]* | | recipe.values | A value that writes the class another value writes, across the recipe's axes, or that a boolean axis writes at true | | recipe.compounds | A compound without a name, two compounds under one name, and a compound whose name writes the class of a variant | | recipe.empty | A value or a compound that states no styles, whose class the runtime writes and no rule reaches | | recipe.defaults | A default naming an axis the recipe does not offer, or a value the axis does not offer | | recipe.selections | A compound matched on an axis the recipe does not offer, or on a value the axis does not offer | | recipe.jsx | A name in options.names that no jsx pattern matches, and a pattern that matches no name, where names lists what a consumer writes the component under | | recipe.colors | A color written outright, a ramp step, a reference, a hue, a palette role that does not exist, or colorPalette pointed at a hue | | recipe.tokens | A token the preset does not define, named by one word or by a path, in any category a property reads, and any composition name it does not define. A CSS-wide keyword and a size a box takes from its content are ignored | | recipe.conditions | A condition neither the compiler's base preset nor the preset defines | | recipe.lengths | A length in px, rem or pt on a property outside options.lengths, the compiler's token function and a custom property's fallback left out | | recipe.modes | _dark, _light, _osDark or _osLight anywhere in the recipe | | recipe.slots | A slot options.parts stamps no part for, and a part no slot styles | | recipe.subtle | fg.subtle as a text color |

The color properties and the category each property reads are taken from the compiler's base preset at run time. The tokens and conditions are read from options.preset, which is the foundation unless a theme package states its own.

boundViolations(recipe, draw, options)

Checks a bound component against its recipe. draw(props) renders the component with the props a caller would write and returns what it rendered into. The check renders the component once with nothing picked and once per value of every axis. On each render it reads the classes that open with the recipe's class off the element and compares them with what the recipe writes: the class of each value picked or defaulted and the class of each compound whose selection matches. Each class the element lacks is reported, and so is each class it has that the recipe does not write. A part of a slot recipe, named by options.slot, gets a value's class only where the value styles that slot. options.defaults names the values a binding fixes through its default props, whose classes the element has where nothing is picked. A fixed value is written by no JSX literal, so the check also reports one the recipe does not list under staticCss. options.subject finds the element where data-recipe and the slot class do not identify it.

import { render } from "@testing-library/react";

import { boundViolations } from "@stealthscale/testing-theme";

expect(boundViolations(recipe, (props) => render(<Button {...props} />).container)).toStrictEqual(
  [],
);
expect(
  boundViolations(
    recipe,
    (props) =>
      render(
        <List.Root {...props}>
          <List.Item />
        </List.Root>,
      ).container,
    {
      slot: "item",
    },
  ),
).toStrictEqual([]);

presetViolations(preset, options)

| Check | Reports | | ------------------- | ------------------------------------------------------------------------------------------------------------- | | preset.registered | A *.recipe.ts or recipe.ts file under at the preset does not register, and a key no recipe file defines | | preset.keys | A recipe registered under a key that is not its class name in camel case | | preset.slots | A slot recipe under recipes, and a recipe without slots under slotRecipes |

Readers

| Export | Reads | | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | recipeClass, variantClass, slotClass, slotVariantClass, compoundClass | The classes a recipe emits, in the naming scheme of @stealthscale/pandacss-naming | | axesOf, valuesOf, defaultsOf, slotsOf, scaleOf, byStep | What a recipe declares, without rendering | | recipeElement, slotElement, classesOf, recipeClasses, slotClasses | What a rendered component drew, by data-recipe on the element a recipe was bound to and on the root of a compound component, and by the slot class or data-part on one part | | resolved, palettesOf, extendedRecipes, fontsOf | What a theme states, with every reference followed | | publishedRecipes | Every recipe the presets of the component packages register, keyed as they register it, for options.recipes |

Licence

MIT. See LICENSE.