All skills
simota avatar

/vitrine

@c805268
by shingo imotasimota/agent-skills85 stars
15

Authoring Storybook stories, component catalogs, and Visual Regression integration (CSF 3.0/Factories, Storybook 10 ESM-only, React Cosmos). Use when building a component catalog.

Use this Skill: https://skilld.dev/gh/simota/agent-skills/vitrine

This session only. Nothing lands on disk.

referencevisual-regression.md

≈2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Visual Regression Testing

Strategies and tooling for visual regression testing with Storybook and React Cosmos.


Strategy Comparison

Tool Cost CI Integration Storybook Support Cosmos Support Best For
Chromatic Paid (free tier) Excellent Native ❌ Design systems
Playwright Free Manual setup Via test runner Via export Budget-conscious
Percy Paid Good Via addon ❌ Enterprise
Loki Free Manual setup Native ❌ Local testing
Lost Pixel Free (OSS) GitHub Action Via URL Via URL Open source projects

Chromatic Setup

Installation & Configuration

// .storybook/main.ts
export default {
  addons: ['@chromatic-com/storybook'],
};
// package.json
{
  "scripts": {
    "chromatic": "chromatic --project-token=$CHROMATIC_PROJECT_TOKEN",
    "chromatic:ci": "chromatic --exit-zero-on-changes --auto-accept-changes main"
  }
}

GitHub Actions Workflow

# .github/workflows/chromatic.yml
name: Chromatic
on: push

jobs:
  chromatic:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
      - run: npm ci
      - uses: chromaui/action@latest
        with:
          projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
          exitZeroOnChanges: true

Chromatic Best Practices

// Skip animations to avoid flaky tests
export const AnimatedButton: Story = {
  parameters: {
    chromatic: {
      disableSnapshot: false,
      delay: 300, // Wait for animation to settle
      diffThreshold: 0.063, // Tolerance for sub-pixel rendering
    },
  },
};

// Capture multiple viewports
export const Responsive: Story = {
  parameters: {
    chromatic: {
      viewports: [320, 768, 1200],
    },
  },
};

// Skip flaky stories
export const AnimatedLoader: Story = {
  parameters: {
    chromatic: { disableSnapshot: true },
  },
};

Playwright Visual Testing

Configuration

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './visual-tests',
  snapshotDir: './visual-tests/__snapshots__',
  expect: {
    toHaveScreenshot: {
      maxDiffPixels: 100,
      threshold: 0.2,
    },
  },
  use: {
    baseURL: 'http://localhost:6006', // Storybook URL
  },
  webServer: {
    command: 'npm run storybook -- --ci',
    port: 6006,
    reuseExistingServer: !process.env.CI,
  },
});

Visual Test Examples

// visual-tests/button.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Button visual tests', () => {
  test('default state', async ({ page }) => {
    await page.goto('/iframe.html?id=components-button--default');
    await expect(page.locator('.sb-show-main')).toHaveScreenshot('button-default.png');
  });

  test('all variants', async ({ page }) => {
    await page.goto('/iframe.html?id=components-button--all-variants');
    await expect(page.locator('.sb-show-main')).toHaveScreenshot('button-variants.png');
  });

  test('hover state', async ({ page }) => {
    await page.goto('/iframe.html?id=components-button--default');
    await page.locator('button').hover();
    await expect(page.locator('.sb-show-main')).toHaveScreenshot('button-hover.png');
  });

  test('dark mode', async ({ page }) => {
    await page.goto('/iframe.html?id=components-button--dark-mode');
    await expect(page.locator('.sb-show-main')).toHaveScreenshot('button-dark.png');
  });
});

Responsive Visual Testing

// visual-tests/responsive.spec.ts
import { test, expect, devices } from '@playwright/test';

const viewports = [
  { name: 'mobile', ...devices['iPhone 13'] },
  { name: 'tablet', ...devices['iPad'] },
  { name: 'desktop', viewport: { width: 1280, height: 720 } },
];

for (const device of viewports) {
  test(`Card component - ${device.name}`, async ({ browser }) => {
    const context = await browser.newContext(device);
    const page = await context.newPage();
    await page.goto('/iframe.html?id=components-card--default');
    await expect(page.locator('.sb-show-main')).toHaveScreenshot(
      `card-${device.name}.png`
    );
    await context.close();
  });
}

Storybook Test Runner

Configuration

// .storybook/test-runner.ts
import type { TestRunnerConfig } from '@storybook/test-runner';
import { toMatchImageSnapshot } from 'jest-image-snapshot';

const config: TestRunnerConfig = {
  setup() {
    expect.extend({ toMatchImageSnapshot });
  },
  async postVisit(page, context) {
    const image = await page.screenshot();
    expect(image).toMatchImageSnapshot({
      customSnapshotsDir: `__snapshots__/${context.id}`,
      customSnapshotIdentifier: context.name,
      failureThreshold: 0.01,
      failureThresholdType: 'percent',
    });
  },
};

export default config;
// package.json
{
  "scripts": {
    "test-storybook": "test-storybook",
    "test-storybook:ci": "test-storybook --coverage --browsers chromium",
    "test-storybook:visual": "test-storybook --tags='visual-test'"
  }
}

Tags for Visual Testing

// Include in visual regression
const meta = {
  component: Button,
  tags: ['autodocs', 'visual-test'],
} satisfies Meta<typeof Button>;

// Exclude specific story (animation causes flaky tests)
export const Animated: Story = {
  tags: ['!visual-test'],
  args: { animated: true },
};

// Run only visual tests:
// test-storybook --tags="visual-test"

Visual Test Workflow

1. Baseline Capture
   └─ First run creates __snapshots__/

2. Development
   └─ Run local visual tests to catch regressions

3. PR Check
   └─ CI compares against baseline
   └─ Fails if unexpected visual changes

4. Review
   └─ Visual diffs in PR (Chromatic/Percy dashboard)
   └─ Accept or reject changes

5. Update Baseline
   └─ Merge updates snapshots for main branch

CI/CD Integration

GitHub Actions (Playwright Visual Tests)

# .github/workflows/visual-tests.yml
name: Visual Regression
on: pull_request

jobs:
  visual:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npx playwright install chromium
      - run: npm run build-storybook
      - run: npx http-server storybook-static -p 6006 &
      - run: npx wait-on http://localhost:6006
      - run: npx playwright test visual-tests/
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: visual-diffs
          path: visual-tests/__snapshots__/*-diff.png

Lost Pixel (Free OSS Alternative)

// lostpixel.config.ts
import { CustomProjectConfig } from 'lost-pixel';

export const config: CustomProjectConfig = {
  storybookShots: {
    storybookUrl: './storybook-static',
  },
  generateOnly: true,
  failOnDifference: true,
};

Storybook Test Widget (Storybook 9)

Storybook 9 adds a built-in Test Widget that runs all story tests in watch mode.

Setup

// .storybook/main.ts (Storybook 9)
export default {
  addons: ['@storybook/experimental-addon-test'],
};

CI Workflow

# .github/workflows/storybook-tests.yml
name: Storybook Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - run: npm run build-storybook -- --quiet
      - name: Serve and test
        run: |
          npx concurrently -k -s first \
            "npx http-server storybook-static --port 6006 --silent" \
            "npx wait-on tcp:6006 && npm run test-storybook"

Source: SKILL.md on GitHub

No alerts13d3 checks · Risk SAFE
  • Gen Agent Trust Hub13d

    The skill provides comprehensive instructions for component catalog management using Storybook, React Cosmos, and other industry-standard tools. No security issues were detected, and the skill follows safe development practices.

  • Socket13d

    No alerts

  • Snyk13d

    Risk: LOW · No issues

Signed by skilld at c805268. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated 2 weeks ago

README badge

README badge for simota/agent-skills/vitrine