@design-parity/page-backdrop
v0.1.47
Published
Opt-in Figma page backdrops: import key pages/screens as background images, place each component instance on them, link every placement back to the code component, and view the page with the candidate renders overlaid on top.
Readme
@design-parity/page-backdrop
Import the key pages of a Figma file as backdrops, place every component instance on them, link each one back to the code component that implements it, and — optionally — show the code renders on top of the design.
Where the rest of design-parity asks "does this Button match its Figma node?", this asks the whole-screen question: here is the Now Playing screen — which of its parts do we implement, where, and do our renders sit right on top of the design?

Opt-in, off by default. Nothing here runs unless a repo commits a
design-pages.jsonwith"enabled": trueand someone invokes thedesign-parity-pagesCLI. This package is not referenced by@design-parity/action, so the PR bot behaves identically whether or not a repo has adopted it. See Turning it on.
What you get
A committed pages.json plus one PNG per page, and a self-contained HTML viewer
built from them:
- Backdrop — the design page, exactly as Figma renders it.
- Hotspots — one rectangle per component instance, colour-coded by how it
was linked: green Code Connect, blue
design-map.json, amber name-match, and a dashed red outline for unlinked — a part of the screen with no code behind it, which is usually the most interesting thing on the page. - Links — every linked placement carries its code handle, and can carry a URL you supply (source file, preview, wherever you want the click to land).
- Overlay — the code's own render laid over its placement, with an opacity
slider and a
differenceblend. Starts off.
Turning it on
Commit a design-pages.json at the repo root:
{
"$schema": "./node_modules/@design-parity/page-backdrop/schema/page-backdrop-config.schema.json",
"enabled": true, // ← required; anything else leaves the feature off
"fileKey": "AbCdEf123456", // the segment after /design/ in the Figma URL
"pages": [
{ "nodeId": "1:2", "id": "now-playing" },
{ "nodeId": "1:8" } // id defaults to a slug of the frame's name
],
"outDir": "design/pages"
}Both halves of the gate are deliberate. No file means the feature has never
been heard of and nothing happens. A file without "enabled": true means
the configuration — which file, which frames, where the output goes — can be
landed and reviewed before anyone switches the feature on. Reviewing the
config never silently turns it on.
Optional keys: scale (PNG export scale, default 2), nested (record
instances inside other instances, default false), and overlay
({ enabled, opacity, blend }, defaulting to off / 0.5 / normal).
Using it
# Is it on for this repo? (safe to run anywhere — says so when it isn't)
design-parity-pages status
# Import the key pages. Needs FIGMA_TOKEN or FIGMA_OAUTH_TOKEN.
design-parity-pages import \
--code-connect figma.connect.json \
--design-map design-map.json
# Build the viewer, with renders laid over the components you have them for.
design-parity-pages view \
--render ui/Player.kt#PlayButton=build/previews/PlayButton.png \
--source-url ui/Player.kt#PlayButton=https://github.com/me/app/blob/main/ui/Player.kt \
--out design/pages/pages.htmlimport is the only step that touches the network, and it is meant to be a
deliberate "refresh the backdrops" commit — not something a check does on every
push. Everything downstream reads the committed manifest.
How placements are linked
Same precedence as the per-component resolver, applied per instance: Code
Connect → design-map.json → name convention → unlinked. The page-specific
wrinkle is which ref gets looked up. A page holds instances, but a link is
attached to a component — usually the component set rather than one
variant of it. So each placement is tried against three refs, widest first:
- the component set (
Button) — where Code Connect normally lives - the main component (
Button/Primary) — a per-variant connection - the instance itself — only a hand-written
design-map.jsonwould point at one, but if a repo did, it is honoured
A name match is always reported as convention and an ambiguous one is left
unlinked with a warning rather than guessed at.
Reading the overlay

A render is pinned to its placement's top-left corner and scaled to the placement's width, keeping its own aspect ratio. It deliberately does not stretch to fill the box — a component that renders taller than its design slot is a real finding, and stretching would hide exactly that drift. An overflowing overlay means the heights disagree.
Switch the blend to difference and matching pixels go black, so only the drift
lights up:

In the shots above, three things are visible and all three are true: the play
button renders taller than its design slot, the Up-next card sits a few pixels
low, and one code component (FilterChip) legitimately backs three different
chip instances, so its single render appears three times.
Coordinates
Placements are stored in frame-local design units (Figma dp), not image
pixels, with the frame's top-left as the origin. The viewer positions by ratio,
so re-exporting the backdrop at a different scale doesn't invalidate the
manifest and there is no density arithmetic anywhere.
Trying it without Figma
fixtures/page-backdrop/ is a real import of the screen shown above, with the
three renders that go over it — enough to build the viewer with no credentials
and no renderer. packages/page-backdrop/test/fixture.test.ts builds it on
every CI run.
