dot-skills Design-to-React Conversion Best Practices
The reverse-engineering pipeline that converts Sketch files into pixel-perfect React + CSS, with regression-safe iteration as the load-bearing constraint. The skill is organized around the cascade effect of design-to-code conversion: a wrong call in stage N corrupts every output from stage N+1 onward, so categories are ordered by how much downstream they own.
The user's primary requirement — "each improvement doesn't cause regressions" — is enforceable only if the iteration loop, the layer tree, and the layout solver are correct before you start polishing styles. Read the rules in priority order.
When to Apply
- Building a converter that ingests a
.sketch file (or equivalent design source) and emits React + CSS
- Iterating on an existing converter where each improvement risks breaking other components
- Diagnosing why a converted component "almost matches" the design but visual-regression fails
- Designing the snapshot-gate / baseline strategy for a design-to-code pipeline
- Choosing between flexbox vs grid vs absolute positioning when the source is freeform geometry
- Translating Sketch-specific primitives (
MSImmutableFlexGroupLayout, attributedString, curvePoint, MSImmutableStyleCorners) into idiomatic CSS
Rule Categories by Priority
The ordering is the cascade — fix earlier stages first; later-stage fixes are wasted if the upstream tree is wrong.
| Priority |
Category |
Impact |
Prefix |
| 1 |
Reverse-Engineering Iteration Strategy |
CRITICAL |
iter- |
| 2 |
Tree Reconstruction & Symbol Resolution |
CRITICAL |
tree- |
| 3 |
Layout Algorithms (Flex/Freeform Inference) |
CRITICAL |
layout- |
| 4 |
Coordinate & Geometry Math |
HIGH |
geom- |
| 5 |
Visual Regression & Diff Algorithms |
HIGH |
diff- |
| 6 |
Style Translation (Color, Gradient, Shadow, Border) |
MEDIUM-HIGH |
style- |
| 7 |
Typography Math |
MEDIUM |
type- |
| 8 |
Path & Shape Rendering |
MEDIUM |
path- |
Quick Reference
1. Reverse-Engineering Iteration Strategy (CRITICAL)
iter-bisect-from-root — Convert top-down, bisect bottom-up to localize regressions in O(log n)
iter-baseline-snapshot-gate — Every change must pass committed baselines before merge
iter-convert-symbols-before-instances — Topologically sort symbols → instances; never inline duplicates
iter-freeze-design-tokens-first — Extract sharedSwatches/layerStyles to CSS variables BEFORE any component
iter-one-family-per-pr — Scope conversions to one component family per iteration
iter-keep-known-good-branch — Maintain a baseline branch as a three-way regression triage anchor
2. Tree Reconstruction & Symbol Resolution (CRITICAL)
tree-resolve-overrides-before-emit — Apply overrideValues against master into named props
tree-hash-subtrees-for-componentization — Structural hashing finds repetition designers missed
tree-collapse-passthrough-groups — Drop no-style single-child groups; preserve world coords
tree-hoist-shared-style-via-subtree-equivalence — Subtree equivalence + modifier classes, not per-property dedup
tree-clipping-mask-is-stacking-context — hasClippingMask requires isolation: isolate + clip-path
tree-foreign-symbols-become-library-imports — Foreign symbols are package imports, not duplicates
3. Layout Algorithms (CRITICAL)
layout-flex-group-enum-mapping — Map MSImmutableFlexGroupLayout enums 1:1 to CSS flex properties
layout-infer-flex-from-axis-projection-overlap — 1D separating-axis test for freeform → flex row/column
layout-detect-grid-via-2d-coordinate-clustering — Cluster edge coordinates with ε to detect CSS Grid
layout-promote-freeform-when-equal-gaps — Equal gaps within tolerance → display: flex; gap: Npx
layout-reverse-engineer-padding-not-margin — Insets become parent padding; rebase children
layout-preserve-wrapping-enabled — wrappingEnabled is the only way the source signals responsive intent
layout-ignore-layout-is-absolute-escape — flexItem.ignoreLayout: true → position: absolute over position: relative parent
4. Coordinate & Geometry Math (HIGH)
geom-compose-parent-transforms-before-emit — Compose 2D affine matrices, don't concatenate raw x/y
geom-round-only-at-leaves — Carry floats through; round once at the CSS boundary
geom-rotation-is-css-transform — Frame is unrotated AABB; emit transform: rotate()
geom-shape-group-bounds-via-union — Bounds = axis-aligned union of children, rebase to origin
geom-clipping-bounds-intersect-not-union — Nested clips intersect; never union or replace
5. Visual Regression & Diff Algorithms (HIGH)
diff-use-ssim-for-aa-content — SSIM for antialiased content; raw pixel diff false-positives on every retest
diff-region-budgeted-tolerances — Per-region SSIM floors (text 0.99, gradient 0.95, image 1.0)
diff-antialias-aware-pixelmatch-threshold — Pixelmatch includeAA: false for icon defect detection
diff-perceptual-hash-for-wrong-component-detection — Hamming distance buckets route triage automatically
diff-subtree-bisection-to-localize-regression — Disable subtrees in binary search to find the offending node
diff-baseline-per-component-not-per-page — Storybook per-story snapshots; scope = blast radius
6. Style Translation (MEDIUM-HIGH)
style-srgb-float-to-hex-via-gamma-correct-path — Sketch sRGB floats are already gamma-encoded; direct conversion only
style-preserve-display-p3 — colorSpace: 1 → emit color(display-p3 …) with sRGB fallback
style-gradient-angle-via-atan2 — atan2(dx, -dy) reframes Sketch vector to CSS gradient angle
style-stack-multi-shadow-in-paint-order — Reverse shadow array — Sketch paints last-first, CSS first-last
style-reconcile-border-position — Border position 0/1/2 → frame expansion or outline for outside
style-per-corner-radii-shorthand — Per-corner radii map to TL TR BR BL clockwise (not Sketch's row order)
7. Typography Math (MEDIUM)
type-split-attributed-string-runs-only-when-differ — Coalesce identical adjacent attribute runs; single-run case needs no inner span
type-pt-lineheight-to-unitless — lineHeight / fontSize → CSS unitless that scales with the font
type-kerning-pt-to-em-letter-spacing — kerning / fontSize → em-relative letter-spacing
type-build-font-fallback-ladder — Sketch family → web stack; SF Pro needs -apple-system, BlinkMacSystemFont, …
type-paragraph-spacing-between-not-after — Use gap on the parent, not margin-bottom with :last-child
8. Path & Shape Rendering (MEDIUM)
path-curve-point-to-svg-cubic-bezier — M + per-segment C from curveFrom/curveTo
path-rectangle-with-fixed-radius-is-css — Detect axis-aligned rounded rects early; emit <div> not <svg>
path-apple-smooth-corners-via-superellipse — Apple smooth corners are superellipses (n≈5), not circular arcs
path-flatten-boolean-ops-at-parse-time — Resolve union/subtract via paper.js in Node; ship one flat path
path-honor-winding-rule — windingRule 0/1 → SVG fill-rule nonzero/evenodd; explicit, not default
How to Use
- Read
references/_sections.md for category definitions and the cascade rationale
- Start with the iteration strategy (
iter-*) — without the regression gate, every other rule is just techniques
- Then tree (
tree-*) and layout (layout-*) — these are the load-bearing structural decisions
- Then geometry (
geom-*) and diff (diff-*) — these are the precision and validation layers
- Style, type, and path are the polish layer — high fidelity but mostly local impact
For a brand-new converter, follow the rules in priority order. For an existing converter, identify which stage owns the regression you're seeing (use [[diff-subtree-bisection-to-localize-regression]] + [[diff-perceptual-hash-for-wrong-component-detection]] to triage) and fix at the highest stage that owns it.
Reference Files
| File |
Description |
| references/_sections.md |
Category definitions, impact levels, cascade rationale |
| assets/templates/_template.md |
Template for adding new rules to this skill |
| metadata.json |
Version, discipline, references |
| AGENTS.md |
Auto-built TOC (regenerate via scripts/build-agents-md.js) |
1---2name: design-to-react-algorithms3description: Reverse-engineering a Sketch file (or Figma export with similar shape) into pixel-perfect React + CSS — the iteration mental model, tree reconstruction, layout inference algorithms, geometry math, visual-regression diffing, and the style/typography/path conversions that make "improvement without regression" enforceable. Trigger even if the user doesn't explicitly mention "algorithms" but is converting a design source into web code, building a design-to-code pipeline, or struggling to make incremental fidelity improvements without breaking previously-converted output.4---5# dot-skills Design-to-React Conversion Best Practices
6
7The reverse-engineering pipeline that converts Sketch files into pixel-perfect React + CSS, **with regression-safe iteration as the load-bearing constraint**. The skill is organized around the cascade effect of design-to-code conversion: a wrong call in stage N corrupts every output from stage N+1 onward, so categories are ordered by how much downstream they own.
8
9The user's primary requirement — *"each improvement doesn't cause regressions"* — is enforceable only if the iteration loop, the layer tree, and the layout solver are correct *before* you start polishing styles. Read the rules in priority order.
10
11## When to Apply
12
13- Building a converter that ingests a `.sketch` file (or equivalent design source) and emits React + CSS
14- Iterating on an existing converter where each improvement risks breaking other components
15- Diagnosing why a converted component "almost matches" the design but visual-regression fails
16- Designing the snapshot-gate / baseline strategy for a design-to-code pipeline
17- Choosing between flexbox vs grid vs absolute positioning when the source is freeform geometry
18- Translating Sketch-specific primitives (`MSImmutableFlexGroupLayout`, `attributedString`, `curvePoint`, `MSImmutableStyleCorners`) into idiomatic CSS
19
20## Rule Categories by Priority
21
22The ordering is the cascade — fix earlier stages first; later-stage fixes are wasted if the upstream tree is wrong.
23
24| Priority | Category | Impact | Prefix |
25|----------|----------|--------|--------|
26| 1 | Reverse-Engineering Iteration Strategy | CRITICAL | `iter-` |
27| 2 | Tree Reconstruction & Symbol Resolution | CRITICAL | `tree-` |
28| 3 | Layout Algorithms (Flex/Freeform Inference) | CRITICAL | `layout-` |
29| 4 | Coordinate & Geometry Math | HIGH | `geom-` |
30| 5 | Visual Regression & Diff Algorithms | HIGH | `diff-` |
31| 6 | Style Translation (Color, Gradient, Shadow, Border) | MEDIUM-HIGH | `style-` |
32| 7 | Typography Math | MEDIUM | `type-` |
33| 8 | Path & Shape Rendering | MEDIUM | `path-` |
34
35## Quick Reference
36
37### 1. Reverse-Engineering Iteration Strategy (CRITICAL)
38
39- [`iter-bisect-from-root`](references/iter-bisect-from-root.md) — Convert top-down, bisect bottom-up to localize regressions in O(log n)
40- [`iter-baseline-snapshot-gate`](references/iter-baseline-snapshot-gate.md) — Every change must pass committed baselines before merge
41- [`iter-convert-symbols-before-instances`](references/iter-convert-symbols-before-instances.md) — Topologically sort symbols → instances; never inline duplicates
42- [`iter-freeze-design-tokens-first`](references/iter-freeze-design-tokens-first.md) — Extract sharedSwatches/layerStyles to CSS variables BEFORE any component
43- [`iter-one-family-per-pr`](references/iter-one-family-per-pr.md) — Scope conversions to one component family per iteration
44- [`iter-keep-known-good-branch`](references/iter-keep-known-good-branch.md) — Maintain a baseline branch as a three-way regression triage anchor
45
46### 2. Tree Reconstruction & Symbol Resolution (CRITICAL)
47
48- [`tree-resolve-overrides-before-emit`](references/tree-resolve-overrides-before-emit.md) — Apply `overrideValues` against master into named props
49- [`tree-hash-subtrees-for-componentization`](references/tree-hash-subtrees-for-componentization.md) — Structural hashing finds repetition designers missed
50- [`tree-collapse-passthrough-groups`](references/tree-collapse-passthrough-groups.md) — Drop no-style single-child groups; preserve world coords
51- [`tree-hoist-shared-style-via-subtree-equivalence`](references/tree-hoist-shared-style-via-subtree-equivalence.md) — Subtree equivalence + modifier classes, not per-property dedup
52- [`tree-clipping-mask-is-stacking-context`](references/tree-clipping-mask-is-stacking-context.md) — `hasClippingMask` requires `isolation: isolate` + clip-path
53- [`tree-foreign-symbols-become-library-imports`](references/tree-foreign-symbols-become-library-imports.md) — Foreign symbols are package imports, not duplicates
54
55### 3. Layout Algorithms (CRITICAL)
56
57- [`layout-flex-group-enum-mapping`](references/layout-flex-group-enum-mapping.md) — Map `MSImmutableFlexGroupLayout` enums 1:1 to CSS flex properties
58- [`layout-infer-flex-from-axis-projection-overlap`](references/layout-infer-flex-from-axis-projection-overlap.md) — 1D separating-axis test for freeform → flex row/column
59- [`layout-detect-grid-via-2d-coordinate-clustering`](references/layout-detect-grid-via-2d-coordinate-clustering.md) — Cluster edge coordinates with ε to detect CSS Grid
60- [`layout-promote-freeform-when-equal-gaps`](references/layout-promote-freeform-when-equal-gaps.md) — Equal gaps within tolerance → `display: flex; gap: Npx`
61- [`layout-reverse-engineer-padding-not-margin`](references/layout-reverse-engineer-padding-not-margin.md) — Insets become parent padding; rebase children
62- [`layout-preserve-wrapping-enabled`](references/layout-preserve-wrapping-enabled.md) — `wrappingEnabled` is the only way the source signals responsive intent
63- [`layout-ignore-layout-is-absolute-escape`](references/layout-ignore-layout-is-absolute-escape.md) — `flexItem.ignoreLayout: true` → `position: absolute` over `position: relative` parent
64
65### 4. Coordinate & Geometry Math (HIGH)
66
67- [`geom-compose-parent-transforms-before-emit`](references/geom-compose-parent-transforms-before-emit.md) — Compose 2D affine matrices, don't concatenate raw x/y
68- [`geom-round-only-at-leaves`](references/geom-round-only-at-leaves.md) — Carry floats through; round once at the CSS boundary
69- [`geom-rotation-is-css-transform`](references/geom-rotation-is-css-transform.md) — Frame is unrotated AABB; emit `transform: rotate()`
70- [`geom-shape-group-bounds-via-union`](references/geom-shape-group-bounds-via-union.md) — Bounds = axis-aligned union of children, rebase to origin
71- [`geom-clipping-bounds-intersect-not-union`](references/geom-clipping-bounds-intersect-not-union.md) — Nested clips intersect; never union or replace
72
73### 5. Visual Regression & Diff Algorithms (HIGH)
74
75- [`diff-use-ssim-for-aa-content`](references/diff-use-ssim-for-aa-content.md) — SSIM for antialiased content; raw pixel diff false-positives on every retest
76- [`diff-region-budgeted-tolerances`](references/diff-region-budgeted-tolerances.md) — Per-region SSIM floors (text 0.99, gradient 0.95, image 1.0)
77- [`diff-antialias-aware-pixelmatch-threshold`](references/diff-antialias-aware-pixelmatch-threshold.md) — Pixelmatch `includeAA: false` for icon defect detection
78- [`diff-perceptual-hash-for-wrong-component-detection`](references/diff-perceptual-hash-for-wrong-component-detection.md) — Hamming distance buckets route triage automatically
79- [`diff-subtree-bisection-to-localize-regression`](references/diff-subtree-bisection-to-localize-regression.md) — Disable subtrees in binary search to find the offending node
80- [`diff-baseline-per-component-not-per-page`](references/diff-baseline-per-component-not-per-page.md) — Storybook per-story snapshots; scope = blast radius
81
82### 6. Style Translation (MEDIUM-HIGH)
83
84- [`style-srgb-float-to-hex-via-gamma-correct-path`](references/style-srgb-float-to-hex-via-gamma-correct-path.md) — Sketch sRGB floats are already gamma-encoded; direct conversion only
85- [`style-preserve-display-p3`](references/style-preserve-display-p3.md) — `colorSpace: 1` → emit `color(display-p3 …)` with sRGB fallback
86- [`style-gradient-angle-via-atan2`](references/style-gradient-angle-via-atan2.md) — `atan2(dx, -dy)` reframes Sketch vector to CSS gradient angle
87- [`style-stack-multi-shadow-in-paint-order`](references/style-stack-multi-shadow-in-paint-order.md) — Reverse shadow array — Sketch paints last-first, CSS first-last
88- [`style-reconcile-border-position`](references/style-reconcile-border-position.md) — Border position 0/1/2 → frame expansion or `outline` for outside
89- [`style-per-corner-radii-shorthand`](references/style-per-corner-radii-shorthand.md) — Per-corner radii map to `TL TR BR BL` clockwise (not Sketch's row order)
90
91### 7. Typography Math (MEDIUM)
92
93- [`type-split-attributed-string-runs-only-when-differ`](references/type-split-attributed-string-runs-only-when-differ.md) — Coalesce identical adjacent attribute runs; single-run case needs no inner span
94- [`type-pt-lineheight-to-unitless`](references/type-pt-lineheight-to-unitless.md) — `lineHeight / fontSize` → CSS unitless that scales with the font
95- [`type-kerning-pt-to-em-letter-spacing`](references/type-kerning-pt-to-em-letter-spacing.md) — `kerning / fontSize` → em-relative `letter-spacing`
96- [`type-build-font-fallback-ladder`](references/type-build-font-fallback-ladder.md) — Sketch family → web stack; SF Pro needs `-apple-system, BlinkMacSystemFont, …`
97- [`type-paragraph-spacing-between-not-after`](references/type-paragraph-spacing-between-not-after.md) — Use `gap` on the parent, not `margin-bottom` with `:last-child`
98
99### 8. Path & Shape Rendering (MEDIUM)
100
101- [`path-curve-point-to-svg-cubic-bezier`](references/path-curve-point-to-svg-cubic-bezier.md) — `M` + per-segment `C` from `curveFrom`/`curveTo`
102- [`path-rectangle-with-fixed-radius-is-css`](references/path-rectangle-with-fixed-radius-is-css.md) — Detect axis-aligned rounded rects early; emit `<div>` not `<svg>`
103- [`path-apple-smooth-corners-via-superellipse`](references/path-apple-smooth-corners-via-superellipse.md) — Apple smooth corners are superellipses (n≈5), not circular arcs
104- [`path-flatten-boolean-ops-at-parse-time`](references/path-flatten-boolean-ops-at-parse-time.md) — Resolve union/subtract via paper.js in Node; ship one flat path
105- [`path-honor-winding-rule`](references/path-honor-winding-rule.md) — `windingRule` 0/1 → SVG `fill-rule` nonzero/evenodd; explicit, not default
106
107## How to Use
108
1091. **Read [`references/_sections.md`](references/_sections.md)** for category definitions and the cascade rationale
1102. **Start with the iteration strategy (`iter-*`)** — without the regression gate, every other rule is just techniques
1113. **Then tree (`tree-*`) and layout (`layout-*`)** — these are the load-bearing structural decisions
1124. **Then geometry (`geom-*`) and diff (`diff-*`)** — these are the precision and validation layers
1135. **Style, type, and path** are the polish layer — high fidelity but mostly local impact
114
115For a brand-new converter, follow the rules in priority order. For an existing converter, identify which stage owns the regression you're seeing (use [[diff-subtree-bisection-to-localize-regression]] + [[diff-perceptual-hash-for-wrong-component-detection]] to triage) and fix at the highest stage that owns it.
116
117## Reference Files
118
119| File | Description |
120|------|-------------|
121| [references/_sections.md](references/_sections.md) | Category definitions, impact levels, cascade rationale |
122| [assets/templates/_template.md](assets/templates/_template.md) | Template for adding new rules to this skill |
123| [metadata.json](metadata.json) | Version, discipline, references |
124| [AGENTS.md](AGENTS.md) | Auto-built TOC (regenerate via `scripts/build-agents-md.js`) |