Clone-fidelity fix waves 1-3 (+ wave 4 in flight): capture settling, media, iframes
Fixes from the cropin.com / ooni.com audit reviews:
- walker: preserve whitespace-only text in inline elements ("ofthe" bug);
capture ::placeholder styles for inputs
- css: emit visibility (hidden-at-rest wordmark artifact); exempt list
markers from inherited elision (lost bullets); ::placeholder rules
- seo: sanitize og:type to Next's enum (render crash -> dev badge leak);
generated next.config disables devIndicators
- capture/stabilize: promote lazy-loader data attrs before snapshots
(collapsed sections, viewport-inconsistent captures); settle autoplay
carousels to home slide before every snapshot; force-reveal hidden
videos for poster shots + log failures
- generate: ship downloaded video files as local <video src>; keep
picture>source through IR prune with srcset rewrite (mobile-crop-on-
desktop heroes); pin Tailwind named screens to computeBands boundaries
- capture/graft: capture cross-origin iframe subtrees (Klaviyo signup
forms) with element-screenshot fallback; asset download retry +
visual-assets-missing reporting
Checkpoint commit: wave 4 (reveal settling, 206 range-response fix,
hidden-geometry banding, bare-zero utility gating) is mid-implementation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
da537e68b8
commit
9aed2540aa
@@ -0,0 +1,64 @@
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { computeBands } from "../src/generate/css.js";
|
||||
import { bandScreens, prefixFor, tailwindGlobalsCss } from "../src/generate/tailwind.js";
|
||||
|
||||
/** Interval covered by a ditto.css band media query (integer px, inclusive). */
|
||||
function bandInterval(media: string | null): [number, number] {
|
||||
if (!media) return [0, Infinity];
|
||||
const min = /min-width:\s*(\d+)px/.exec(media);
|
||||
const max = /max-width:\s*(\d+)px/.exec(media);
|
||||
return [min ? +min[1]! : 0, max ? +max[1]! : Infinity];
|
||||
}
|
||||
|
||||
/** Integer interval covered by a Tailwind variant prefix under the given screens.
|
||||
* v4 semantics for named AND arbitrary variants: `X:` = width >= X; `max-X:` = width < X. */
|
||||
function prefixInterval(prefix: string, screens: Map<string, number>): [number, number] {
|
||||
let lo = 0, hi = Infinity;
|
||||
for (const m of prefix.matchAll(/(max-)?(?:(sm|md|lg|xl|2xl)|(?:min-)?\[(\d+)px\]):/g)) {
|
||||
const px = m[3] ? +m[3] : screens.get(m[2]!);
|
||||
assert.ok(px !== undefined, `screen defined for ${m[0]}`);
|
||||
if (m[1]) hi = Math.min(hi, px! - 1);
|
||||
else lo = Math.max(lo, px!);
|
||||
}
|
||||
return [lo, hi];
|
||||
}
|
||||
|
||||
function assertAgreement(viewports: number[], canonical: number): void {
|
||||
const screens = new Map(bandScreens(viewports, canonical));
|
||||
for (const b of computeBands(viewports, canonical)) {
|
||||
if (!b.media) continue;
|
||||
assert.deepEqual(
|
||||
prefixInterval(prefixFor(b.media), screens),
|
||||
bandInterval(b.media),
|
||||
`band vp${b.vp} (${b.media}) matches its Tailwind prefix`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
describe("tailwind screens agree with computeBands boundaries", () => {
|
||||
it("standard 375/768/1280/1920 ladder: md/lg/2xl pinned to the band midpoint boundaries", () => {
|
||||
const screens = new Map(bandScreens([375, 768, 1280, 1920], 1280));
|
||||
// Midpoints 571/1024/1600 → bands ≤571, 572–1024, base, ≥1601.
|
||||
assert.deepEqual([...screens], [["md", 572], ["lg", 1025], ["2xl", 1601]]);
|
||||
// NOT Tailwind's stock 768/1024/1536 — stock 2xl (1536) would flip utility-classed
|
||||
// nodes to the 1920 layout in 1536–1600 while ditto.css still holds the 1280 layout.
|
||||
assert.notEqual(screens.get("2xl"), 1536);
|
||||
assertAgreement([375, 768, 1280, 1920], 1280);
|
||||
});
|
||||
|
||||
it("odd viewport set: bands fall back to exact arbitrary prefixes (no named screens needed)", () => {
|
||||
assert.deepEqual(bandScreens([320, 900, 1440], 900), []);
|
||||
assertAgreement([320, 900, 1440], 900);
|
||||
});
|
||||
|
||||
it("emits the derived screens into the generated @theme", () => {
|
||||
const css = tailwindGlobalsCss({
|
||||
reset: "", fontCss: "", tokensCss: "", htmlBg: "#fff", bodyFont: "sans-serif",
|
||||
clip: "", colorTokens: [], viewports: [375, 768, 1280, 1920], canonical: 1280,
|
||||
});
|
||||
assert.ok(css.includes("--breakpoint-md: 572px;"));
|
||||
assert.ok(css.includes("--breakpoint-lg: 1025px;"));
|
||||
assert.ok(css.includes("--breakpoint-2xl: 1601px;"));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user