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

@compare-ui/storybook

v0.1.0

Published

Storybook helpers for design-comparison workflows.

Readme

@compare-ui/storybook

Storybook helpers for design-comparison workflows.

This package lets you:

  • define design-comparison metadata directly on Storybook stories
  • register Playwright tests from Storybook stories
  • turn one story into multiple design-comparison cases

Installation

pnpm add @compare-ui/storybook @compare-ui/runner @compare-ui/core
pnpm add @playwright/test

This package assumes your app already has its normal Storybook packages installed.

What it exports

Main public APIs:

  • defineStorybookDesignComparison<TArgs>(...)
  • registerStorybookDesignComparisonTests(...) from @compare-ui/storybook/playwright

Main public types:

  • StorybookDesignComparisonConfig<TArgs>
  • StorybookDesignComparisonCase<TArgs>

The published public entrypoints are:

  • @compare-ui/storybook
  • @compare-ui/storybook/playwright

Story metadata

Use defineStorybookDesignComparison<TArgs>(...) inside parameters.designComparison.

Example:

import { artifactRefs, gridRowArtifact, overlayArtifact } from '@compare-ui/core/config';
import { defineStorybookDesignComparison } from '@compare-ui/storybook';
import type { Meta, StoryObj } from '@storybook/react-vite';

import { InteractiveExampleScreen } from './interactive-example-screen';

type ExampleStoryArgs = {
  title: string;
  mode: 'default' | 'fullScreen';
};

const meta = {
  title: 'Screens/ExampleScreen',
  render: (args: ExampleStoryArgs) => <InteractiveExampleScreen {...args} />,
} satisfies Meta<ExampleStoryArgs>;

export default meta;

type Story = StoryObj<typeof meta>;

export const My: Story = {
  args: {
    title: 'Interactive Screen 123',
    mode: 'default',
  },
  parameters: {
    designComparison: defineStorybookDesignComparison<ExampleStoryArgs>({
      compare: {
        threshold: 0.1,
        acceptance: {
          maxDiffPercent: 0.5,
        },
      },
      artifacts: [
        overlayArtifact(),
        gridRowArtifact({
          items: [artifactRefs.overlay, artifactRefs.actual, artifactRefs.reference],
          size: 16,
          lineWidth: 2,
        }),
      ],
      cases: [
        {
          name: 'default',
          args: {
            mode: 'default',
          },
          reference: {
            type: 'fs',
            path: './tests/fixtures/example-screen-default.png',
          },
        },
        {
          name: 'full-screen',
          args: {
            mode: 'fullScreen',
          },
          reference: {
            type: 'fs',
            path: './tests/fixtures/example-screen-full-screen.png',
            crop: {
              bottom: 20,
            },
          },
        },
      ],
    }),
  },
};

Cases

Each story may define one or more comparison cases.

The shared story-level config may also define capture defaults for all cases:

  • targetSelector
  • settleDelayMs
  • strictRenderHealth
  • surfaceBackground

Each case can provide:

  • name
  • args
  • reference
  • viewport
  • targetSelector
  • settleDelayMs
  • strictRenderHealth
  • surfaceBackground
  • optional compare overrides
  • optional artifact overrides
  • optional reporting overrides

args are typed as Partial<TArgs>, where TArgs is the same args type used by the story.

That means one story can describe multiple comparison states without creating multiple story exports.

When both shared config and case config provide the same capture option, the case value wins.

Playwright package setup

This package uses the consuming app's Playwright installation.

Supported peer range:

  • @playwright/test >=1.40.0 <2

If your workspace also uses Playwright component testing, keep these packages on the exact same version:

  • @playwright/test
  • @playwright/experimental-ct-react

This is a workspace package setup requirement. The package docs and workspace validation should keep these versions aligned; the Storybook helper does not need extra runtime enforcement for this.

The workspace also runs tarball compatibility smoke tests in CI so we keep validating the published package against more than one consumer dependency shape.

TypeScript resolver compatibility

These package exports rely on modern exports resolution.

Recommended tsconfig.json resolver modes:

  • moduleResolution: "bundler"
  • moduleResolution: "node16"
  • moduleResolution: "nodenext"

Older moduleResolution: "node" setups may still work at runtime but often produce TypeScript friction for subpath imports such as:

  • @compare-ui/storybook/playwright
  • @compare-ui/core/config

If TypeScript reports import-resolution errors for those subpaths, switch to one of the recommended resolver modes above.

Args support

Storybook comparison cases should support normal JSON-serializable Storybook args.

That includes:

  • strings
  • numbers
  • booleans
  • null
  • arrays
  • nested objects

Example:

args: {
  title: 'Interactive Screen 123',
  media: {
    src: '/hero.png',
    alt: 'Hero image',
  },
  products: [
    { id: '1', name: 'One' },
    { id: '2', name: 'Two' },
  ],
  selectedProductId: null,
}

The Playwright helper applies supported args through Storybook's preview channel before capture so the story is rendered in the intended state without relying on a primitive-only args contract.

For values that are not safely serializable into Storybook args, such as:

  • undefined
  • functions
  • JSX elements
  • symbols
  • bigint
  • dates
  • maps
  • sets
  • class instances
  • cyclic objects

the recommended Storybook approach is to use argTypes.mapping or another story-level indirection instead of passing those values directly through cases[].args.

Registering tests

Use registerStorybookDesignComparisonTests(...) inside a Playwright test file.

Recommended location:

  • tests/storybook-design-comparison.spec.ts

Example:

import { registerStorybookDesignComparisonTests } from '@compare-ui/storybook/playwright';

import * as exampleScreenStories from '../src/example-screen.stories';

registerStorybookDesignComparisonTests({
  storybookUrl: 'http://127.0.0.1:6007',
  storyModules: [exampleScreenStories],
});

The helper should:

  • inspect the provided story modules
  • find stories that define parameters.designComparison
  • create one Playwright test per comparison case

Comparison execution follows the shared @compare-ui/runner and @compare-ui/core pipeline.

That means image-processing behavior such as:

  • crop handling
  • explicit size normalization
  • explicit transparent-background flattening

comes from the shared comparison flow rather than from Storybook-specific image rules in this package.

Capture target

By default, Storybook comparisons capture #storybook-root.

Use targetSelector when the comparison should capture a narrower element inside the story canvas.

Example:

{
  name: 'card',
  targetSelector: '[data-testid="card-root"]',
  reference: {
    type: 'fs',
    path: './tests/fixtures/card.png',
  },
}

If targetSelector is omitted, the default capture target remains #storybook-root.

Shared config may also define a default targetSelector for all cases in the story. A case-level targetSelector overrides that shared value.

Selector capture uses an integer-snapped clip rectangle internally so fractional layout positions do not drift by a pixel between runs.

Surface background

Use surfaceBackground when one logical surface color should drive:

  • the Storybook capture surface
  • transparent-PNG flattening before compare, when flattenBackground is not already configured explicitly
  • generated grid-row or join artifact backgrounds, when those artifact instructions do not already define one

Example:

defineStorybookDesignComparison({
  surfaceBackground: [17, 17, 17, 255],
  cases: [
    {
      name: 'card',
      targetSelector: '[data-testid="card-root"]',
      reference: {
        type: 'fs',
        path: './tests/fixtures/card.png',
      },
    },
  ],
})

Case-level surfaceBackground overrides the shared story-level value.

Viewport handling

When a case defines viewport, the helper should apply that size to the browser page before taking the screenshot.

That means viewport is capture behavior, not reporting metadata only.

When the capture target is the default #storybook-root, the helper also constrains the Storybook root to that viewport size before capture.

So with the default root capture:

  • the page viewport is set to viewport
  • #storybook-root is sized to match it
  • the resulting screenshot is intended to represent a fixed viewport-sized surface instead of a content-height root

When a case uses a custom targetSelector, the page viewport is still applied, but the final screenshot dimensions follow the selected element.

Example:

{
  name: 'mobile',
  viewport: { x: 361, y: 423 },
  reference: {
    type: 'fs',
    path: './tests/fixtures/mobile.png',
  },
}

Storybook URL

registerStorybookDesignComparisonTests(...) should support this resolution order:

  1. explicit storybookUrl
  2. process.env.STORYBOOK_URL
  3. default http://127.0.0.1:6006

That means storybookUrl may be omitted when the default local Storybook port is being used.

Example with the default:

registerStorybookDesignComparisonTests({
  storyModules: [exampleScreenStories],
});

Example with an override:

registerStorybookDesignComparisonTests({
  storybookUrl: 'http://127.0.0.1:6007',
  storyModules: [exampleScreenStories],
});

Starting Storybook

Storybook should already be running before the tests execute.

Typical local workflow:

  1. start Storybook
  2. run the Playwright Storybook comparison tests

Typical CI workflow:

  1. build or serve Storybook
  2. run the Playwright Storybook comparison tests

This package should not start Storybook for you. It expects a reachable Storybook URL.

Readiness model

The helper should capture only after the story is actually ready for comparison.

Expected flow:

  1. navigate to the story iframe with normal document readiness
  2. apply viewport when provided
  3. wait for Storybook render and play completion through Storybook lifecycle events
  4. apply supported args when the case defines them
  5. resolve the capture target
  6. wait for the target to exist and be visible
  7. apply settleDelayMs when provided
  8. capture the screenshot

The readiness model should follow Storybook lifecycle/events plus explicit settling. It should not rely on networkidle.

Render health

Use strictRenderHealth when a case should fail before compare if the preview is obviously unhealthy.

When enabled, the helper fails early on signals such as:

  • page runtime errors
  • failed network requests for important preview assets
  • HTTP error responses for scripts, stylesheets, images, and fetch/XHR requests
  • broken images inside the capture target
  • zero-size capture targets

Example:

{
  name: 'strict-preview',
  strictRenderHealth: true,
  reference: {
    type: 'fs',
    path: './tests/fixtures/strict-preview.png',
  },
}

This stays opt-in so existing comparison flows are not forced into a stricter failure policy unless you want that behavior.

Story ids

The package resolves Storybook story ids from Storybook metadata.

You do not need to hardcode ids like:

  • screens-examplescreen--with-play-function

This keeps tests aligned with Storybook's generated story ids.

play functions

Stories can use normal Storybook play functions.

The generated tests should take the screenshot after the story has rendered and its play function has completed.

When a case needs extra visual settling after play, provide settleDelayMs.

Example:

{
  name: 'animated-state',
  settleDelayMs: 150,
  reference: {
    type: 'fs',
    path: './tests/fixtures/animated-state.png',
  },
}

That means the same story can define:

  • render state
  • interactive state
  • design-comparison metadata

in one place.

Shared config may also define a default settleDelayMs for all cases in the story. A case-level settleDelayMs overrides that shared value.

Focusing on one story

Generated test names include:

  • the Storybook title
  • the Storybook story name
  • the design-comparison case name

Example generated test name:

[design-comparison] Screens/ExampleScreen :: My :: full-screen

The recommended workflow is to use Playwright's normal --grep support.

Example:

pnpm test:storybook-design-comparison -- --grep "ExampleScreen"

or:

pnpm test:storybook-design-comparison -- --grep "full-screen"

The most reliable --grep values are:

  • part of the Storybook title, such as ExampleScreen
  • the Storybook story name, such as My
  • the case name, such as full-screen

Typical workflow

  1. Create or update a Storybook story.
  2. Add parameters.designComparison.
  3. Define one or more cases.
  4. Register the story module in a Storybook design-comparison test file.
  5. Run the Playwright tests.

Recipe: Storybook Story With A Local Reference PNG

Story file:

import { defineStorybookDesignComparison } from '@compare-ui/storybook';
import type { Meta, StoryObj } from '@storybook/react-vite';

import { ExampleScreen } from './example-screen';

type ExampleStoryArgs = {
  title: string;
};

const meta = {
  title: 'Screens/ExampleScreen',
  render: (args: ExampleStoryArgs) => <ExampleScreen {...args} />,
} satisfies Meta<ExampleStoryArgs>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
  args: {
    title: 'Interactive Screen 123',
  },
  parameters: {
    designComparison: defineStorybookDesignComparison<ExampleStoryArgs>({
      cases: [
        {
          name: 'default',
          reference: {
            type: 'fs',
            path: './tests/fixtures/example-screen-reference.png',
          },
        },
      ],
    }),
  },
};

Playwright spec:

import { registerStorybookDesignComparisonTests } from '@compare-ui/storybook/playwright';

import * as exampleScreenStories from '../src/example-screen.stories';

registerStorybookDesignComparisonTests({
  storyModules: [exampleScreenStories],
});

Workflow:

  1. start Storybook
  2. run the Playwright Storybook comparison spec

Recipe: Storybook Story With A Figma Reference

Bootstrap the Figma resolver once before registering the Storybook comparison tests.

Playwright spec:

import '@compare-ui/figma';
import { registerFigmaFromEnv } from '@compare-ui/figma';
import { registerStorybookDesignComparisonTests } from '@compare-ui/storybook/playwright';

import * as exampleScreenStories from '../src/example-screen.stories';

registerFigmaFromEnv();

registerStorybookDesignComparisonTests({
  storyModules: [exampleScreenStories],
});

If TypeScript does not recognize type: 'figma', keep the import '@compare-ui/figma'; line in a setup module or in this spec so the augmentation is included in the program.

Story file:

import { defineStorybookDesignComparison } from '@compare-ui/storybook';
import type { Meta, StoryObj } from '@storybook/react-vite';

import { ExampleScreen } from './example-screen';

type ExampleStoryArgs = {
  title: string;
};

const meta = {
  title: 'Screens/ExampleScreen',
  render: (args: ExampleStoryArgs) => <ExampleScreen {...args} />,
} satisfies Meta<ExampleStoryArgs>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
  args: {
    title: 'Interactive Screen 123',
  },
  parameters: {
    designComparison: defineStorybookDesignComparison<ExampleStoryArgs>({
      cases: [
        {
          name: 'figma-reference',
          reference: {
            type: 'figma',
            url: 'https://figma.com/design/abc123/Example?node-id=2020-1617',
            crop: {
              bottom: 20,
            },
          },
        },
      ],
    }),
  },
};

Workflow:

  1. start Storybook
  2. register the Figma resolver once in the test bootstrap or spec file
  3. run the Playwright Storybook comparison spec

Notes

  • This package is Storybook-specific.
  • Import defineStorybookDesignComparison(...) from @compare-ui/storybook.
  • Import registerStorybookDesignComparisonTests(...) from @compare-ui/storybook/playwright.
  • It is intended to work with Storybook stories plus Playwright-based execution.
  • Comparison execution itself is handled by lower-level packages.
  • cases[].args should follow Storybook's JSON-serializable args model.
  • supported case args are applied through Storybook's preview-channel update flow before capture.
  • image-processing policy stays in the shared core/runner flow.
  • strictRenderHealth is opt-in and is intended for teams that want preview-health failures before compare.
  • surfaceBackground is the high-level Storybook knob for keeping capture background, flattening, and composite artifact background aligned.