fix(live-accept): handle JSX self-closing <style />, single-line variants, and same-line style blocks

Three related extraction bugs surfaced in the EAC session all rooted
in the line-based state machine:

1. `<style ... />` (JSX self-closing) had no separate `</style>` for
   the "skip until close" mode to exit on, so the state machine stuck
   and every `data-impeccable-variant` marker after it got missed.
   Accept reported `handled: false, error: "Variant N not found"`.
2. A variant whose entire `<div ...>...</div>` sits on one line had
   its body silently discarded — the marker line was `continue`d past,
   and the extractor started capturing from the next line, which
   usually belonged to a different variant or the wrapper close.
3. `extractCss` kept scanning for `</style>` after a self-closing
   opener, greedily swallowing every subsequent variant div as "CSS".
   Result: a mangled carbonize block stuffed with HTML and a duplicate
   variant rendered below.

## Fix

Replaced the line-based state machine with a string-based flow:

- `stripStyleAndJoin(lines, block)` returns the wrapper text with
  `<style>` elements fully removed. Handles self-closing, same-line
  open+close, and multi-line open/close. Markers inside CSS strings
  (e.g. `@scope ([data-impeccable-variant="1"])`) are gone by the
  time extraction runs — no false positives.
- `extractInnerByAttr(text, attrMatch)` is a balanced-tag matcher that
  walks the joined text finding `<TAG ...attrMatch...>…</TAG>` with
  proper depth tracking for nested same-tag elements. Handles
  single-line, multi-line, and deeply nested variants.
- `extractOriginal` and `extractVariant` are thin wrappers over the
  above.
- `extractCss` gets explicit same-line handling: returns null for
  self-closing (nothing to carbonize), extracts inner content via
  regex for same-line `<style>…</style>`, falls through to the
  existing multi-line path otherwise.

## Tests

New tests/live-accept.test.mjs with four cases — all failing before,
all passing after:

- Self-closing `<style />` with dangerouslySetInnerHTML
- Single-line `<style>…</style>`
- Multi-line `<style>...</style>` (regression baseline)
- Discard restores the original element after self-closing style

Wired into `bun run test`. Full suite passes.

Credit: precise repro + root-cause trace from the other agent in the
EAC session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-04-22 01:01:43 -07:00
co-authored by Claude Opus 4.7
parent a4832adf2f
commit cd8dbff014
26 changed files with 1458 additions and 781 deletions
@@ -51,6 +51,16 @@ Editorial has permission for Committed, Full palette, and Drenched strategies. U
- Don't center everything. Left-aligned in asymmetric compositions feels more designed.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` — breakpoint-free responsiveness.
## Imagery
Editorial register leans on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
- **For greenfield work without local assets, reach for stock imagery** from Unsplash (`https://images.unsplash.com/photo-{id}?w=…&q=80`), Pexels, or similar. A well-chosen Unsplash photo is a valid deliverable — colored placeholder blocks are not.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Don't stop at zero** when the brief implies imagery. A moto forum without motorcycle photos, a restaurant without food, a hotel without a view — these read as stubs, not as editorial restraint.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions.
@@ -192,91 +192,117 @@ function findMarkerBlock(id, lines) {
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines (still indented as stored in the wrapper).
*
* CSS inside a <style> block can reference `data-impeccable-variant="N"` via
* `@scope`, which would falsely match the HTML div we're looking for — so skip
* style regions entirely.
* Join wrapper lines into a single string with `<style>` elements removed so
* marker matching and div-depth tracking aren't confused by:
* - CSS `@scope ([data-impeccable-variant="N"])` strings that look like the
* HTML marker we're searching for
* - JSX self-closing `<style ... />` (no separate `</style>` to close on)
* - Same-line `<style>…</style>` blocks
* - Multi-line `<style>\n…\n</style>` blocks
*/
function extractOriginal(lines, block) {
let inOriginal = false;
function stripStyleAndJoin(lines, block) {
const out = [];
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
let line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inStyle) {
// Strip any complete <style> elements on this line (self-closed or
// same-line-closed), including their body content.
line = line
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/g, '')
.replace(/<style\b[^>]*\/\s*>/g, '');
if (!inOriginal && line.includes('data-impeccable-variant="original"')) {
inOriginal = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="original">
}
if (inOriginal) {
// Count div opens/closes to find the matching </div>
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // this is the closing </div> of the original wrapper
content.push(line);
// If a <style> opener remains (multi-line body starts here), strip from
// the opener to end-of-line and flip into skip mode.
const openerIdx = line.search(/<style\b/);
if (openerIdx !== -1) {
line = line.slice(0, openerIdx);
inStyle = true;
}
out.push(line);
} else {
// In multi-line style body; drop everything until we see </style>.
const closeIdx = line.search(/<\/style\s*>/);
if (closeIdx !== -1) {
inStyle = false;
out.push(line.slice(closeIdx).replace(/<\/style\s*>/, ''));
}
// else: skip line entirely
}
}
return out.join('\n');
}
return content;
/**
* Find the inner content of `<TAG ...attrMatch...>…</TAG>` inside `text`,
* handling nested same-tag elements via depth counting. `attrMatch` is a
* regex source fragment that must appear inside the opener tag.
* Returns the inner string (may be empty), or null if not found.
*/
function extractInnerByAttr(text, attrMatch) {
const openerRe = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*' + attrMatch + '[^>]*>');
const openMatch = text.match(openerRe);
if (!openMatch) return null;
const tagName = openMatch[1];
const innerStart = openMatch.index + openMatch[0].length;
// Match any opener or closer of this tag name after innerStart.
// (Does not match self-closing <TAG … />, which doesn't contribute to depth.)
const tagRe = new RegExp('<(?:/)?' + tagName + '\\b[^>]*>', 'g');
tagRe.lastIndex = innerStart;
let depth = 1;
let m;
while ((m = tagRe.exec(text))) {
const isClose = m[0].startsWith('</');
const isSelfClose = !isClose && /\/\s*>$/.test(m[0]);
if (isClose) {
depth--;
if (depth === 0) return text.slice(innerStart, m.index);
} else if (!isSelfClose) {
depth++;
}
}
return null;
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines.
*/
function extractOriginal(lines, block) {
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="original"');
if (inner === null) return [];
return inner.split('\n');
}
/**
* Extract a specific variant's inner content (stripping the wrapper div).
* Returns an array of lines, or null if not found.
*
* Skip <style> blocks — see extractOriginal for why.
*/
function extractVariant(lines, block, variantNum) {
let inVariant = false;
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inVariant && line.includes('data-impeccable-variant="' + variantNum + '"')) {
inVariant = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="N">
}
if (inVariant) {
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // closing </div> of the variant wrapper
content.push(line);
}
}
return content.length > 0 ? content : null;
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="' + variantNum + '"');
if (inner === null) return null;
const result = inner.split('\n');
// Collapse a lone empty leading/trailing line (common after string splice).
while (result.length > 1 && result[0].trim() === '') result.shift();
while (result.length > 1 && result[result.length - 1].trim() === '') result.pop();
return result.length > 0 ? result : null;
}
/**
* Extract the colocated <style> block content (between the style tags).
* Returns an array of CSS lines, or null if no style block found.
*
* Handles three shapes of `<style data-impeccable-css="ID" ...>`:
* 1. Self-closing: `<style ... />` — no body; return null (nothing to carbonize).
* 2. Same-line open+close: `<style>...</style>` — return the inner content.
* 3. Multi-line: `<style>` on one line, `</style>` on a later line — return
* the lines between them.
*/
function extractCss(lines, block, id) {
const styleAttr = 'data-impeccable-css="' + id + '"';
@@ -287,6 +313,14 @@ function extractCss(lines, block, id) {
const line = lines[i];
if (!inStyle && line.includes(styleAttr)) {
// Self-closing: nothing to carbonize.
if (/<style\b[^>]*\/\s*>/.test(line)) return null;
// Same-line open + close: extract inner text.
const sameLine = line.match(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/);
if (sameLine) {
const inner = sameLine[1];
return inner.length > 0 ? inner.split('\n') : null;
}
inStyle = true;
continue; // skip the <style> opening tag
}
@@ -51,6 +51,16 @@ Editorial has permission for Committed, Full palette, and Drenched strategies. U
- Don't center everything. Left-aligned in asymmetric compositions feels more designed.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` — breakpoint-free responsiveness.
## Imagery
Editorial register leans on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
- **For greenfield work without local assets, reach for stock imagery** from Unsplash (`https://images.unsplash.com/photo-{id}?w=…&q=80`), Pexels, or similar. A well-chosen Unsplash photo is a valid deliverable — colored placeholder blocks are not.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Don't stop at zero** when the brief implies imagery. A moto forum without motorcycle photos, a restaurant without food, a hotel without a view — these read as stubs, not as editorial restraint.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions.
@@ -192,91 +192,117 @@ function findMarkerBlock(id, lines) {
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines (still indented as stored in the wrapper).
*
* CSS inside a <style> block can reference `data-impeccable-variant="N"` via
* `@scope`, which would falsely match the HTML div we're looking for — so skip
* style regions entirely.
* Join wrapper lines into a single string with `<style>` elements removed so
* marker matching and div-depth tracking aren't confused by:
* - CSS `@scope ([data-impeccable-variant="N"])` strings that look like the
* HTML marker we're searching for
* - JSX self-closing `<style ... />` (no separate `</style>` to close on)
* - Same-line `<style>…</style>` blocks
* - Multi-line `<style>\n…\n</style>` blocks
*/
function extractOriginal(lines, block) {
let inOriginal = false;
function stripStyleAndJoin(lines, block) {
const out = [];
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
let line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inStyle) {
// Strip any complete <style> elements on this line (self-closed or
// same-line-closed), including their body content.
line = line
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/g, '')
.replace(/<style\b[^>]*\/\s*>/g, '');
if (!inOriginal && line.includes('data-impeccable-variant="original"')) {
inOriginal = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="original">
}
if (inOriginal) {
// Count div opens/closes to find the matching </div>
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // this is the closing </div> of the original wrapper
content.push(line);
// If a <style> opener remains (multi-line body starts here), strip from
// the opener to end-of-line and flip into skip mode.
const openerIdx = line.search(/<style\b/);
if (openerIdx !== -1) {
line = line.slice(0, openerIdx);
inStyle = true;
}
out.push(line);
} else {
// In multi-line style body; drop everything until we see </style>.
const closeIdx = line.search(/<\/style\s*>/);
if (closeIdx !== -1) {
inStyle = false;
out.push(line.slice(closeIdx).replace(/<\/style\s*>/, ''));
}
// else: skip line entirely
}
}
return out.join('\n');
}
return content;
/**
* Find the inner content of `<TAG ...attrMatch...>…</TAG>` inside `text`,
* handling nested same-tag elements via depth counting. `attrMatch` is a
* regex source fragment that must appear inside the opener tag.
* Returns the inner string (may be empty), or null if not found.
*/
function extractInnerByAttr(text, attrMatch) {
const openerRe = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*' + attrMatch + '[^>]*>');
const openMatch = text.match(openerRe);
if (!openMatch) return null;
const tagName = openMatch[1];
const innerStart = openMatch.index + openMatch[0].length;
// Match any opener or closer of this tag name after innerStart.
// (Does not match self-closing <TAG … />, which doesn't contribute to depth.)
const tagRe = new RegExp('<(?:/)?' + tagName + '\\b[^>]*>', 'g');
tagRe.lastIndex = innerStart;
let depth = 1;
let m;
while ((m = tagRe.exec(text))) {
const isClose = m[0].startsWith('</');
const isSelfClose = !isClose && /\/\s*>$/.test(m[0]);
if (isClose) {
depth--;
if (depth === 0) return text.slice(innerStart, m.index);
} else if (!isSelfClose) {
depth++;
}
}
return null;
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines.
*/
function extractOriginal(lines, block) {
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="original"');
if (inner === null) return [];
return inner.split('\n');
}
/**
* Extract a specific variant's inner content (stripping the wrapper div).
* Returns an array of lines, or null if not found.
*
* Skip <style> blocks — see extractOriginal for why.
*/
function extractVariant(lines, block, variantNum) {
let inVariant = false;
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inVariant && line.includes('data-impeccable-variant="' + variantNum + '"')) {
inVariant = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="N">
}
if (inVariant) {
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // closing </div> of the variant wrapper
content.push(line);
}
}
return content.length > 0 ? content : null;
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="' + variantNum + '"');
if (inner === null) return null;
const result = inner.split('\n');
// Collapse a lone empty leading/trailing line (common after string splice).
while (result.length > 1 && result[0].trim() === '') result.shift();
while (result.length > 1 && result[result.length - 1].trim() === '') result.pop();
return result.length > 0 ? result : null;
}
/**
* Extract the colocated <style> block content (between the style tags).
* Returns an array of CSS lines, or null if no style block found.
*
* Handles three shapes of `<style data-impeccable-css="ID" ...>`:
* 1. Self-closing: `<style ... />` — no body; return null (nothing to carbonize).
* 2. Same-line open+close: `<style>...</style>` — return the inner content.
* 3. Multi-line: `<style>` on one line, `</style>` on a later line — return
* the lines between them.
*/
function extractCss(lines, block, id) {
const styleAttr = 'data-impeccable-css="' + id + '"';
@@ -287,6 +313,14 @@ function extractCss(lines, block, id) {
const line = lines[i];
if (!inStyle && line.includes(styleAttr)) {
// Self-closing: nothing to carbonize.
if (/<style\b[^>]*\/\s*>/.test(line)) return null;
// Same-line open + close: extract inner text.
const sameLine = line.match(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/);
if (sameLine) {
const inner = sameLine[1];
return inner.length > 0 ? inner.split('\n') : null;
}
inStyle = true;
continue; // skip the <style> opening tag
}
@@ -51,6 +51,16 @@ Editorial has permission for Committed, Full palette, and Drenched strategies. U
- Don't center everything. Left-aligned in asymmetric compositions feels more designed.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` — breakpoint-free responsiveness.
## Imagery
Editorial register leans on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
- **For greenfield work without local assets, reach for stock imagery** from Unsplash (`https://images.unsplash.com/photo-{id}?w=…&q=80`), Pexels, or similar. A well-chosen Unsplash photo is a valid deliverable — colored placeholder blocks are not.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Don't stop at zero** when the brief implies imagery. A moto forum without motorcycle photos, a restaurant without food, a hotel without a view — these read as stubs, not as editorial restraint.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions.
@@ -192,91 +192,117 @@ function findMarkerBlock(id, lines) {
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines (still indented as stored in the wrapper).
*
* CSS inside a <style> block can reference `data-impeccable-variant="N"` via
* `@scope`, which would falsely match the HTML div we're looking for — so skip
* style regions entirely.
* Join wrapper lines into a single string with `<style>` elements removed so
* marker matching and div-depth tracking aren't confused by:
* - CSS `@scope ([data-impeccable-variant="N"])` strings that look like the
* HTML marker we're searching for
* - JSX self-closing `<style ... />` (no separate `</style>` to close on)
* - Same-line `<style>…</style>` blocks
* - Multi-line `<style>\n…\n</style>` blocks
*/
function extractOriginal(lines, block) {
let inOriginal = false;
function stripStyleAndJoin(lines, block) {
const out = [];
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
let line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inStyle) {
// Strip any complete <style> elements on this line (self-closed or
// same-line-closed), including their body content.
line = line
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/g, '')
.replace(/<style\b[^>]*\/\s*>/g, '');
if (!inOriginal && line.includes('data-impeccable-variant="original"')) {
inOriginal = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="original">
}
if (inOriginal) {
// Count div opens/closes to find the matching </div>
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // this is the closing </div> of the original wrapper
content.push(line);
// If a <style> opener remains (multi-line body starts here), strip from
// the opener to end-of-line and flip into skip mode.
const openerIdx = line.search(/<style\b/);
if (openerIdx !== -1) {
line = line.slice(0, openerIdx);
inStyle = true;
}
out.push(line);
} else {
// In multi-line style body; drop everything until we see </style>.
const closeIdx = line.search(/<\/style\s*>/);
if (closeIdx !== -1) {
inStyle = false;
out.push(line.slice(closeIdx).replace(/<\/style\s*>/, ''));
}
// else: skip line entirely
}
}
return out.join('\n');
}
return content;
/**
* Find the inner content of `<TAG ...attrMatch...>…</TAG>` inside `text`,
* handling nested same-tag elements via depth counting. `attrMatch` is a
* regex source fragment that must appear inside the opener tag.
* Returns the inner string (may be empty), or null if not found.
*/
function extractInnerByAttr(text, attrMatch) {
const openerRe = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*' + attrMatch + '[^>]*>');
const openMatch = text.match(openerRe);
if (!openMatch) return null;
const tagName = openMatch[1];
const innerStart = openMatch.index + openMatch[0].length;
// Match any opener or closer of this tag name after innerStart.
// (Does not match self-closing <TAG … />, which doesn't contribute to depth.)
const tagRe = new RegExp('<(?:/)?' + tagName + '\\b[^>]*>', 'g');
tagRe.lastIndex = innerStart;
let depth = 1;
let m;
while ((m = tagRe.exec(text))) {
const isClose = m[0].startsWith('</');
const isSelfClose = !isClose && /\/\s*>$/.test(m[0]);
if (isClose) {
depth--;
if (depth === 0) return text.slice(innerStart, m.index);
} else if (!isSelfClose) {
depth++;
}
}
return null;
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines.
*/
function extractOriginal(lines, block) {
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="original"');
if (inner === null) return [];
return inner.split('\n');
}
/**
* Extract a specific variant's inner content (stripping the wrapper div).
* Returns an array of lines, or null if not found.
*
* Skip <style> blocks — see extractOriginal for why.
*/
function extractVariant(lines, block, variantNum) {
let inVariant = false;
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inVariant && line.includes('data-impeccable-variant="' + variantNum + '"')) {
inVariant = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="N">
}
if (inVariant) {
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // closing </div> of the variant wrapper
content.push(line);
}
}
return content.length > 0 ? content : null;
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="' + variantNum + '"');
if (inner === null) return null;
const result = inner.split('\n');
// Collapse a lone empty leading/trailing line (common after string splice).
while (result.length > 1 && result[0].trim() === '') result.shift();
while (result.length > 1 && result[result.length - 1].trim() === '') result.pop();
return result.length > 0 ? result : null;
}
/**
* Extract the colocated <style> block content (between the style tags).
* Returns an array of CSS lines, or null if no style block found.
*
* Handles three shapes of `<style data-impeccable-css="ID" ...>`:
* 1. Self-closing: `<style ... />` — no body; return null (nothing to carbonize).
* 2. Same-line open+close: `<style>...</style>` — return the inner content.
* 3. Multi-line: `<style>` on one line, `</style>` on a later line — return
* the lines between them.
*/
function extractCss(lines, block, id) {
const styleAttr = 'data-impeccable-css="' + id + '"';
@@ -287,6 +313,14 @@ function extractCss(lines, block, id) {
const line = lines[i];
if (!inStyle && line.includes(styleAttr)) {
// Self-closing: nothing to carbonize.
if (/<style\b[^>]*\/\s*>/.test(line)) return null;
// Same-line open + close: extract inner text.
const sameLine = line.match(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/);
if (sameLine) {
const inner = sameLine[1];
return inner.length > 0 ? inner.split('\n') : null;
}
inStyle = true;
continue; // skip the <style> opening tag
}
@@ -51,6 +51,16 @@ Editorial has permission for Committed, Full palette, and Drenched strategies. U
- Don't center everything. Left-aligned in asymmetric compositions feels more designed.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` — breakpoint-free responsiveness.
## Imagery
Editorial register leans on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
- **For greenfield work without local assets, reach for stock imagery** from Unsplash (`https://images.unsplash.com/photo-{id}?w=…&q=80`), Pexels, or similar. A well-chosen Unsplash photo is a valid deliverable — colored placeholder blocks are not.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Don't stop at zero** when the brief implies imagery. A moto forum without motorcycle photos, a restaurant without food, a hotel without a view — these read as stubs, not as editorial restraint.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions.
@@ -192,91 +192,117 @@ function findMarkerBlock(id, lines) {
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines (still indented as stored in the wrapper).
*
* CSS inside a <style> block can reference `data-impeccable-variant="N"` via
* `@scope`, which would falsely match the HTML div we're looking for — so skip
* style regions entirely.
* Join wrapper lines into a single string with `<style>` elements removed so
* marker matching and div-depth tracking aren't confused by:
* - CSS `@scope ([data-impeccable-variant="N"])` strings that look like the
* HTML marker we're searching for
* - JSX self-closing `<style ... />` (no separate `</style>` to close on)
* - Same-line `<style>…</style>` blocks
* - Multi-line `<style>\n…\n</style>` blocks
*/
function extractOriginal(lines, block) {
let inOriginal = false;
function stripStyleAndJoin(lines, block) {
const out = [];
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
let line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inStyle) {
// Strip any complete <style> elements on this line (self-closed or
// same-line-closed), including their body content.
line = line
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/g, '')
.replace(/<style\b[^>]*\/\s*>/g, '');
if (!inOriginal && line.includes('data-impeccable-variant="original"')) {
inOriginal = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="original">
}
if (inOriginal) {
// Count div opens/closes to find the matching </div>
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // this is the closing </div> of the original wrapper
content.push(line);
// If a <style> opener remains (multi-line body starts here), strip from
// the opener to end-of-line and flip into skip mode.
const openerIdx = line.search(/<style\b/);
if (openerIdx !== -1) {
line = line.slice(0, openerIdx);
inStyle = true;
}
out.push(line);
} else {
// In multi-line style body; drop everything until we see </style>.
const closeIdx = line.search(/<\/style\s*>/);
if (closeIdx !== -1) {
inStyle = false;
out.push(line.slice(closeIdx).replace(/<\/style\s*>/, ''));
}
// else: skip line entirely
}
}
return out.join('\n');
}
return content;
/**
* Find the inner content of `<TAG ...attrMatch...>…</TAG>` inside `text`,
* handling nested same-tag elements via depth counting. `attrMatch` is a
* regex source fragment that must appear inside the opener tag.
* Returns the inner string (may be empty), or null if not found.
*/
function extractInnerByAttr(text, attrMatch) {
const openerRe = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*' + attrMatch + '[^>]*>');
const openMatch = text.match(openerRe);
if (!openMatch) return null;
const tagName = openMatch[1];
const innerStart = openMatch.index + openMatch[0].length;
// Match any opener or closer of this tag name after innerStart.
// (Does not match self-closing <TAG … />, which doesn't contribute to depth.)
const tagRe = new RegExp('<(?:/)?' + tagName + '\\b[^>]*>', 'g');
tagRe.lastIndex = innerStart;
let depth = 1;
let m;
while ((m = tagRe.exec(text))) {
const isClose = m[0].startsWith('</');
const isSelfClose = !isClose && /\/\s*>$/.test(m[0]);
if (isClose) {
depth--;
if (depth === 0) return text.slice(innerStart, m.index);
} else if (!isSelfClose) {
depth++;
}
}
return null;
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines.
*/
function extractOriginal(lines, block) {
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="original"');
if (inner === null) return [];
return inner.split('\n');
}
/**
* Extract a specific variant's inner content (stripping the wrapper div).
* Returns an array of lines, or null if not found.
*
* Skip <style> blocks — see extractOriginal for why.
*/
function extractVariant(lines, block, variantNum) {
let inVariant = false;
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inVariant && line.includes('data-impeccable-variant="' + variantNum + '"')) {
inVariant = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="N">
}
if (inVariant) {
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // closing </div> of the variant wrapper
content.push(line);
}
}
return content.length > 0 ? content : null;
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="' + variantNum + '"');
if (inner === null) return null;
const result = inner.split('\n');
// Collapse a lone empty leading/trailing line (common after string splice).
while (result.length > 1 && result[0].trim() === '') result.shift();
while (result.length > 1 && result[result.length - 1].trim() === '') result.pop();
return result.length > 0 ? result : null;
}
/**
* Extract the colocated <style> block content (between the style tags).
* Returns an array of CSS lines, or null if no style block found.
*
* Handles three shapes of `<style data-impeccable-css="ID" ...>`:
* 1. Self-closing: `<style ... />` — no body; return null (nothing to carbonize).
* 2. Same-line open+close: `<style>...</style>` — return the inner content.
* 3. Multi-line: `<style>` on one line, `</style>` on a later line — return
* the lines between them.
*/
function extractCss(lines, block, id) {
const styleAttr = 'data-impeccable-css="' + id + '"';
@@ -287,6 +313,14 @@ function extractCss(lines, block, id) {
const line = lines[i];
if (!inStyle && line.includes(styleAttr)) {
// Self-closing: nothing to carbonize.
if (/<style\b[^>]*\/\s*>/.test(line)) return null;
// Same-line open + close: extract inner text.
const sameLine = line.match(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/);
if (sameLine) {
const inner = sameLine[1];
return inner.length > 0 ? inner.split('\n') : null;
}
inStyle = true;
continue; // skip the <style> opening tag
}
@@ -51,6 +51,16 @@ Editorial has permission for Committed, Full palette, and Drenched strategies. U
- Don't center everything. Left-aligned in asymmetric compositions feels more designed.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` — breakpoint-free responsiveness.
## Imagery
Editorial register leans on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
- **For greenfield work without local assets, reach for stock imagery** from Unsplash (`https://images.unsplash.com/photo-{id}?w=…&q=80`), Pexels, or similar. A well-chosen Unsplash photo is a valid deliverable — colored placeholder blocks are not.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Don't stop at zero** when the brief implies imagery. A moto forum without motorcycle photos, a restaurant without food, a hotel without a view — these read as stubs, not as editorial restraint.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions.
@@ -192,91 +192,117 @@ function findMarkerBlock(id, lines) {
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines (still indented as stored in the wrapper).
*
* CSS inside a <style> block can reference `data-impeccable-variant="N"` via
* `@scope`, which would falsely match the HTML div we're looking for — so skip
* style regions entirely.
* Join wrapper lines into a single string with `<style>` elements removed so
* marker matching and div-depth tracking aren't confused by:
* - CSS `@scope ([data-impeccable-variant="N"])` strings that look like the
* HTML marker we're searching for
* - JSX self-closing `<style ... />` (no separate `</style>` to close on)
* - Same-line `<style>…</style>` blocks
* - Multi-line `<style>\n…\n</style>` blocks
*/
function extractOriginal(lines, block) {
let inOriginal = false;
function stripStyleAndJoin(lines, block) {
const out = [];
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
let line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inStyle) {
// Strip any complete <style> elements on this line (self-closed or
// same-line-closed), including their body content.
line = line
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/g, '')
.replace(/<style\b[^>]*\/\s*>/g, '');
if (!inOriginal && line.includes('data-impeccable-variant="original"')) {
inOriginal = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="original">
}
if (inOriginal) {
// Count div opens/closes to find the matching </div>
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // this is the closing </div> of the original wrapper
content.push(line);
// If a <style> opener remains (multi-line body starts here), strip from
// the opener to end-of-line and flip into skip mode.
const openerIdx = line.search(/<style\b/);
if (openerIdx !== -1) {
line = line.slice(0, openerIdx);
inStyle = true;
}
out.push(line);
} else {
// In multi-line style body; drop everything until we see </style>.
const closeIdx = line.search(/<\/style\s*>/);
if (closeIdx !== -1) {
inStyle = false;
out.push(line.slice(closeIdx).replace(/<\/style\s*>/, ''));
}
// else: skip line entirely
}
}
return out.join('\n');
}
return content;
/**
* Find the inner content of `<TAG ...attrMatch...>…</TAG>` inside `text`,
* handling nested same-tag elements via depth counting. `attrMatch` is a
* regex source fragment that must appear inside the opener tag.
* Returns the inner string (may be empty), or null if not found.
*/
function extractInnerByAttr(text, attrMatch) {
const openerRe = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*' + attrMatch + '[^>]*>');
const openMatch = text.match(openerRe);
if (!openMatch) return null;
const tagName = openMatch[1];
const innerStart = openMatch.index + openMatch[0].length;
// Match any opener or closer of this tag name after innerStart.
// (Does not match self-closing <TAG … />, which doesn't contribute to depth.)
const tagRe = new RegExp('<(?:/)?' + tagName + '\\b[^>]*>', 'g');
tagRe.lastIndex = innerStart;
let depth = 1;
let m;
while ((m = tagRe.exec(text))) {
const isClose = m[0].startsWith('</');
const isSelfClose = !isClose && /\/\s*>$/.test(m[0]);
if (isClose) {
depth--;
if (depth === 0) return text.slice(innerStart, m.index);
} else if (!isSelfClose) {
depth++;
}
}
return null;
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines.
*/
function extractOriginal(lines, block) {
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="original"');
if (inner === null) return [];
return inner.split('\n');
}
/**
* Extract a specific variant's inner content (stripping the wrapper div).
* Returns an array of lines, or null if not found.
*
* Skip <style> blocks — see extractOriginal for why.
*/
function extractVariant(lines, block, variantNum) {
let inVariant = false;
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inVariant && line.includes('data-impeccable-variant="' + variantNum + '"')) {
inVariant = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="N">
}
if (inVariant) {
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // closing </div> of the variant wrapper
content.push(line);
}
}
return content.length > 0 ? content : null;
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="' + variantNum + '"');
if (inner === null) return null;
const result = inner.split('\n');
// Collapse a lone empty leading/trailing line (common after string splice).
while (result.length > 1 && result[0].trim() === '') result.shift();
while (result.length > 1 && result[result.length - 1].trim() === '') result.pop();
return result.length > 0 ? result : null;
}
/**
* Extract the colocated <style> block content (between the style tags).
* Returns an array of CSS lines, or null if no style block found.
*
* Handles three shapes of `<style data-impeccable-css="ID" ...>`:
* 1. Self-closing: `<style ... />` — no body; return null (nothing to carbonize).
* 2. Same-line open+close: `<style>...</style>` — return the inner content.
* 3. Multi-line: `<style>` on one line, `</style>` on a later line — return
* the lines between them.
*/
function extractCss(lines, block, id) {
const styleAttr = 'data-impeccable-css="' + id + '"';
@@ -287,6 +313,14 @@ function extractCss(lines, block, id) {
const line = lines[i];
if (!inStyle && line.includes(styleAttr)) {
// Self-closing: nothing to carbonize.
if (/<style\b[^>]*\/\s*>/.test(line)) return null;
// Same-line open + close: extract inner text.
const sameLine = line.match(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/);
if (sameLine) {
const inner = sameLine[1];
return inner.length > 0 ? inner.split('\n') : null;
}
inStyle = true;
continue; // skip the <style> opening tag
}
@@ -51,6 +51,16 @@ Editorial has permission for Committed, Full palette, and Drenched strategies. U
- Don't center everything. Left-aligned in asymmetric compositions feels more designed.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` — breakpoint-free responsiveness.
## Imagery
Editorial register leans on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
- **For greenfield work without local assets, reach for stock imagery** from Unsplash (`https://images.unsplash.com/photo-{id}?w=…&q=80`), Pexels, or similar. A well-chosen Unsplash photo is a valid deliverable — colored placeholder blocks are not.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Don't stop at zero** when the brief implies imagery. A moto forum without motorcycle photos, a restaurant without food, a hotel without a view — these read as stubs, not as editorial restraint.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions.
+99 -65
View File
@@ -192,91 +192,117 @@ function findMarkerBlock(id, lines) {
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines (still indented as stored in the wrapper).
*
* CSS inside a <style> block can reference `data-impeccable-variant="N"` via
* `@scope`, which would falsely match the HTML div we're looking for — so skip
* style regions entirely.
* Join wrapper lines into a single string with `<style>` elements removed so
* marker matching and div-depth tracking aren't confused by:
* - CSS `@scope ([data-impeccable-variant="N"])` strings that look like the
* HTML marker we're searching for
* - JSX self-closing `<style ... />` (no separate `</style>` to close on)
* - Same-line `<style>…</style>` blocks
* - Multi-line `<style>\n…\n</style>` blocks
*/
function extractOriginal(lines, block) {
let inOriginal = false;
function stripStyleAndJoin(lines, block) {
const out = [];
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
let line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inStyle) {
// Strip any complete <style> elements on this line (self-closed or
// same-line-closed), including their body content.
line = line
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/g, '')
.replace(/<style\b[^>]*\/\s*>/g, '');
if (!inOriginal && line.includes('data-impeccable-variant="original"')) {
inOriginal = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="original">
}
if (inOriginal) {
// Count div opens/closes to find the matching </div>
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // this is the closing </div> of the original wrapper
content.push(line);
// If a <style> opener remains (multi-line body starts here), strip from
// the opener to end-of-line and flip into skip mode.
const openerIdx = line.search(/<style\b/);
if (openerIdx !== -1) {
line = line.slice(0, openerIdx);
inStyle = true;
}
out.push(line);
} else {
// In multi-line style body; drop everything until we see </style>.
const closeIdx = line.search(/<\/style\s*>/);
if (closeIdx !== -1) {
inStyle = false;
out.push(line.slice(closeIdx).replace(/<\/style\s*>/, ''));
}
// else: skip line entirely
}
}
return out.join('\n');
}
return content;
/**
* Find the inner content of `<TAG ...attrMatch...>…</TAG>` inside `text`,
* handling nested same-tag elements via depth counting. `attrMatch` is a
* regex source fragment that must appear inside the opener tag.
* Returns the inner string (may be empty), or null if not found.
*/
function extractInnerByAttr(text, attrMatch) {
const openerRe = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*' + attrMatch + '[^>]*>');
const openMatch = text.match(openerRe);
if (!openMatch) return null;
const tagName = openMatch[1];
const innerStart = openMatch.index + openMatch[0].length;
// Match any opener or closer of this tag name after innerStart.
// (Does not match self-closing <TAG … />, which doesn't contribute to depth.)
const tagRe = new RegExp('<(?:/)?' + tagName + '\\b[^>]*>', 'g');
tagRe.lastIndex = innerStart;
let depth = 1;
let m;
while ((m = tagRe.exec(text))) {
const isClose = m[0].startsWith('</');
const isSelfClose = !isClose && /\/\s*>$/.test(m[0]);
if (isClose) {
depth--;
if (depth === 0) return text.slice(innerStart, m.index);
} else if (!isSelfClose) {
depth++;
}
}
return null;
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines.
*/
function extractOriginal(lines, block) {
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="original"');
if (inner === null) return [];
return inner.split('\n');
}
/**
* Extract a specific variant's inner content (stripping the wrapper div).
* Returns an array of lines, or null if not found.
*
* Skip <style> blocks — see extractOriginal for why.
*/
function extractVariant(lines, block, variantNum) {
let inVariant = false;
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inVariant && line.includes('data-impeccable-variant="' + variantNum + '"')) {
inVariant = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="N">
}
if (inVariant) {
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // closing </div> of the variant wrapper
content.push(line);
}
}
return content.length > 0 ? content : null;
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="' + variantNum + '"');
if (inner === null) return null;
const result = inner.split('\n');
// Collapse a lone empty leading/trailing line (common after string splice).
while (result.length > 1 && result[0].trim() === '') result.shift();
while (result.length > 1 && result[result.length - 1].trim() === '') result.pop();
return result.length > 0 ? result : null;
}
/**
* Extract the colocated <style> block content (between the style tags).
* Returns an array of CSS lines, or null if no style block found.
*
* Handles three shapes of `<style data-impeccable-css="ID" ...>`:
* 1. Self-closing: `<style ... />` — no body; return null (nothing to carbonize).
* 2. Same-line open+close: `<style>...</style>` — return the inner content.
* 3. Multi-line: `<style>` on one line, `</style>` on a later line — return
* the lines between them.
*/
function extractCss(lines, block, id) {
const styleAttr = 'data-impeccable-css="' + id + '"';
@@ -287,6 +313,14 @@ function extractCss(lines, block, id) {
const line = lines[i];
if (!inStyle && line.includes(styleAttr)) {
// Self-closing: nothing to carbonize.
if (/<style\b[^>]*\/\s*>/.test(line)) return null;
// Same-line open + close: extract inner text.
const sameLine = line.match(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/);
if (sameLine) {
const inner = sameLine[1];
return inner.length > 0 ? inner.split('\n') : null;
}
inStyle = true;
continue; // skip the <style> opening tag
}
@@ -51,6 +51,16 @@ Editorial has permission for Committed, Full palette, and Drenched strategies. U
- Don't center everything. Left-aligned in asymmetric compositions feels more designed.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` — breakpoint-free responsiveness.
## Imagery
Editorial register leans on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
- **For greenfield work without local assets, reach for stock imagery** from Unsplash (`https://images.unsplash.com/photo-{id}?w=…&q=80`), Pexels, or similar. A well-chosen Unsplash photo is a valid deliverable — colored placeholder blocks are not.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Don't stop at zero** when the brief implies imagery. A moto forum without motorcycle photos, a restaurant without food, a hotel without a view — these read as stubs, not as editorial restraint.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions.
@@ -192,91 +192,117 @@ function findMarkerBlock(id, lines) {
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines (still indented as stored in the wrapper).
*
* CSS inside a <style> block can reference `data-impeccable-variant="N"` via
* `@scope`, which would falsely match the HTML div we're looking for — so skip
* style regions entirely.
* Join wrapper lines into a single string with `<style>` elements removed so
* marker matching and div-depth tracking aren't confused by:
* - CSS `@scope ([data-impeccable-variant="N"])` strings that look like the
* HTML marker we're searching for
* - JSX self-closing `<style ... />` (no separate `</style>` to close on)
* - Same-line `<style>…</style>` blocks
* - Multi-line `<style>\n…\n</style>` blocks
*/
function extractOriginal(lines, block) {
let inOriginal = false;
function stripStyleAndJoin(lines, block) {
const out = [];
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
let line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inStyle) {
// Strip any complete <style> elements on this line (self-closed or
// same-line-closed), including their body content.
line = line
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/g, '')
.replace(/<style\b[^>]*\/\s*>/g, '');
if (!inOriginal && line.includes('data-impeccable-variant="original"')) {
inOriginal = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="original">
}
if (inOriginal) {
// Count div opens/closes to find the matching </div>
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // this is the closing </div> of the original wrapper
content.push(line);
// If a <style> opener remains (multi-line body starts here), strip from
// the opener to end-of-line and flip into skip mode.
const openerIdx = line.search(/<style\b/);
if (openerIdx !== -1) {
line = line.slice(0, openerIdx);
inStyle = true;
}
out.push(line);
} else {
// In multi-line style body; drop everything until we see </style>.
const closeIdx = line.search(/<\/style\s*>/);
if (closeIdx !== -1) {
inStyle = false;
out.push(line.slice(closeIdx).replace(/<\/style\s*>/, ''));
}
// else: skip line entirely
}
}
return out.join('\n');
}
return content;
/**
* Find the inner content of `<TAG ...attrMatch...>…</TAG>` inside `text`,
* handling nested same-tag elements via depth counting. `attrMatch` is a
* regex source fragment that must appear inside the opener tag.
* Returns the inner string (may be empty), or null if not found.
*/
function extractInnerByAttr(text, attrMatch) {
const openerRe = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*' + attrMatch + '[^>]*>');
const openMatch = text.match(openerRe);
if (!openMatch) return null;
const tagName = openMatch[1];
const innerStart = openMatch.index + openMatch[0].length;
// Match any opener or closer of this tag name after innerStart.
// (Does not match self-closing <TAG … />, which doesn't contribute to depth.)
const tagRe = new RegExp('<(?:/)?' + tagName + '\\b[^>]*>', 'g');
tagRe.lastIndex = innerStart;
let depth = 1;
let m;
while ((m = tagRe.exec(text))) {
const isClose = m[0].startsWith('</');
const isSelfClose = !isClose && /\/\s*>$/.test(m[0]);
if (isClose) {
depth--;
if (depth === 0) return text.slice(innerStart, m.index);
} else if (!isSelfClose) {
depth++;
}
}
return null;
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines.
*/
function extractOriginal(lines, block) {
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="original"');
if (inner === null) return [];
return inner.split('\n');
}
/**
* Extract a specific variant's inner content (stripping the wrapper div).
* Returns an array of lines, or null if not found.
*
* Skip <style> blocks — see extractOriginal for why.
*/
function extractVariant(lines, block, variantNum) {
let inVariant = false;
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inVariant && line.includes('data-impeccable-variant="' + variantNum + '"')) {
inVariant = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="N">
}
if (inVariant) {
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // closing </div> of the variant wrapper
content.push(line);
}
}
return content.length > 0 ? content : null;
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="' + variantNum + '"');
if (inner === null) return null;
const result = inner.split('\n');
// Collapse a lone empty leading/trailing line (common after string splice).
while (result.length > 1 && result[0].trim() === '') result.shift();
while (result.length > 1 && result[result.length - 1].trim() === '') result.pop();
return result.length > 0 ? result : null;
}
/**
* Extract the colocated <style> block content (between the style tags).
* Returns an array of CSS lines, or null if no style block found.
*
* Handles three shapes of `<style data-impeccable-css="ID" ...>`:
* 1. Self-closing: `<style ... />` — no body; return null (nothing to carbonize).
* 2. Same-line open+close: `<style>...</style>` — return the inner content.
* 3. Multi-line: `<style>` on one line, `</style>` on a later line — return
* the lines between them.
*/
function extractCss(lines, block, id) {
const styleAttr = 'data-impeccable-css="' + id + '"';
@@ -287,6 +313,14 @@ function extractCss(lines, block, id) {
const line = lines[i];
if (!inStyle && line.includes(styleAttr)) {
// Self-closing: nothing to carbonize.
if (/<style\b[^>]*\/\s*>/.test(line)) return null;
// Same-line open + close: extract inner text.
const sameLine = line.match(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/);
if (sameLine) {
const inner = sameLine[1];
return inner.length > 0 ? inner.split('\n') : null;
}
inStyle = true;
continue; // skip the <style> opening tag
}
@@ -51,6 +51,16 @@ Editorial has permission for Committed, Full palette, and Drenched strategies. U
- Don't center everything. Left-aligned in asymmetric compositions feels more designed.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` — breakpoint-free responsiveness.
## Imagery
Editorial register leans on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
- **For greenfield work without local assets, reach for stock imagery** from Unsplash (`https://images.unsplash.com/photo-{id}?w=…&q=80`), Pexels, or similar. A well-chosen Unsplash photo is a valid deliverable — colored placeholder blocks are not.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Don't stop at zero** when the brief implies imagery. A moto forum without motorcycle photos, a restaurant without food, a hotel without a view — these read as stubs, not as editorial restraint.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions.
+99 -65
View File
@@ -192,91 +192,117 @@ function findMarkerBlock(id, lines) {
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines (still indented as stored in the wrapper).
*
* CSS inside a <style> block can reference `data-impeccable-variant="N"` via
* `@scope`, which would falsely match the HTML div we're looking for — so skip
* style regions entirely.
* Join wrapper lines into a single string with `<style>` elements removed so
* marker matching and div-depth tracking aren't confused by:
* - CSS `@scope ([data-impeccable-variant="N"])` strings that look like the
* HTML marker we're searching for
* - JSX self-closing `<style ... />` (no separate `</style>` to close on)
* - Same-line `<style>…</style>` blocks
* - Multi-line `<style>\n…\n</style>` blocks
*/
function extractOriginal(lines, block) {
let inOriginal = false;
function stripStyleAndJoin(lines, block) {
const out = [];
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
let line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inStyle) {
// Strip any complete <style> elements on this line (self-closed or
// same-line-closed), including their body content.
line = line
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/g, '')
.replace(/<style\b[^>]*\/\s*>/g, '');
if (!inOriginal && line.includes('data-impeccable-variant="original"')) {
inOriginal = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="original">
}
if (inOriginal) {
// Count div opens/closes to find the matching </div>
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // this is the closing </div> of the original wrapper
content.push(line);
// If a <style> opener remains (multi-line body starts here), strip from
// the opener to end-of-line and flip into skip mode.
const openerIdx = line.search(/<style\b/);
if (openerIdx !== -1) {
line = line.slice(0, openerIdx);
inStyle = true;
}
out.push(line);
} else {
// In multi-line style body; drop everything until we see </style>.
const closeIdx = line.search(/<\/style\s*>/);
if (closeIdx !== -1) {
inStyle = false;
out.push(line.slice(closeIdx).replace(/<\/style\s*>/, ''));
}
// else: skip line entirely
}
}
return out.join('\n');
}
return content;
/**
* Find the inner content of `<TAG ...attrMatch...>…</TAG>` inside `text`,
* handling nested same-tag elements via depth counting. `attrMatch` is a
* regex source fragment that must appear inside the opener tag.
* Returns the inner string (may be empty), or null if not found.
*/
function extractInnerByAttr(text, attrMatch) {
const openerRe = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*' + attrMatch + '[^>]*>');
const openMatch = text.match(openerRe);
if (!openMatch) return null;
const tagName = openMatch[1];
const innerStart = openMatch.index + openMatch[0].length;
// Match any opener or closer of this tag name after innerStart.
// (Does not match self-closing <TAG … />, which doesn't contribute to depth.)
const tagRe = new RegExp('<(?:/)?' + tagName + '\\b[^>]*>', 'g');
tagRe.lastIndex = innerStart;
let depth = 1;
let m;
while ((m = tagRe.exec(text))) {
const isClose = m[0].startsWith('</');
const isSelfClose = !isClose && /\/\s*>$/.test(m[0]);
if (isClose) {
depth--;
if (depth === 0) return text.slice(innerStart, m.index);
} else if (!isSelfClose) {
depth++;
}
}
return null;
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines.
*/
function extractOriginal(lines, block) {
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="original"');
if (inner === null) return [];
return inner.split('\n');
}
/**
* Extract a specific variant's inner content (stripping the wrapper div).
* Returns an array of lines, or null if not found.
*
* Skip <style> blocks — see extractOriginal for why.
*/
function extractVariant(lines, block, variantNum) {
let inVariant = false;
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inVariant && line.includes('data-impeccable-variant="' + variantNum + '"')) {
inVariant = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="N">
}
if (inVariant) {
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // closing </div> of the variant wrapper
content.push(line);
}
}
return content.length > 0 ? content : null;
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="' + variantNum + '"');
if (inner === null) return null;
const result = inner.split('\n');
// Collapse a lone empty leading/trailing line (common after string splice).
while (result.length > 1 && result[0].trim() === '') result.shift();
while (result.length > 1 && result[result.length - 1].trim() === '') result.pop();
return result.length > 0 ? result : null;
}
/**
* Extract the colocated <style> block content (between the style tags).
* Returns an array of CSS lines, or null if no style block found.
*
* Handles three shapes of `<style data-impeccable-css="ID" ...>`:
* 1. Self-closing: `<style ... />` — no body; return null (nothing to carbonize).
* 2. Same-line open+close: `<style>...</style>` — return the inner content.
* 3. Multi-line: `<style>` on one line, `</style>` on a later line — return
* the lines between them.
*/
function extractCss(lines, block, id) {
const styleAttr = 'data-impeccable-css="' + id + '"';
@@ -287,6 +313,14 @@ function extractCss(lines, block, id) {
const line = lines[i];
if (!inStyle && line.includes(styleAttr)) {
// Self-closing: nothing to carbonize.
if (/<style\b[^>]*\/\s*>/.test(line)) return null;
// Same-line open + close: extract inner text.
const sameLine = line.match(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/);
if (sameLine) {
const inner = sameLine[1];
return inner.length > 0 ? inner.split('\n') : null;
}
inStyle = true;
continue; // skip the <style> opening tag
}
@@ -51,6 +51,16 @@ Editorial has permission for Committed, Full palette, and Drenched strategies. U
- Don't center everything. Left-aligned in asymmetric compositions feels more designed.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` — breakpoint-free responsiveness.
## Imagery
Editorial register leans on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
- **For greenfield work without local assets, reach for stock imagery** from Unsplash (`https://images.unsplash.com/photo-{id}?w=…&q=80`), Pexels, or similar. A well-chosen Unsplash photo is a valid deliverable — colored placeholder blocks are not.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Don't stop at zero** when the brief implies imagery. A moto forum without motorcycle photos, a restaurant without food, a hotel without a view — these read as stubs, not as editorial restraint.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions.
@@ -192,91 +192,117 @@ function findMarkerBlock(id, lines) {
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines (still indented as stored in the wrapper).
*
* CSS inside a <style> block can reference `data-impeccable-variant="N"` via
* `@scope`, which would falsely match the HTML div we're looking for — so skip
* style regions entirely.
* Join wrapper lines into a single string with `<style>` elements removed so
* marker matching and div-depth tracking aren't confused by:
* - CSS `@scope ([data-impeccable-variant="N"])` strings that look like the
* HTML marker we're searching for
* - JSX self-closing `<style ... />` (no separate `</style>` to close on)
* - Same-line `<style>…</style>` blocks
* - Multi-line `<style>\n…\n</style>` blocks
*/
function extractOriginal(lines, block) {
let inOriginal = false;
function stripStyleAndJoin(lines, block) {
const out = [];
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
let line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inStyle) {
// Strip any complete <style> elements on this line (self-closed or
// same-line-closed), including their body content.
line = line
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/g, '')
.replace(/<style\b[^>]*\/\s*>/g, '');
if (!inOriginal && line.includes('data-impeccable-variant="original"')) {
inOriginal = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="original">
}
if (inOriginal) {
// Count div opens/closes to find the matching </div>
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // this is the closing </div> of the original wrapper
content.push(line);
// If a <style> opener remains (multi-line body starts here), strip from
// the opener to end-of-line and flip into skip mode.
const openerIdx = line.search(/<style\b/);
if (openerIdx !== -1) {
line = line.slice(0, openerIdx);
inStyle = true;
}
out.push(line);
} else {
// In multi-line style body; drop everything until we see </style>.
const closeIdx = line.search(/<\/style\s*>/);
if (closeIdx !== -1) {
inStyle = false;
out.push(line.slice(closeIdx).replace(/<\/style\s*>/, ''));
}
// else: skip line entirely
}
}
return out.join('\n');
}
return content;
/**
* Find the inner content of `<TAG ...attrMatch...>…</TAG>` inside `text`,
* handling nested same-tag elements via depth counting. `attrMatch` is a
* regex source fragment that must appear inside the opener tag.
* Returns the inner string (may be empty), or null if not found.
*/
function extractInnerByAttr(text, attrMatch) {
const openerRe = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*' + attrMatch + '[^>]*>');
const openMatch = text.match(openerRe);
if (!openMatch) return null;
const tagName = openMatch[1];
const innerStart = openMatch.index + openMatch[0].length;
// Match any opener or closer of this tag name after innerStart.
// (Does not match self-closing <TAG … />, which doesn't contribute to depth.)
const tagRe = new RegExp('<(?:/)?' + tagName + '\\b[^>]*>', 'g');
tagRe.lastIndex = innerStart;
let depth = 1;
let m;
while ((m = tagRe.exec(text))) {
const isClose = m[0].startsWith('</');
const isSelfClose = !isClose && /\/\s*>$/.test(m[0]);
if (isClose) {
depth--;
if (depth === 0) return text.slice(innerStart, m.index);
} else if (!isSelfClose) {
depth++;
}
}
return null;
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines.
*/
function extractOriginal(lines, block) {
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="original"');
if (inner === null) return [];
return inner.split('\n');
}
/**
* Extract a specific variant's inner content (stripping the wrapper div).
* Returns an array of lines, or null if not found.
*
* Skip <style> blocks — see extractOriginal for why.
*/
function extractVariant(lines, block, variantNum) {
let inVariant = false;
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inVariant && line.includes('data-impeccable-variant="' + variantNum + '"')) {
inVariant = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="N">
}
if (inVariant) {
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // closing </div> of the variant wrapper
content.push(line);
}
}
return content.length > 0 ? content : null;
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="' + variantNum + '"');
if (inner === null) return null;
const result = inner.split('\n');
// Collapse a lone empty leading/trailing line (common after string splice).
while (result.length > 1 && result[0].trim() === '') result.shift();
while (result.length > 1 && result[result.length - 1].trim() === '') result.pop();
return result.length > 0 ? result : null;
}
/**
* Extract the colocated <style> block content (between the style tags).
* Returns an array of CSS lines, or null if no style block found.
*
* Handles three shapes of `<style data-impeccable-css="ID" ...>`:
* 1. Self-closing: `<style ... />` — no body; return null (nothing to carbonize).
* 2. Same-line open+close: `<style>...</style>` — return the inner content.
* 3. Multi-line: `<style>` on one line, `</style>` on a later line — return
* the lines between them.
*/
function extractCss(lines, block, id) {
const styleAttr = 'data-impeccable-css="' + id + '"';
@@ -287,6 +313,14 @@ function extractCss(lines, block, id) {
const line = lines[i];
if (!inStyle && line.includes(styleAttr)) {
// Self-closing: nothing to carbonize.
if (/<style\b[^>]*\/\s*>/.test(line)) return null;
// Same-line open + close: extract inner text.
const sameLine = line.match(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/);
if (sameLine) {
const inner = sameLine[1];
return inner.length > 0 ? inner.split('\n') : null;
}
inStyle = true;
continue; // skip the <style> opening tag
}
@@ -51,6 +51,16 @@ Editorial has permission for Committed, Full palette, and Drenched strategies. U
- Don't center everything. Left-aligned in asymmetric compositions feels more designed.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` — breakpoint-free responsiveness.
## Imagery
Editorial register leans on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
- **For greenfield work without local assets, reach for stock imagery** from Unsplash (`https://images.unsplash.com/photo-{id}?w=…&q=80`), Pexels, or similar. A well-chosen Unsplash photo is a valid deliverable — colored placeholder blocks are not.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Don't stop at zero** when the brief implies imagery. A moto forum without motorcycle photos, a restaurant without food, a hotel without a view — these read as stubs, not as editorial restraint.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions.
@@ -192,91 +192,117 @@ function findMarkerBlock(id, lines) {
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines (still indented as stored in the wrapper).
*
* CSS inside a <style> block can reference `data-impeccable-variant="N"` via
* `@scope`, which would falsely match the HTML div we're looking for so skip
* style regions entirely.
* Join wrapper lines into a single string with `<style>` elements removed so
* marker matching and div-depth tracking aren't confused by:
* - CSS `@scope ([data-impeccable-variant="N"])` strings that look like the
* HTML marker we're searching for
* - JSX self-closing `<style ... />` (no separate `</style>` to close on)
* - Same-line `<style>…</style>` blocks
* - Multi-line `<style>\n\n</style>` blocks
*/
function extractOriginal(lines, block) {
let inOriginal = false;
function stripStyleAndJoin(lines, block) {
const out = [];
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
let line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inStyle) {
// Strip any complete <style> elements on this line (self-closed or
// same-line-closed), including their body content.
line = line
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/g, '')
.replace(/<style\b[^>]*\/\s*>/g, '');
if (!inOriginal && line.includes('data-impeccable-variant="original"')) {
inOriginal = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="original">
}
if (inOriginal) {
// Count div opens/closes to find the matching </div>
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // this is the closing </div> of the original wrapper
content.push(line);
// If a <style> opener remains (multi-line body starts here), strip from
// the opener to end-of-line and flip into skip mode.
const openerIdx = line.search(/<style\b/);
if (openerIdx !== -1) {
line = line.slice(0, openerIdx);
inStyle = true;
}
out.push(line);
} else {
// In multi-line style body; drop everything until we see </style>.
const closeIdx = line.search(/<\/style\s*>/);
if (closeIdx !== -1) {
inStyle = false;
out.push(line.slice(closeIdx).replace(/<\/style\s*>/, ''));
}
// else: skip line entirely
}
}
return out.join('\n');
}
return content;
/**
* Find the inner content of `<TAG ...attrMatch...>…</TAG>` inside `text`,
* handling nested same-tag elements via depth counting. `attrMatch` is a
* regex source fragment that must appear inside the opener tag.
* Returns the inner string (may be empty), or null if not found.
*/
function extractInnerByAttr(text, attrMatch) {
const openerRe = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*' + attrMatch + '[^>]*>');
const openMatch = text.match(openerRe);
if (!openMatch) return null;
const tagName = openMatch[1];
const innerStart = openMatch.index + openMatch[0].length;
// Match any opener or closer of this tag name after innerStart.
// (Does not match self-closing <TAG … />, which doesn't contribute to depth.)
const tagRe = new RegExp('<(?:/)?' + tagName + '\\b[^>]*>', 'g');
tagRe.lastIndex = innerStart;
let depth = 1;
let m;
while ((m = tagRe.exec(text))) {
const isClose = m[0].startsWith('</');
const isSelfClose = !isClose && /\/\s*>$/.test(m[0]);
if (isClose) {
depth--;
if (depth === 0) return text.slice(innerStart, m.index);
} else if (!isSelfClose) {
depth++;
}
}
return null;
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines.
*/
function extractOriginal(lines, block) {
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="original"');
if (inner === null) return [];
return inner.split('\n');
}
/**
* Extract a specific variant's inner content (stripping the wrapper div).
* Returns an array of lines, or null if not found.
*
* Skip <style> blocks see extractOriginal for why.
*/
function extractVariant(lines, block, variantNum) {
let inVariant = false;
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inVariant && line.includes('data-impeccable-variant="' + variantNum + '"')) {
inVariant = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="N">
}
if (inVariant) {
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // closing </div> of the variant wrapper
content.push(line);
}
}
return content.length > 0 ? content : null;
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="' + variantNum + '"');
if (inner === null) return null;
const result = inner.split('\n');
// Collapse a lone empty leading/trailing line (common after string splice).
while (result.length > 1 && result[0].trim() === '') result.shift();
while (result.length > 1 && result[result.length - 1].trim() === '') result.pop();
return result.length > 0 ? result : null;
}
/**
* Extract the colocated <style> block content (between the style tags).
* Returns an array of CSS lines, or null if no style block found.
*
* Handles three shapes of `<style data-impeccable-css="ID" ...>`:
* 1. Self-closing: `<style ... />` no body; return null (nothing to carbonize).
* 2. Same-line open+close: `<style>...</style>` return the inner content.
* 3. Multi-line: `<style>` on one line, `</style>` on a later line return
* the lines between them.
*/
function extractCss(lines, block, id) {
const styleAttr = 'data-impeccable-css="' + id + '"';
@@ -287,6 +313,14 @@ function extractCss(lines, block, id) {
const line = lines[i];
if (!inStyle && line.includes(styleAttr)) {
// Self-closing: nothing to carbonize.
if (/<style\b[^>]*\/\s*>/.test(line)) return null;
// Same-line open + close: extract inner text.
const sameLine = line.match(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/);
if (sameLine) {
const inner = sameLine[1];
return inner.length > 0 ? inner.split('\n') : null;
}
inStyle = true;
continue; // skip the <style> opening tag
}
@@ -51,6 +51,16 @@ Editorial has permission for Committed, Full palette, and Drenched strategies. U
- Don't center everything. Left-aligned in asymmetric compositions feels more designed.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` — breakpoint-free responsiveness.
## Imagery
Editorial register leans on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
- **For greenfield work without local assets, reach for stock imagery** from Unsplash (`https://images.unsplash.com/photo-{id}?w=…&q=80`), Pexels, or similar. A well-chosen Unsplash photo is a valid deliverable — colored placeholder blocks are not.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Don't stop at zero** when the brief implies imagery. A moto forum without motorcycle photos, a restaurant without food, a hotel without a view — these read as stubs, not as editorial restraint.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions.
+99 -65
View File
@@ -192,91 +192,117 @@ function findMarkerBlock(id, lines) {
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines (still indented as stored in the wrapper).
*
* CSS inside a <style> block can reference `data-impeccable-variant="N"` via
* `@scope`, which would falsely match the HTML div we're looking for so skip
* style regions entirely.
* Join wrapper lines into a single string with `<style>` elements removed so
* marker matching and div-depth tracking aren't confused by:
* - CSS `@scope ([data-impeccable-variant="N"])` strings that look like the
* HTML marker we're searching for
* - JSX self-closing `<style ... />` (no separate `</style>` to close on)
* - Same-line `<style>…</style>` blocks
* - Multi-line `<style>\n\n</style>` blocks
*/
function extractOriginal(lines, block) {
let inOriginal = false;
function stripStyleAndJoin(lines, block) {
const out = [];
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
let line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inStyle) {
// Strip any complete <style> elements on this line (self-closed or
// same-line-closed), including their body content.
line = line
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/g, '')
.replace(/<style\b[^>]*\/\s*>/g, '');
if (!inOriginal && line.includes('data-impeccable-variant="original"')) {
inOriginal = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="original">
}
if (inOriginal) {
// Count div opens/closes to find the matching </div>
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // this is the closing </div> of the original wrapper
content.push(line);
// If a <style> opener remains (multi-line body starts here), strip from
// the opener to end-of-line and flip into skip mode.
const openerIdx = line.search(/<style\b/);
if (openerIdx !== -1) {
line = line.slice(0, openerIdx);
inStyle = true;
}
out.push(line);
} else {
// In multi-line style body; drop everything until we see </style>.
const closeIdx = line.search(/<\/style\s*>/);
if (closeIdx !== -1) {
inStyle = false;
out.push(line.slice(closeIdx).replace(/<\/style\s*>/, ''));
}
// else: skip line entirely
}
}
return out.join('\n');
}
return content;
/**
* Find the inner content of `<TAG ...attrMatch...>…</TAG>` inside `text`,
* handling nested same-tag elements via depth counting. `attrMatch` is a
* regex source fragment that must appear inside the opener tag.
* Returns the inner string (may be empty), or null if not found.
*/
function extractInnerByAttr(text, attrMatch) {
const openerRe = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*' + attrMatch + '[^>]*>');
const openMatch = text.match(openerRe);
if (!openMatch) return null;
const tagName = openMatch[1];
const innerStart = openMatch.index + openMatch[0].length;
// Match any opener or closer of this tag name after innerStart.
// (Does not match self-closing <TAG … />, which doesn't contribute to depth.)
const tagRe = new RegExp('<(?:/)?' + tagName + '\\b[^>]*>', 'g');
tagRe.lastIndex = innerStart;
let depth = 1;
let m;
while ((m = tagRe.exec(text))) {
const isClose = m[0].startsWith('</');
const isSelfClose = !isClose && /\/\s*>$/.test(m[0]);
if (isClose) {
depth--;
if (depth === 0) return text.slice(innerStart, m.index);
} else if (!isSelfClose) {
depth++;
}
}
return null;
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines.
*/
function extractOriginal(lines, block) {
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="original"');
if (inner === null) return [];
return inner.split('\n');
}
/**
* Extract a specific variant's inner content (stripping the wrapper div).
* Returns an array of lines, or null if not found.
*
* Skip <style> blocks see extractOriginal for why.
*/
function extractVariant(lines, block, variantNum) {
let inVariant = false;
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inVariant && line.includes('data-impeccable-variant="' + variantNum + '"')) {
inVariant = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="N">
}
if (inVariant) {
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // closing </div> of the variant wrapper
content.push(line);
}
}
return content.length > 0 ? content : null;
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="' + variantNum + '"');
if (inner === null) return null;
const result = inner.split('\n');
// Collapse a lone empty leading/trailing line (common after string splice).
while (result.length > 1 && result[0].trim() === '') result.shift();
while (result.length > 1 && result[result.length - 1].trim() === '') result.pop();
return result.length > 0 ? result : null;
}
/**
* Extract the colocated <style> block content (between the style tags).
* Returns an array of CSS lines, or null if no style block found.
*
* Handles three shapes of `<style data-impeccable-css="ID" ...>`:
* 1. Self-closing: `<style ... />` no body; return null (nothing to carbonize).
* 2. Same-line open+close: `<style>...</style>` return the inner content.
* 3. Multi-line: `<style>` on one line, `</style>` on a later line return
* the lines between them.
*/
function extractCss(lines, block, id) {
const styleAttr = 'data-impeccable-css="' + id + '"';
@@ -287,6 +313,14 @@ function extractCss(lines, block, id) {
const line = lines[i];
if (!inStyle && line.includes(styleAttr)) {
// Self-closing: nothing to carbonize.
if (/<style\b[^>]*\/\s*>/.test(line)) return null;
// Same-line open + close: extract inner text.
const sameLine = line.match(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/);
if (sameLine) {
const inner = sameLine[1];
return inner.length > 0 ? inner.split('\n') : null;
}
inStyle = true;
continue; // skip the <style> opening tag
}
+1 -1
View File
@@ -49,7 +49,7 @@
"dev": "bun run server/index.js",
"preview": "bun run build && wrangler pages dev",
"deploy": "bun run build && wrangler pages deploy build/",
"test": "bun test tests/build.test.js tests/detect-antipatterns.test.js && node --test tests/detect-antipatterns-fixtures.test.mjs && node --test tests/detect-antipatterns-browser.test.mjs && node --test tests/cleanup-deprecated.test.mjs && node --test tests/live-wrap.test.mjs && node --test tests/live-server.test.mjs && node --test tests/framework-fixtures.test.mjs",
"test": "bun test tests/build.test.js tests/detect-antipatterns.test.js && node --test tests/detect-antipatterns-fixtures.test.mjs && node --test tests/detect-antipatterns-browser.test.mjs && node --test tests/cleanup-deprecated.test.mjs && node --test tests/live-wrap.test.mjs && node --test tests/live-accept.test.mjs && node --test tests/live-server.test.mjs && node --test tests/framework-fixtures.test.mjs",
"prepack": "cp README.md README.repo.md && cp README.npm.md README.md",
"postpack": "cp README.repo.md README.md && rm README.repo.md",
"screenshot": "bun run scripts/screenshot-antipatterns.js",
@@ -51,6 +51,16 @@ Editorial has permission for Committed, Full palette, and Drenched strategies. U
- Don't center everything. Left-aligned in asymmetric compositions feels more designed.
- When cards ARE the right affordance, use `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` — breakpoint-free responsiveness.
## Imagery
Editorial register leans on imagery. A restaurant, hotel, magazine, or product landing page without any imagery reads as incomplete, not as restrained. A solid-color rectangle where a hero image should go is worse than a representative stock photo.
- **For greenfield work without local assets, reach for stock imagery** from Unsplash (`https://images.unsplash.com/photo-{id}?w=…&q=80`), Pexels, or similar. A well-chosen Unsplash photo is a valid deliverable — colored placeholder blocks are not.
- **Search for the brand's physical object**, not the generic category: "handmade pasta on a scratched wooden table" beats "Italian food"; "cypress trees above a limestone hotel facade at dusk" beats "luxury hotel".
- **One decisive photo beats five mediocre ones.** Hero imagery should commit to a mood; padding with more stock doesn't rescue an indecisive one.
- **Don't stop at zero** when the brief implies imagery. A moto forum without motorcycle photos, a restaurant without food, a hotel without a view — these read as stubs, not as editorial restraint.
- **Alt text is part of the voice.** "Coastal fettuccine, hand-cut, served on the terrace" beats "pasta dish".
## Motion
- One well-orchestrated page-load with staggered reveals beats scattered micro-interactions.
@@ -192,91 +192,117 @@ function findMarkerBlock(id, lines) {
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines (still indented as stored in the wrapper).
*
* CSS inside a <style> block can reference `data-impeccable-variant="N"` via
* `@scope`, which would falsely match the HTML div we're looking for so skip
* style regions entirely.
* Join wrapper lines into a single string with `<style>` elements removed so
* marker matching and div-depth tracking aren't confused by:
* - CSS `@scope ([data-impeccable-variant="N"])` strings that look like the
* HTML marker we're searching for
* - JSX self-closing `<style ... />` (no separate `</style>` to close on)
* - Same-line `<style>…</style>` blocks
* - Multi-line `<style>\n\n</style>` blocks
*/
function extractOriginal(lines, block) {
let inOriginal = false;
function stripStyleAndJoin(lines, block) {
const out = [];
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
let line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inStyle) {
// Strip any complete <style> elements on this line (self-closed or
// same-line-closed), including their body content.
line = line
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/g, '')
.replace(/<style\b[^>]*\/\s*>/g, '');
if (!inOriginal && line.includes('data-impeccable-variant="original"')) {
inOriginal = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="original">
}
if (inOriginal) {
// Count div opens/closes to find the matching </div>
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // this is the closing </div> of the original wrapper
content.push(line);
// If a <style> opener remains (multi-line body starts here), strip from
// the opener to end-of-line and flip into skip mode.
const openerIdx = line.search(/<style\b/);
if (openerIdx !== -1) {
line = line.slice(0, openerIdx);
inStyle = true;
}
out.push(line);
} else {
// In multi-line style body; drop everything until we see </style>.
const closeIdx = line.search(/<\/style\s*>/);
if (closeIdx !== -1) {
inStyle = false;
out.push(line.slice(closeIdx).replace(/<\/style\s*>/, ''));
}
// else: skip line entirely
}
}
return out.join('\n');
}
return content;
/**
* Find the inner content of `<TAG ...attrMatch...>…</TAG>` inside `text`,
* handling nested same-tag elements via depth counting. `attrMatch` is a
* regex source fragment that must appear inside the opener tag.
* Returns the inner string (may be empty), or null if not found.
*/
function extractInnerByAttr(text, attrMatch) {
const openerRe = new RegExp('<([A-Za-z][A-Za-z0-9]*)\\b[^>]*' + attrMatch + '[^>]*>');
const openMatch = text.match(openerRe);
if (!openMatch) return null;
const tagName = openMatch[1];
const innerStart = openMatch.index + openMatch[0].length;
// Match any opener or closer of this tag name after innerStart.
// (Does not match self-closing <TAG … />, which doesn't contribute to depth.)
const tagRe = new RegExp('<(?:/)?' + tagName + '\\b[^>]*>', 'g');
tagRe.lastIndex = innerStart;
let depth = 1;
let m;
while ((m = tagRe.exec(text))) {
const isClose = m[0].startsWith('</');
const isSelfClose = !isClose && /\/\s*>$/.test(m[0]);
if (isClose) {
depth--;
if (depth === 0) return text.slice(innerStart, m.index);
} else if (!isSelfClose) {
depth++;
}
}
return null;
}
/**
* Extract the original element content from within the variant wrapper.
* Returns an array of lines.
*/
function extractOriginal(lines, block) {
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="original"');
if (inner === null) return [];
return inner.split('\n');
}
/**
* Extract a specific variant's inner content (stripping the wrapper div).
* Returns an array of lines, or null if not found.
*
* Skip <style> blocks see extractOriginal for why.
*/
function extractVariant(lines, block, variantNum) {
let inVariant = false;
let inStyle = false;
let depth = 0;
const content = [];
for (let i = block.start; i <= block.end; i++) {
const line = lines[i];
if (!inStyle && /<style[\s>]/.test(line)) { inStyle = true; continue; }
if (inStyle) {
if (line.trimStart().startsWith('</style>')) inStyle = false;
continue;
}
if (!inVariant && line.includes('data-impeccable-variant="' + variantNum + '"')) {
inVariant = true;
depth = 1;
continue; // skip the opening <div data-impeccable-variant="N">
}
if (inVariant) {
const opens = (line.match(/<div[\s>]/g) || []).length;
const closes = (line.match(/<\/div\s*>/g) || []).length;
depth += opens - closes;
if (depth <= 0) break; // closing </div> of the variant wrapper
content.push(line);
}
}
return content.length > 0 ? content : null;
const text = stripStyleAndJoin(lines, block);
const inner = extractInnerByAttr(text, 'data-impeccable-variant="' + variantNum + '"');
if (inner === null) return null;
const result = inner.split('\n');
// Collapse a lone empty leading/trailing line (common after string splice).
while (result.length > 1 && result[0].trim() === '') result.shift();
while (result.length > 1 && result[result.length - 1].trim() === '') result.pop();
return result.length > 0 ? result : null;
}
/**
* Extract the colocated <style> block content (between the style tags).
* Returns an array of CSS lines, or null if no style block found.
*
* Handles three shapes of `<style data-impeccable-css="ID" ...>`:
* 1. Self-closing: `<style ... />` no body; return null (nothing to carbonize).
* 2. Same-line open+close: `<style>...</style>` return the inner content.
* 3. Multi-line: `<style>` on one line, `</style>` on a later line return
* the lines between them.
*/
function extractCss(lines, block, id) {
const styleAttr = 'data-impeccable-css="' + id + '"';
@@ -287,6 +313,14 @@ function extractCss(lines, block, id) {
const line = lines[i];
if (!inStyle && line.includes(styleAttr)) {
// Self-closing: nothing to carbonize.
if (/<style\b[^>]*\/\s*>/.test(line)) return null;
// Same-line open + close: extract inner text.
const sameLine = line.match(/<style\b[^>]*>([\s\S]*?)<\/style\s*>/);
if (sameLine) {
const inner = sameLine[1];
return inner.length > 0 ? inner.split('\n') : null;
}
inStyle = true;
continue; // skip the <style> opening tag
}
+149
View File
@@ -0,0 +1,149 @@
/**
* Tests for live-accept.mjs the deterministic accept/discard helper.
* Run with: node --test tests/live-accept.test.mjs
*/
import { describe, it, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, writeFileSync, readFileSync, rmSync } from 'node:fs';
import { dirname, join, resolve } from 'node:path';
import { tmpdir } from 'node:os';
import { fileURLToPath } from 'node:url';
import { execFileSync } from 'node:child_process';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ACCEPT = resolve(__dirname, '..', 'source/skills/impeccable/scripts/live-accept.mjs');
function runAccept(cwd, args) {
try {
const out = execFileSync('node', [ACCEPT, ...args], {
cwd,
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
});
return JSON.parse(out.trim());
} catch (err) {
const body = err.stdout?.toString().trim() || err.stderr?.toString().trim() || '';
return JSON.parse(body || '{}');
}
}
describe('live-accept — style-element edge cases', () => {
let tmp;
beforeEach(() => { tmp = mkdtempSync(join(tmpdir(), 'impeccable-accept-test-')); });
afterEach(() => { rmSync(tmp, { recursive: true, force: true }); });
// Historical bug: extractVariant flipped into "inStyle" mode on <style and
// scanned for </style> line-by-line. JSX self-closing <style ... /> has no
// separate closer, so it got stuck forever and missed data-impeccable-variant
// divs that came after.
it('finds the accepted variant after a JSX self-closing <style /> block', () => {
const html = `<body>
<!-- impeccable-variants-start SELFC -->
<div data-impeccable-variants="SELFC" data-impeccable-variant-count="3" style="display: contents">
<div data-impeccable-variant="original">
<p class="hook">original text</p>
</div>
<style data-impeccable-css="SELFC" dangerouslySetInnerHTML={{ __html: '@scope ([data-impeccable-variant="1"]) { .hook { color: red; } }' }} />
<div data-impeccable-variant="1">
<p class="hook">variant one</p>
</div>
<div data-impeccable-variant="2" style="display: none">
<p class="hook">variant two</p>
</div>
<div data-impeccable-variant="3" style="display: none">
<p class="hook">variant three</p>
</div>
</div>
<!-- impeccable-variants-end SELFC -->
</body>`;
writeFileSync(join(tmp, 'page.html'), html);
const result = runAccept(tmp, ['--id', 'SELFC', '--variant', '2']);
assert.equal(result.handled, true, `accept should succeed: ${JSON.stringify(result)}`);
const after = readFileSync(join(tmp, 'page.html'), 'utf-8');
// Self-closing style has no extractable CSS body, so there's nothing to carbonize —
// no carbonize block, no data-impeccable-variant wrapper (it would serve no purpose).
assert.ok(!after.includes('impeccable-carbonize-start'), 'no carbonize block (self-closing style has no body)');
assert.ok(!after.includes('impeccable-variants-start'), 'variant markers removed');
assert.ok(after.includes('variant two'), 'variant 2 content kept');
assert.ok(!after.includes('variant three'), 'other variant content dropped');
assert.ok(!after.includes('variant one'), 'other variant content dropped');
assert.ok(!after.includes('original text'), 'original content dropped');
});
// Variant: same-line <style>…</style> block should also be treated as a
// single skipped unit; the line has both open and close tags.
it('finds the accepted variant after a single-line <style>…</style> block', () => {
const html = `<body>
<!-- impeccable-variants-start ONELINE -->
<div data-impeccable-variants="ONELINE" data-impeccable-variant-count="3" style="display: contents">
<div data-impeccable-variant="original"><p class="hook">original</p></div>
<style data-impeccable-css="ONELINE">@scope ([data-impeccable-variant="1"]) { .hook { color: red; } }</style>
<div data-impeccable-variant="1"><p class="hook">variant one</p></div>
<div data-impeccable-variant="2" style="display: none"><p class="hook">variant two</p></div>
<div data-impeccable-variant="3" style="display: none"><p class="hook">variant three</p></div>
</div>
<!-- impeccable-variants-end ONELINE -->
</body>`;
writeFileSync(join(tmp, 'page.html'), html);
const result = runAccept(tmp, ['--id', 'ONELINE', '--variant', '3']);
assert.equal(result.handled, true, `accept should succeed: ${JSON.stringify(result)}`);
const after = readFileSync(join(tmp, 'page.html'), 'utf-8');
assert.ok(after.includes('data-impeccable-variant="3"'), 'accepted wrapper for variant 3 present');
assert.ok(after.includes('variant three'), 'variant 3 content kept');
assert.ok(!after.includes('variant two'), 'other variant content dropped');
});
// Baseline: the standard multi-line <style>...</style> case must keep working.
it('finds the accepted variant after a multi-line <style>…</style> block (regression baseline)', () => {
const html = `<body>
<!-- impeccable-variants-start MULTI -->
<div data-impeccable-variants="MULTI" data-impeccable-variant-count="3" style="display: contents">
<div data-impeccable-variant="original"><p class="hook">original</p></div>
<style data-impeccable-css="MULTI">
@scope ([data-impeccable-variant="1"]) { .hook { color: red; } }
@scope ([data-impeccable-variant="2"]) { .hook { color: green; } }
</style>
<div data-impeccable-variant="1"><p class="hook">variant one</p></div>
<div data-impeccable-variant="2" style="display: none"><p class="hook">variant two</p></div>
</div>
<!-- impeccable-variants-end MULTI -->
</body>`;
writeFileSync(join(tmp, 'page.html'), html);
const result = runAccept(tmp, ['--id', 'MULTI', '--variant', '1']);
assert.equal(result.handled, true, `accept should succeed: ${JSON.stringify(result)}`);
const after = readFileSync(join(tmp, 'page.html'), 'utf-8');
assert.ok(after.includes('data-impeccable-variant="1"'), 'accepted wrapper for variant 1 present');
assert.ok(after.includes('variant one'), 'variant 1 content kept');
});
// Discard must restore the original element after a self-closing <style />,
// proving extractOriginal also survives the style pattern.
it('discard restores the original element after a JSX self-closing <style />', () => {
const html = `<body>
<!-- impeccable-variants-start DISC -->
<div data-impeccable-variants="DISC" data-impeccable-variant-count="2" style="display: contents">
<div data-impeccable-variant="original"><p class="hook">ORIGINAL CONTENT</p></div>
<style data-impeccable-css="DISC" dangerouslySetInnerHTML={{ __html: '@scope ([data-impeccable-variant="1"]) { .hook { color: red; } }' }} />
<div data-impeccable-variant="1"><p class="hook">variant one</p></div>
<div data-impeccable-variant="2" style="display: none"><p class="hook">variant two</p></div>
</div>
<!-- impeccable-variants-end DISC -->
</body>`;
writeFileSync(join(tmp, 'page.html'), html);
const result = runAccept(tmp, ['--id', 'DISC', '--discard']);
assert.equal(result.handled, true, `discard should succeed: ${JSON.stringify(result)}`);
const after = readFileSync(join(tmp, 'page.html'), 'utf-8');
assert.ok(after.includes('ORIGINAL CONTENT'), 'original restored');
assert.ok(!after.includes('impeccable-variants-start'), 'wrapper markers gone');
assert.ok(!after.includes('variant one'), 'variants dropped');
});
});