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​