Project configuration
A .pixmoat.yaml file keeps a project’s comparison and capture settings in your repository, so a change to them goes through the same review as your code. This page explains how the file is read and when each setting takes effect. .pixmoat.yaml reference lists every key with its type, default and scope.
The file is optional. Without it, every build uses the project’s settings from Settings.
Build-scoped and project-global settings
Every key has one of three scopes. The reference shows the scope of each key.
Build-scoped settings
Build-scoped settings travel with the commit. When startBuild() creates a build, the Playwright client sends them with it, and pixmoat stores the result with that build. A key you leave out keeps the project’s current setting.
So a branch can try a different tolerance or turn on accessibility audits without changing other branches, and each build keeps the settings it was compared with. The build-scoped sections are default_tolerance, blocking_mode, removal_gating, diff, viewports, browsers, flakiness, performance, accessibility, console and themes.
Limit: blocking_mode, removal_gating and the gating key of performance, accessibility and console are stored with the build, but pixmoat enforces the project’s value from Settings. The file’s value doesn’t change the commit status, and doesn’t change what must be decided before Finish review. Set these in Settings.
The client sends the file only when it creates the build:
startBuild()inglobal-setup.tssends it. IfPIXMOAT_BUILD_IDis already set,startBuild()adds to that build and sends nothing.- The Playwright reporter (beta) never sends it.
Project-global settings
Project-global settings apply to the project as a whole, for every branch. Builds don’t read them from the file: a feature branch can’t change who must approve its own review. The project-global sections are retention, agent_auto_approve, review_policy, owner_map, default_reviewers, webhook and git_provider.
Change these in Settings. The pixmoat CLI can apply them from the file with pixmoat config apply on the default branch, but the CLI has no published install path yet (see CLI commands). Until then, keep project-global sections in the file only as a record, and make the change in Settings too.
Client-only settings
The capture section is read by the Playwright client before each screenshot. pixmoat ignores it. Flaky snapshots explains what each key does.
Where the file goes
Create .pixmoat.yaml in the directory you run npx playwright test from. The client also accepts .pixmoat.yml if there’s no .pixmoat.yaml.
The client reads the file from its current working directory only. It doesn’t look in the repository root or in parent directories. If your CI job runs cd e2e && npx playwright test, the file must be in the e2e directory; a file at the repository root is ignored without a warning, and the build uses the project’s settings.
Start the file with the schema line and version: 1:
# yaml-language-server: $schema=https://pixmoat.com/schemas/pixmoat-config.schema.json
version: 1
default_tolerance: 0.001
blocking_mode: blocking
The schema line gives you completion and validation in editors that use the YAML language server, such as VS Code with the YAML extension.
If the file can’t be parsed, isn’t a mapping or contains a secret such as webhook.secret, startBuild() stops with an error that starts with pixmoat: and names the file. No build is created. Fix the file and run the tests again. Keep tokens and webhook secrets in CI/CD variables and Settings, never in this file.
This example sets every section:
# yaml-language-server: $schema=https://pixmoat.com/schemas/pixmoat-config.schema.json
#
# A .pixmoat.yaml that sets every section. Keep only the keys you need; a key
# you leave out keeps the project's current setting.
#
# File location: .pixmoat.yaml in the directory you run `npx playwright test`
# from (usually the repository root).
# Install: nothing extra. @s4labs/pixmoat-playwright reads this file.
# Variables: none. Never put tokens or webhook secrets in this file.
# Command: npx playwright test
# Expected: the run creates its build without a "pixmoat:" error about
# this file, and pixmoat stores the build-scoped values with the
# build. Keys marked "enforced from Settings" are stored but
# pixmoat applies the project's Settings value. Project-global
# keys (retention, agent_auto_approve, webhook, review_policy,
# default_reviewers, owner_map, git_provider) only change when
# applied from the default branch.
version: 1 # schema version, must be 1
# ── Build-scoped settings: travel with the commit ─────────────────────
default_tolerance: 0.001 # 0.0–1.0, mismatch ratio threshold
blocking_mode: blocking # blocking | report_only (enforced from Settings)
removal_gating: advisory # advisory | blocking (enforced from Settings)
diff:
threshold: 0.1 # YIQ color sensitivity (0.0–1.0)
detect_aa: true # anti-aliasing detection
diff_color: [255, 0, 255, 255] # RGBA highlight color
dpr: 1 # device pixel ratio (1, 2 or 3)
viewports: # named viewport presets
desktop: 1280x720
mobile: 375x812
browsers: # expected browser identifiers
- chromium
flakiness:
captures: 2 # 1–5; the Playwright client still captures once
flakiness_threshold: 0.002 # inter-capture mismatch ratio
quarantine_after: 3 # flaky builds before quarantine
recovery_after: 5 # stable builds before recovery
performance: # collection also needs PIXMOAT_PERFORMANCE=true
enabled: true
gating: advisory # advisory | blocking (enforced from Settings)
regression_threshold: 0.15 # allowed variance from baseline (0.0–1.0)
thresholds:
lcp: { soft_fail: 2500, hard_fail: 4000 }
cls: { soft_fail: 0.1, hard_fail: 0.25 }
accessibility:
enabled: true
preset: wcag_aa # contrast_only | wcag_aa | wcag_aaa
gating: advisory # advisory | blocking (enforced from Settings)
rules:
exclude:
- region
console:
enabled: true
gating: advisory # advisory | blocking (enforced from Settings)
capture_levels: [error, warning]
ignore:
- "Download the React DevTools"
themes:
light:
media: "(prefers-color-scheme: light)"
dark:
media: "(prefers-color-scheme: dark)"
capture: # read by the capture client only
animations: end # end | pause | allow
wait_for_fonts: true
wait_for_images: true
image_timeout_ms: 3000
# ── Project-global settings: applied only from the default branch ─────
retention:
keep_builds: 50 # keep N most recent builds per branch
delete_after_days: 90 # delete builds older than N days
branch_rules:
- branch: "release/*"
keep_builds: 100
agent_auto_approve: intent_only # none | intent_only | all
review_policy:
min_approvals: 1
require_distinct_from_author: true
agent_may_finish: false
webhook:
url: https://ci.example.com/hook
provider: generic # generic | gitlab (label only)
on_approved: true
on_rejected: true
# The webhook secret is set in the UI only, never in this file.
Turn on accessibility auditing
The client runs an accessibility audit after each screenshot when the file has an accessibility section:
accessibility:
enabled: true
preset: wcag_aa
- An
accessibilitysection turns the audit on, even an empty one.accessibility: {}makes the client audit every snapshot. Onlyenabled: falseinside the section turns it off. - Without an
accessibilitysection, the client never audits. Turning on Accessibility in Settings doesn’t make the client audit. The toggle only decides whether pixmoat evaluates the results the client sends. - Write
enabled: true. Withaccessibility: {}, the client audits but pixmoat uses the project’s Accessibility toggle to decide whether to evaluate the results. If the toggle is off, the audits run and the results are dropped.
Accessibility checks covers presets, gating and reviewing the results.
Collect performance metrics
Performance collection has two switches, and you need both:
- Set the environment variable
PIXMOAT_PERFORMANCE=truein the Playwright process. This is the only thing that makes the client collect metrics.performance.enabled: truein the file doesn’t. - Set
performance.enabled: truein the file, or turn on Performance in Settings. Without it, pixmoat drops the metrics the client sends.
Performance and console explains the metrics and thresholds.
Capture viewports, browsers and themes
The client decides what to capture from the test code and the environment, not from the viewports, browsers and diff.dpr keys:
viewportsdoesn’t resize anything. It names presets that pixmoat stores with the build. To capture several sizes, passviewportstopixmoat.snapshot()or setPIXMOAT_VIEWPORTS.browsersdoesn’t open any browser. To capture several browsers, passbrowserstopixmoat.snapshot()or setPIXMOAT_BROWSERS.diff.dprdoesn’t change the pixel ratio. The client takes it from thedproption,PIXMOAT_DPRor the Playwright context’sdeviceScaleFactor.flakiness.capturesdoesn’t make the client capture more than once. The client takes one screenshot per snapshot.
Connect Playwright shows the options.
themes is different: the client reads it. Once the file defines themes, every snapshot() call captures the page once per theme:
themes:
light:
media: "(prefers-color-scheme: light)"
dark:
media: "(prefers-color-scheme: dark)"
The client uses dark mode for a theme whose media contains dark, and light mode otherwise. Each theme becomes its own snapshot, with ___<theme> added to the browser label, for example chromium___dark.
Console capture and page settling
Two more sections change what the client does:
consolerecords console errors during capture, but only withenabled: true. Performance and console shows the setup.capturecontrols animations and waiting for fonts and images before each screenshot. Flaky snapshots shows the options.
Precedence
When the same setting is set in several places, the most specific one wins:
- an option passed to
pixmoat.snapshot()in test code - a build-scoped key in
.pixmoat.yaml, except the blocking and gating keys listed under Build-scoped settings - the project’s setting in Settings
- pixmoat’s default
Plan limits always apply on top. For example, a retention setting can’t keep builds longer than your plan allows.
UI behavior
When the project’s latest build on the default branch included a .pixmoat.yaml file, Settings shows a banner that says the settings are managed by .pixmoat.yaml and that changes made there will be overwritten on the next pixmoat config apply. The Accessibility, Console, Review Policy, Owner Map and Git Provider sections show the same banner.
The settings stay editable. A build-scoped change you make in Settings is used by every build whose file leaves that key out. Builds whose file sets the key keep using the file’s value, except for the blocking and gating keys, which always come from Settings.
CLI commands
The pixmoat CLI has config validate, config diff and config apply commands. It has no published install path yet, so these commands are for developers who build the CLI from source. CLI reference lists their options.
Fragment. pixmoat config apply writes the project-global settings. It refuses to run unless the current branch ($CI_COMMIT_BRANCH, $GITHUB_REF_NAME or the checked-out branch) is the project’s default branch, so run it only there:
pixmoat-config:
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- pixmoat config validate
- pixmoat config apply