LogoSkills

live-preview-match

cocode 스타일 live-preview 페이지를 위한 Web↔Flutter Web 임베드 듀얼 픽셀 매칭. 같은 CoUI 컴포넌트의 Jaspr Web 렌더링과 Flutter Web 렌더링이 서로 어긋날 때 이를 감지합니다. 양쪽 모두 VM Service가 없는 프로덕션 사이트에서도 동작합니다. `coui.cocode.im/live-preview-p...

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#

  1. Navigate to the page (Playwright).
  2. Find all variant cards: [data-co="preview-card"].
  3. 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-view data-flt-renderer ready).
    • Diff A.png vs B.png with anti-aliasing tolerance and font-rendering tolerance turned up.
  4. 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:

ClassTriggerAction
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 noiseBelow thresholds, no clusteringPass

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 regression instead.
  • 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).