Configuration
Configuration lives in merrykat.config.ts — or .js, .json, or a merrykat key in package.json. The CLI reads and validates it in your CI job.
Everything, with its default
export default {
viewports: [
// `accessibility: true` on the first viewport and nowhere else is the explicit
// spelling of the default: unstated everywhere means the first one gets the pass.
{ name: 'desktop', width: 1280, height: 800, accessibility: true },
{ name: 'mobile', width: 420, height: 700 },
],
include: ['**'],
exclude: [],
delay: 250,
play: true,
playTimeout: 5000,
failOn: ['error', 'blank', 'likely_bug'],
baselineBranch: null,
ai: { enabled: true, maxExamined: 10, codeContext: true, depth: 'standard' },
accessibility: { enabled: true },
compare: { fingerprints: 'auto', audit: 0.02, alwaysCompare: [] },
};Every field is optional. If you don’t have a Merrykat configuration, you get the configuration above.
Viewports
A viewport is a name, a width and a height, plus an optional device pixel ratio.
A viewport can also be a media feature under test rather than a size. Three are supported, each mapping onto the browser’s own emulation: colorScheme (prefers-color-scheme), contrast (prefers-contrast) and forcedColors (forced-colors). Anything you leave out keeps the run’s usual default.
viewports: [
{ name: 'desktop', width: 1280, height: 800 },
{ name: 'mobile', width: 420, height: 700, dpr: 2 },
// Same size as 'desktop', rendered under prefers-color-scheme: dark
{ name: 'desktop-dark', width: 1280, height: 800, media: { colorScheme: 'dark' } },
// prefers-contrast: more, and forced-colors: active
{ name: 'high-contrast', width: 1280, height: 800, media: { contrast: 'more' } },
{ name: 'forced-colors', width: 1280, height: 800, media: { forcedColors: 'active' } },
],prefers-reduced-motion is deliberately not on that list: it is pinned to reduce for every capture as a determinism control, alongside disabled animations and transitions, a hidden caret and hidden scrollbars.
A viewport can also carry accessibility: true, which changes where the axe pass runs — see Accessibility testing.
Choosing stories
| Option | Default | Meaning |
|---|---|---|
include | ['**'] | Story id / title globs to compare. |
exclude | [] | Story id / title globs to leave out, applied after include. |
delay | 250 | Settle time, in ms, before a story that differs from the baseline is re-shot. |
play | true | Run each story’s play() function before capturing. |
playTimeout | 5000 | Ceiling on one play(), in ms. |
baselineBranch | null | Overrides the pull request’s base branch when resolving a baseline. For repositories whose stable branch is not the branch pull requests target. |
Failure policy
failOn decides what turns the check run red. Everything not listed is reported and left neutral. A visual difference is usually the point of the pull request rather than a fault in it.
| Value | Fails when |
|---|---|
error | A story newly throws — Storybook’s own error overlay, not a console warning. On by default. |
blank | A story newly renders nothing. On by default. |
likely_bug | An examined difference comes back as a high-severity likely bug. On by default. |
any_diff | Any visual difference at all. For repositories that want every change acknowledged. |
new_a11y_violation | The pull request introduces an accessibility violation. Off by default. |
error and blank both mean newly broken. If the baseline was already broken or blank, Merrykat reports it but doesn’t fail the check.
The AI layer
| Option | Default | Meaning |
|---|---|---|
ai.enabled | true | Off still produces the full diff report, one representative story per change class instead of a chosen shortlist. |
ai.maxExamined | 10 | Hard ceiling on stories sent to the vision stage. Differences and newly added stories draw on this one budget, differences first. |
ai.codeContext | true | Send the pull request’s changed-file list, and the hunks of style and token files, so Merrykat can better explain results. |
ai.depth | 'standard' | 'deep' spends more of the model’s attention per examined story. |
The code context is your source, so it is handled as such: computed locally by the CLI, encrypted with a key unique to your installation, stored encrypted and only for a short time, and deleted as soon as it has been used. Using --code-context=false on the CLI turns it off for a single run without editing the file.
Per-story parameters
You can configure Merrykat for individual stories through Storybook parameters.
export const Slow = {
parameters: { merrykat: { delay: 900, viewports: ['desktop'] } },
};
export const WaitsForSomething = {
parameters: { merrykat: { waitFor: '[data-loaded="true"]' } },
};
export const NotWorthChecking = {
parameters: { merrykat: { disable: true } },
};| Parameter | Meaning |
|---|---|
disable | Skip this story entirely. |
delay | Settle time for this story, in ms, up to 10000. |
viewports | Render this story only at the named viewports. |
waitFor | Wait for a selector to appear before capturing. |
play | (Boolean) Override the run’s play() setting for this story. |
accessibility | false switches the axe pass off for this story. A story can only ever turn it off, never on. |
Command line
| Flag | Meaning |
|---|---|
--dir <dir> | Storybook build directory. |
--project <key> | Project key, for repositories with several Storybooks. |
--config <file> | Path to the configuration file. |
--api <url> | API base URL. MERRYKAT_API_URL overrides it. |
--sha / --branch / --ref | Override the git context detected from CI. |
--code-context [bool] | Send the pull request’s diff as context for this run. |
--accessibility [bool] | Run the axe pass for this run. |
--quiet | Only print warnings and errors. |