diff --git a/docs/case-study.md b/docs/case-study.md index 9158315..6827161 100644 --- a/docs/case-study.md +++ b/docs/case-study.md @@ -37,7 +37,7 @@ Eleven routes, one per layout plus a few long posts with code, tables and math: ```bash R=(/ /about /blog /blog/page/2 /projects /tags /tags/markdown /blog/code-sample \ /blog/github-markdown-guide /blog/deriving-ols-estimator /blog/new-features-in-v1) -renderdiff http://localhost:3001 http://localhost:3002 $R +renderdiff http://localhost:3001 http://localhost:3002 "${R[@]}" ``` That is 55 shots at the default widths (1440, 1152, 896, 704, 375). renderdiff 0.3.0, Node @@ -117,7 +117,7 @@ The rest is two things the v4 migration does on purpose: After looking at crops for each, I accepted them: ```bash -renderdiff http://localhost:3001 http://localhost:3002 $R --rerun-failed \ +renderdiff http://localhost:3001 http://localhost:3002 "${R[@]}" --rerun-failed \ --accept "Tailwind v4 oklch palette and sub-pixel prose line-height (24.0001px); checked crops, invisible" ``` @@ -164,6 +164,10 @@ not verified: dark mode, hover and focus states, the open mobile menu, and the p  + Fixed after this study: the threshold is now 0.05 (cut-off about 88). Rerun against the + same two builds, the pink shows up on every page that has it: 10 of 11 routes differ + instead of 4, and the new differences are the pink button, links and tags. + - **Phantom bands.** In run 1, `/` showed `band +48@y~1719 band -44@y~1721 band +24 band -28` on a page whose height didn't change. The bands sit on the newsletter form and the footer, where nothing differs above the pixel threshold. Once the header was fixed, the diff --git a/src/compare.ts b/src/compare.ts index 3a696c0..aa4783a 100644 --- a/src/compare.ts +++ b/src/compare.ts @@ -90,7 +90,8 @@ export interface Spot { target: Region | Band; } -const PIXELMATCH = { threshold: 0.1, includeAA: false, alpha: 0.25 }; +/** Not pixelmatch's default 0.1: it passes Tailwind v3→v4 pink-500 (YIQ delta 117, cut-off 352). */ +const PIXELMATCH = { threshold: 0.05, includeAA: false, alpha: 0.25 }; /** Edit-distance cap for `myers`: past it, memory is O(D²) and the answer is "unrelated" anyway. */ const MAX_EDIT = 2000; diff --git a/tests/fixtures/pages/README.md b/tests/fixtures/pages/README.md index 87168f5..bedc325 100644 --- a/tests/fixtures/pages/README.md +++ b/tests/fixtures/pages/README.md @@ -12,7 +12,7 @@ Three groups: `inline-block-vs-flex`. The two sides are visually identical and built with different CSS. These must score ≈100%. They are the corpus's central claim: the score tracks pixels, not markup, so a CSS refactor that changed nothing visible reads as "nothing changed". -- **Controls** — `shifted`, `taller`, `color`, `nav`, `margin`. Deliberately different. These must score low. +- **Controls** — `shifted`, `taller`, `color`, `tint`, `nav`, `margin`. Deliberately different. These must score low. A corpus of only passing cases proves nothing; without these, a tool that returned 100% unconditionally would pass the suite. They also pin the diff regions (`color`), the row alignment (`taller`, `nav`, and `shifted`, which has no band), the text diff (`nav`) and the diff --git a/tests/fixtures/pages/abs-vs-flex/left.html b/tests/fixtures/pages/abs-vs-flex/left.html index 8a5459a..eb57b54 100644 --- a/tests/fixtures/pages/abs-vs-flex/left.html +++ b/tests/fixtures/pages/abs-vs-flex/left.html @@ -8,9 +8,11 @@ /* LEFT: the transform-centring idiom. `translate(-50%, -50%)` only lands on whole pixels when the free space either side is an integer, which is why the hero and the box both have explicit even dimensions: - desktop (926 - 448) / 2 = 239 and (318 - 160) / 2 = 79, both exact. */ + desktop (926 - 448) / 2 = 239 and (318 - 160) / 2 = 79, both exact. At 375 the shared hero + is 343 wide, so left: 50% and translate(-50%) both land on .5; the box below keeps them whole. */ .hero { position: relative; } .hero-box { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); } +@media (max-width: 480px) { .hero { width: 342px; } .hero-box { width: 310px; } }
diff --git a/tests/fixtures/pages/abs-vs-flex/right.html b/tests/fixtures/pages/abs-vs-flex/right.html index 99d806a..9075ed4 100644 --- a/tests/fixtures/pages/abs-vs-flex/right.html +++ b/tests/fixtures/pages/abs-vs-flex/right.html @@ -7,6 +7,7 @@ diff --git a/tests/fixtures/pages/tint/left.html b/tests/fixtures/pages/tint/left.html new file mode 100644 index 0000000..d6a534c --- /dev/null +++ b/tests/fixtures/pages/tint/left.html @@ -0,0 +1,50 @@ + + + + +This pair is deliberately different. One swatch is a slightly different pink on each side, and nothing else changes.
+ +208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
A single 208 by 160 swatch is a small share of the page, so this one lands in the amber band rather than the red one. That is the intended reading: the score is a triage signal that tells you where to look, not a verdict on how bad the change is.
+It still has to move. A recoloured element that scored as parity would mean the comparison was measuring layout instead of pixels.
+This pair is deliberately different. One swatch is a slightly different pink on each side, and nothing else changes.
+ +208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
208 by 160.
A single 208 by 160 swatch is a small share of the page, so this one lands in the amber band rather than the red one. That is the intended reading: the score is a triage signal that tells you where to look, not a verdict on how bad the change is.
+It still has to move. A recoloured element that scored as parity would mean the comparison was measuring layout instead of pixels.
+