Skip to main content

CLI Reference

Complete reference for uiMatch CLI commands and options.

Commands Overview

uiMatch provides the following commands:

  • compare - Compare a single Figma design with implementation
  • suite - Run multiple comparisons from a JSON suite file
  • text-diff - Compare two text strings and show similarity score
  • doctor - Diagnose installation and configuration issues
  • settings - View or reset project comparison settings
  • version - Display CLI version information

compare Command

Compare a Figma design with your implementation.

Basic Syntax

Note: This assumes @uimatch/cli is already installed (globally or as a dev dependency).

npx @uimatch/cli compare \
figma=<FIGMA_REFERENCE> \
story=<URL> \
selector=<CSS_SELECTOR> \
[options]

Run Once Without Installing

If you want to try uiMatch without adding it to your project:

npx -p @uimatch/cli uimatch compare \
figma=<FIGMA_REFERENCE> \
story=<URL> \
selector=<CSS_SELECTOR> \
[options]

This explicitly tells npx which package to install (@uimatch/cli) and which binary to run (uimatch).

Required Parameters

ParameterDescriptionExample
figmaFigma file and node referenceFILE_KEY:NODE_ID or full URL
storyURL to comparehttp://localhost:3000
selectorCSS selector for target element#my-component

Common Options

Output Control

outDir=<path>            # Output directory (files not saved by default)

Size Handling

size=strict              # Sizes must match exactly (default)
size=pad # Pad smaller image with letterboxing
size=crop # Compare common area only
size=scale # Scale implementation to Figma size

Quality Gates

profile=component/strict # Pixel-perfect (pixelDiffRatio: 0.01, deltaE: 3.0)
profile=component/dev # Development (pixelDiffRatio: 0.08, deltaE: 5.0)
profile=page-vs-component # Padded comparison (pixelDiffRatio: 0.12)
profile=lenient # Prototyping (pixelDiffRatio: 0.15, deltaE: 8.0)

# Fine-grained thresholds (overrides profile)
areaGapCritical=<0..1> # Critical area gap threshold (default: 0.15)
areaGapWarning=<0..1> # Warning area gap threshold (default: 0.05)

See Quality Gate Profiles for detailed threshold settings.

Browser Options

viewport=<WxH>           # Custom viewport size (e.g., "1920x1080")

Use environment variable UIMATCH_HEADLESS=false to show browser window during execution.

Text Matching (Experimental)

Enable text content comparison alongside pixel-based comparison to detect copy differences, typos, and missing text.

text=true                          # Enable text matching (default: false)
textMode=self|descendants # Text collection scope (default: self)
# self: Element's own text only
# descendants: Include child elements
textNormalize=none|nfkc|nfkc_ws # Normalization mode (default: nfkc_ws)
# none: No normalization
# nfkc: Unicode NFKC normalization
# nfkc_ws: NFKC + whitespace collapsing
textCase=sensitive|insensitive # Case sensitivity (default: insensitive)
textMatch=exact|contains|ratio # Matching mode (default: ratio)
# exact: Exact match required
# contains: Substring matching
# ratio: Similarity scoring
textMinRatio=0..1 # Minimum similarity threshold (default: 0.98)
# Only applies when textMatch=ratio
textGate=true|false # Use text match for quality gate (default: false)
--textGate # Alternative flag format (same as textGate=true)
# When enabled, CI passes/fails based on text match
# instead of visual differences
# Visual differences are still reported

Note: Text matching results appear in the textMatch section of report.json when outDir is specified.

See Text Matching for detailed information on normalization, similarity scoring, and use cases.

Examples

Basic Comparison

npx @uimatch/cli compare \
figma=abc123:1-2 \
story=http://localhost:3000 \
selector="#button"

With Strict Quality Profile

npx @uimatch/cli compare \
figma=https://figma.com/file/abc123?node-id=1-2 \
story=http://localhost:6006/iframe.html?id=button--primary \
selector=".storybook-button" \
profile=component/strict

Mobile Viewport

npx @uimatch/cli compare \
figma=abc123:1-2 \
story=http://localhost:3000 \
selector="#mobile-nav" \
viewport=375x667

With Text Matching

Compare both visual appearance and text content to detect typos and copy differences:

npx @uimatch/cli compare \
figma=abc123:1-2 \
story=http://localhost:6006/iframe.html?id=accordion--default \
selector="[data-testid='accordion']" \
text=true \
textMode=descendants \
textMinRatio=0.95 \
outDir=./comparison-results

Results include a textMatch section in report.json:

{
"textMatch": {
"enabled": true,
"ratio": 0.42,
"equal": false,
"details": {
"missing": ["accordion", "vertically", "stacked"],
"extra": ["is", "it", "accessible"]
}
}
}

Text-Heavy Pages (Text Gate Mode)

For text-heavy pages like Terms of Service or Privacy Policy, you can use text gate mode to pass CI based on text accuracy while still monitoring visual differences:

npx @uimatch/cli compare \
figma=abc123:1-2 \
story=http://localhost:3000/terms \
selector="#terms-content" \
text=true \
--textGate \
profile=page/text-doc \
outDir=./comparison-results

In this mode:

  • CI passes if text content matches (exit code 0)
  • Visual differences are reported but don't fail the build
  • Useful when content accuracy is more important than pixel-perfect layout

suite Command

Run multiple comparisons from a JSON configuration file.

Basic Syntax

npx @uimatch/cli suite path=<suite-file.json> [options]

Suite File Format

{
"name": "Component Library Tests",
"defaults": {
"profile": "component/dev"
},
"items": [
{
"name": "Button Primary",
"figma": "abc123:1-2",
"story": "http://localhost:3000/components/button",
"selector": "#button-primary"
},
{
"name": "Navigation Header",
"figma": "abc123:3-4",
"story": "http://localhost:3000/",
"selector": "header.nav"
}
]
}

Options

path=<suite.json>        # Path to suite file
outDir=<path> # Output directory (default: .uimatch-suite)
concurrency=<number> # Run comparisons in parallel (default: 4)

Example

npx @uimatch/cli suite path=tests/visual-regression.json concurrency=3

text-diff Command

Compare two text strings and show similarity score with classification.

Basic Syntax

npx @uimatch/cli text-diff <expected> <actual> [options]

Positional Arguments

ArgumentDescriptionExample
expectedThe expected text (e.g., from Figma design)"Sign in"
actualThe actual text (e.g., from implementation)"SIGN IN"

Options

--case-sensitive         # Perform case-sensitive comparison (default: case-insensitive)
--threshold=<number> # Similarity threshold (0-1, default: 0.9)

Note: Options must use = syntax (e.g., --threshold=0.8). Space-separated format (--threshold 0.8) is not supported.

Output Format

Returns a JSON object with the following fields:

{
kind: 'exact-match' | 'whitespace-or-case-only' | 'normalized-match' | 'mismatch',
similarity: number, // 0-1 range
expected: string, // Original expected text
actual: string, // Original actual text
normalizedExpected: string, // Normalized expected text
normalizedActual: string, // Normalized actual text
equalRaw: boolean, // True if raw texts are identical
equalNormalized: boolean // True if normalized texts are identical
}

Classification Types

KindDescriptionExample
exact-matchTexts are identical without any modification"Login" vs "Login"
whitespace-or-case-onlyTexts differ only in whitespace, case, or NFKC normalization"Sign in" vs "SIGN IN"
normalized-matchSimilarity is at or above the selected threshold"Save changes now" vs "Save changes later" at 0.6
mismatchSimilarity is below the selected thresholdThe same pair at 0.7

Text Normalization

The comparison applies the following normalization steps:

  1. NFKC Unicode normalization - Converts full-width characters to half-width
  2. Whitespace collapsing - Collapses consecutive whitespace into single space
  3. Trim - Removes leading/trailing whitespace
  4. Case normalization - Converts to lowercase (unless --case-sensitive is used)

Examples

Basic Comparison

npx @uimatch/cli text-diff "Sign in" "SIGN  IN"

Output:

{
"kind": "whitespace-or-case-only",
"similarity": 1.0,
"equalRaw": false,
"equalNormalized": true
}

Case-Sensitive Comparison

npx @uimatch/cli text-diff "Submit" "submit" --case-sensitive

Output:

{
"kind": "normalized-match",
"equalRaw": false,
"equalNormalized": false
}

The similarity remains above the default threshold, so the result is a normalized-match even though case normalization is disabled.

With Custom Threshold

npx @uimatch/cli text-diff "Save changes now" "Save changes later" --threshold=0.6

Output:

{
"kind": "normalized-match",
"equalRaw": false,
"equalNormalized": false
}

This pair is above 0.6 and below 0.7; selecting 0.7 therefore classifies the same input as a mismatch.

Full-Width Character Handling

npx @uimatch/cli text-diff "Button123" "Button123"

Output:

{
"kind": "whitespace-or-case-only",
"similarity": 1.0,
"equalRaw": false,
"equalNormalized": true
}

Use Cases

  • Text label validation - Compare Figma text labels with implementation
  • Copy review - Compare intentional wording changes against an explicit threshold
  • Typography debugging - Identify subtle text differences (case, whitespace, unicode)
  • Component testing - Validate text content in UI components

@uimatch/core is an internal package and is not a supported public API. Use this command when text comparison is required in scripts or CI.

settings Command

View the effective project configuration or remove .uimatchrc.json and return to defaults.

npx @uimatch/cli settings get
npx @uimatch/cli settings reset

Running settings without an action is equivalent to settings get. The command does not provide a set action; edit .uimatchrc.json and inspect the effective result with settings get.

version Command

Display the current version of the CLI.

Basic Syntax

npx @uimatch/cli version

Alternatives

You can also use standard flags:

npx @uimatch/cli --version
# or
npx @uimatch/cli -v

Environment Variables

Set these in .env or your environment:

FIGMA_ACCESS_TOKEN=your_token_here       # Required for Figma API access
UIMATCH_LOG_LEVEL=debug|info|warn|error|silent # Logging verbosity (default: info)
UIMATCH_HEADLESS=true|false # Playwright headless mode (default: true)
# Set to 'false' to show browser window
# Applies to compare/suite/doctor commands
UIMATCH_CHROMIUM_SANDBOX=true|false # Chromium sandbox (default: true)
# Use false only when the runtime cannot support it
UIMATCH_SELECTOR_PLUGIN_TIMEOUT_MS=30000 # Plugin deadline (1..2147483647 ms)

Exit Codes

  • 0 - All comparisons passed
  • 1 - One or more comparisons failed
  • 2 - Invalid arguments or configuration error

Exit code 2 means the invocation itself has to change — an unknown command, an invalid argument, a malformed suite file, a missing environment variable. Anything the comparison detected exits with 1.

Failures that carry a stable code print it on stderr as ❌ Error [<code>]: <message>:

CodeExitMeaning
UIMATCH_CONFIG_INVALID_FIGMA_REF2figma is not current, fileKey:nodeId, or a URL
UIMATCH_CONFIG_MISSING_FIGMA_TOKEN2FIGMA_ACCESS_TOKEN is required but not set
UIMATCH_SELECTOR_NOT_FOUND1The selector was not found or never became visible
UIMATCH_IMAGE_SIZE_MISMATCH1Image dimensions differ while size=strict

Programmatic callers can match the same codes:

import { uiMatchCompare, UiMatchError } from '@uimatch/cli';

try {
await uiMatchCompare({ figma, story, selector });
} catch (error) {
if (error instanceof UiMatchError) {
console.error(error.code, error.category);
}
}

Advanced Usage

Content Basis

Control which area to use for calculating pixel difference ratio:

contentBasis=union          # Union of both content areas (default)
contentBasis=intersection # Intersection of both areas (recommended for pad mode)
contentBasis=figma # Use Figma's content area only
contentBasis=impl # Use implementation's content area only

Best Practice: Use intersection with size=pad to exclude letterboxing from metrics.

Custom Anchor Plugins

Use custom selector resolution plugins:

selectorsPlugin=@my-company/custom-anchor-plugin

Selector anchor files and the source paths they reference must stay within the project root. uiMatch uses an explicit projectRoot=<path> when provided, otherwise the nearest Git root, and finally the current working directory. Paths are resolved through symlinks before this boundary is checked.

selectors=.uimatch/anchors.json projectRoot=/path/to/project

See Plugins for details on creating custom plugins.

Quality Gate Profiles

uiMatch uses quality gate profiles to manage thresholds instead of individual CLI flags.

ProfileUse CasepixelDiffRatiodeltaEDescription
component/strictDesign system components0.01 (1%)3.0Pixel-perfect comparison
component/devDevelopment workflow0.08 (8%)5.0Relaxed for iteration
page-vs-componentPadded comparisons0.12 (12%)5.0Accounts for letterboxing
page/text-docText-heavy pages0.20 (20%)6.0Terms, privacy, documentation
lenientPrototyping0.15 (15%)8.0Very relaxed thresholds
customCustom settings--Uses .uimatchrc.json

Using Profiles

# Pixel-perfect comparison
npx @uimatch/cli compare figma=... story=... selector=... profile=component/strict

# Development workflow
npx @uimatch/cli compare figma=... story=... selector=... profile=component/dev

# Text-heavy pages (Terms, Privacy Policy, etc.)
npx @uimatch/cli compare figma=... story=... selector=... profile=page/text-doc

Custom Configuration

For fine-grained control, create .uimatchrc.json:

{
"comparison": {
"colorDeltaEThreshold": 3.0,
"acceptancePixelDiffRatio": 0.01,
"acceptanceColorDeltaE": 3.0
}
}

colorDeltaEThreshold controls StyleDiff significance and SFS normalization. acceptanceColorDeltaE controls the aggregate color quality gate. Profiles provide a single deltaE value that overrides both stages for that run.

Tips

  1. Start with lenient profile (profile=lenient) and tighten as needed
  2. Use UIMATCH_HEADLESS=false during development to see browser window
  3. Name your comparisons for easier debugging in CI logs
  4. Group related comparisons in suite files for organization

See Also