Live Preview Match#
한마디로#
같은 디자인을 두 가지 기술(Jaspr Web과 Flutter Web)로 각각 그려 놓고, 두 그림이 정말 똑같이 보이는지 자동으로 비교해 주는 도구입니다. 마치 "틀린 그림 찾기" 게임을 컴퓨터가 대신 해 주는 것과 같습니다. 같은 버튼인데 한쪽은 색이 다르거나 글자가 살짝 어긋나 있으면 자동으로 짚어 줍니다. 사람 눈으로 일일이 대조하지 않아도 되니 시간을 크게 아낄 수 있습니다.
무엇을·언제#
- 무엇을 해 주나요: 한 페이지에 나란히 놓인 두 개의 미리보기 화면(Web 쪽과 Flutter 쪽)을 화면 캡처해서, 색·글자·간격 같은 차이를 찾아내고 카드별로 합격/문제 판정을 내려 줍니다.
- 언제 작동하나요:
coui.cocode.im/live-preview-poc같은, 같은 디자인을 두 방식으로 동시에 보여 주는 페이지를 점검할 때 켜집니다. - 어디서 되나요: 개발용 특수 도구 없이도 실제로 배포된(운영 중인) 사이트에서 그대로 동작합니다.
- 어떤 점이 좋나요: 차이가 발견되면 그 종류(글자체 문제·색 문제·배치 문제)를 구분해 주고, 원본·비교본·차이 표시를 한눈에 보여 주는 이미지를 만들어 줍니다.
핵심 용어#
| 용어 | 쉬운 설명 |
|---|---|
| Jaspr Web / Flutter Web | 같은 화면을 그려 내는 서로 다른 두 가지 기술. 이 둘이 똑같이 보이는지가 비교 대상입니다. |
| CoUI | 두 기술이 공통으로 쓰는 디자인 묶음(버튼·카드 같은 화면 부품 모음). |
| pixel diff (픽셀 비교) | 두 그림을 점 단위로 맞대어 보며 다른 곳을 찾아내는 작업. |
| tolerance (허용 오차) | "이 정도 미세한 차이는 같은 것으로 본다"고 정해 두는 기준. 엄격하게(strict)·보통(default)·느슨하게(loose) 조절할 수 있습니다. |
| screenshot (스크린샷) | 화면을 그대로 사진처럼 찍어 둔 이미지. 비교의 재료가 됩니다. |
| verdict (판정) | 차이를 분석한 뒤 내리는 결론(합격인지, 문제가 있는지). |
| regression (회귀 점검) | 시점이 다른 두 캡처를 비교해 "예전과 달라졌는지" 확인하는 것. 이 도구의 비교와는 다른 작업입니다. |
| selector (선택자) | 페이지에서 비교할 두 패널이 어디인지 콕 집어 주는 표식. |
CoUI's promise is "Flutter and Jaspr Web render the same design". The live-preview-poc page already shows them side-by-side; this skill turns that page into an automated proof.
Why this needs a dedicated skill#
The native pixel-loop (Marionette + Figma over VM Service) doesn't help because both sides run in a browser. The Jaspr audit (/jaspr-web audit) doesn't help because it counts elements but doesn't compare two renders to each other. This skill is the missing piece:
same-page, two-panel pixel diff with awareness of Flutter Web's canvas quirks.
Page Topology Assumption#
┌─ Variant Card ─────────────────────────────────────────────┐
│ ┌──── Web panel ────┐ ┌──── Flutter panel ────┐ │
│ │ Jaspr-rendered │ │ Flutter Web canvas │ │
│ │ < button class= │ │ < flutter-view > │ │
│ │ " co-button │ │ ... │ │
│ │ co-primary " > │ │ < /flutter-view > │ │
│ │ Primary │ │ │ │
│ │ < /button > │ │ │ │
│ └───────────────────┘ └───────────────────────┘ │
└────────────────────────────────────────────────────────────┘
↑ ↑
clip rect derived from clip rect derived from
[data-co= " preview-web " ] [data-co= " preview-flutter " ]
The skill assumes each variant card has two clearly marked panels reachable via two stable container selectors. cocode's live-preview emits exactly this structure based on the page's HTML — see
commands/match.md for the assumed catalogue.
How matching works#
- Navigate to the page (Playwright).
- Find all variant cards:
[data-co="preview-card"]. -
For each card:
getBoundingClientRect()for the Web panel container → clip screenshot →A.png.getBoundingClientRect()for the Flutter panel container → clip screenshot →B.png.- Wait for the Flutter panel to finish loading (no "Loading Flutter..." text, canvas mounted, first frame painted via
flutter-viewdata-flt-rendererready). - Diff
A.pngvsB.pngwith anti-aliasing tolerance and font-rendering tolerance turned up.
- Emit per-variant verdict + 3-up image (
A | B | diff).
Tolerance presets#
Flutter Web font-rendering and Jaspr DOM font-rendering will never be byte-identical. Defaults are tuned for "same design language" rather than "same bytes":
default:
pixel: 2
color_delta_e: 3.0 # CIEDE2000
ignore_aa: true # ignore anti-alias edge differences
mask_outside_intersection: true # only compare the overlapping rect
strict:
pixel: 1
color_delta_e: 2.0
ignore_aa: false
loose:
pixel: 4
color_delta_e: 5.0
ignore_aa: true
Pass with --tolerance-preset=strict (default default).
What counts as a real divergence#
The skill classifies diffs into:
| Class | Trigger | Action |
|---|---|---|
| typography | Glyph centroids shift > 1px or font weight differs | Open issue; this is usually a CSS variable miss on one side |
| color | color_delta_e > threshold over > 5% of the panel |
Open issue; usually a theme token diff |
| layout | Element bounding-box diff > pixel threshold | Open issue; usually a spacing scale diff |
| rendering noise | Below thresholds, no clustering | Pass |
The classifier writes a short LLM-style verdict per card to verdict.md.
Production readiness#
This skill works on the deployed production site. It does not require:
- VM Service.
- Source maps.
- Flutter Semantics being enabled.
It only requires that the Jaspr page marks the two panels with stable selectors. The default selectors are listed in
commands/match.md and can be overridden via --selectors=<yaml>.
When not to use#
- A single render (Web-only or Flutter-only) — use
/jaspr-web regressioninstead. - Native Flutter on iOS/Android — use
cc-pixel-loop. - Cross-screenshot comparison across two different runs/timestamps — that's regression, not match.
Cross-references#
- Sibling skill:
cc-jaspr-web:jaspr-web - Native counterpart:
cc-pixel-loop:pixel-loop - Article reference: Pixel-Perfect Flutter UIs with Figma, Maestro, and Dart MCPs — Very Good Ventures, May 2026 (informs tolerance defaults).