similarbuild 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/package.json +1 -1
  2. package/templates/commands/build-page.md +62 -493
  3. package/templates/commands/build-site.md +98 -705
  4. package/templates/commands/clip-section.md +102 -501
  5. package/templates/skills/sb-build-wp/SKILL.md +37 -164
  6. package/templates/skills/sb-build-wp/references/wp-build-rules.md +11 -1
  7. package/templates/skills/sb-inspect-live/scripts/inspect-live.mjs +4 -367
  8. package/templates/skills/sb-review-checks/SKILL.md +0 -113
  9. package/templates/skills/sb-review-checks/references/review-rules.md +0 -195
  10. package/templates/skills/sb-review-checks/scripts/lib/anti-patterns.mjs +0 -385
  11. package/templates/skills/sb-review-checks/scripts/lib/cross-reference.mjs +0 -115
  12. package/templates/skills/sb-review-checks/scripts/lib/design-quality.mjs +0 -541
  13. package/templates/skills/sb-review-checks/scripts/review-checks.mjs +0 -250
  14. package/templates/skills/sb-review-checks/scripts/tests/test-anti-patterns.mjs +0 -366
  15. package/templates/skills/sb-review-checks/scripts/tests/test-cross-reference.mjs +0 -170
  16. package/templates/skills/sb-review-checks/scripts/tests/test-design-quality.mjs +0 -493
  17. package/templates/skills/sb-review-checks/scripts/tests/test-review-checks.mjs +0 -267
  18. package/templates/skills/sb-test-interactivity/SKILL.md +0 -133
  19. package/templates/skills/sb-test-interactivity/scripts/test-interactivity.mjs +0 -970
  20. package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/aria-controls-broken.html +0 -32
  21. package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/aria-controls-good.html +0 -47
  22. package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/deferred-listeners.html +0 -67
  23. package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/details-good.html +0 -25
  24. package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/dialog-good.html +0 -38
  25. package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/no-interactive.html +0 -15
  26. package/templates/skills/sb-test-interactivity/scripts/tests/test-test-interactivity.mjs +0 -223
@@ -5,191 +5,64 @@ description: Composes a standalone WordPress/Elementor HTML fragment from a live
5
5
 
6
6
  # sb-build-wp
7
7
 
8
- ## Overview
8
+ ## Mental model
9
9
 
10
- Produces the single `.html` fragment a user pastes into an Elementor HTML widget a `<style>` block scoped to one root class, the matching markup, and (when the section needs interactivity) a small `<script>` block. The fragment must survive Elementor's container constraints and the active theme's `!important`-laced overrides without any post-paste tweaking. Production-grade from the first build: every image has alt + correct loading hint, hero is preloaded, fonts use `display=swap`, buttons are `type="button"`, headings don't skip levels.
10
+ Pixel parity over architecture. Read the section reference screenshot, build HTML+CSS+JS that visually matches it, render the result, compare, adjust. The screenshot IS the spec not the DOM, not rule cascades, not classification taxonomies.
11
11
 
12
- Unlike the upstream skills (`sb-inspect-live`, `sb-extract-assets`) where a script does ~95% of the work, **this skill is creative composition**. The LLM picks the pattern, names the classes, structures the markup, and decides where to apply defensive specificity. The `scripts/build-wp.mjs` helper handles only the deterministic plumbing around the composition: structural validation of the produced HTML and formatted persistence to disk.
12
+ ## Overview
13
13
 
14
- Act as a WordPress/Elementor specialist. You know exactly which theme overrides bite (font-family, font-size, font-weight, color via `.elementor *` blanket rules; `img { height: auto !important }`; button background reset) and you defend against all of them with chained-scope selectors and surgical `!important` on the four font properties only. You do NOT touch Liquid, Shopify schema, or any other target — that's a different skill's job.
14
+ Composes ONE section (or a full page assembled from sections) as a standalone HTML fragment ready to paste into Elementor as a Custom HTML widget. The fragment is self-contained: scoped `<style>` block + markup + optional `<script>`. No external imports beyond Google Fonts.
15
15
 
16
16
  ## Inputs
17
17
 
18
- | Argument | Required | Notes |
19
- | ----------------- | -------- | --------------------------------------------------------------------------------------------- |
20
- | `inspection` | yes | Path to `inspection.json` from `sb-inspect-live`. Drives section type, tokens, DOM structure. |
21
- | `assetsMap` | yes | Path to `assets-map.json` from `sb-extract-assets`. Source of every `<img src>` and inline SVG. |
22
- | `outputPath` | yes | Where the final `.html` is written (parent dirs created automatically). |
23
- | `designKnowledge` | no | Subset of `<plugin>/memory/design-knowledge.md` filtered by the orchestrator (a11y/perf rules relevant to this section). When absent, use the defaults in `references/wp-build-rules.md`. |
24
- | `fixHints` | no | JSON array from `sb-review-checks` after a failed validation cycle. When present, you are patching a previous build, not building from scratch. |
25
- | `previousHtmlPath` | no | Path to the previous build (only meaningful with `fixHints`). Read it, apply the targeted patches, do not refactor untouched code. |
18
+ The orchestrator passes the path to an inspection bundle. The composer reads:
26
19
 
27
- The orchestrator passes these as concrete paths or inline values there is no CLI for this skill, the LLM drives.
20
+ - `inspection.json` `tokens` (typography + colors + layout), `dom` (light DOM walker), `imgUrls`, `sectionCrops[]` (one PNG per visual band, native resolution)
21
+ - `assets-map.json` — URL → localPath / inline-SVG dictionary produced by `sb-extract-assets`
22
+ - `referenceImagePath` — the reference screenshot for the section being composed (one of `inspection.sectionCrops[i].path`)
23
+ - (optional) `previousHtmlPath` + `fixHints` — for retry iterations after a render+diff pass
28
24
 
29
25
  ## Output
30
26
 
31
- A single `.html` file written to `outputPath`. The file is a fragment — no `<html>`, `<head>`, or `<body>` wrappers — containing in order:
32
-
33
- 1. Optional `<link rel="preconnect">` (fonts.googleapis.com, fonts.gstatic.com), `<link rel="preload" as="image">` for the hero, `<link rel="stylesheet">` for the Google Font.
34
- 2. A `<style>` block: universal reset scope container (full-bleed if applicable) → mobile-first element styles with chained-scope selectors → `@media (min-width: 1000px)` desktop overrides.
35
- 3. The scoped markup (`<section class="{scope}">...</section>` or appropriate semantic root).
36
- 4. Optional `<script>` block for vanilla JS interactivity.
37
-
38
- ## On Activation
39
-
40
- 1. **Read the inputs.** Parse `inspection.json` (capture `sectionType`, `tokens`, `dom`, **`domLive`**, `pseudoElements`, `imgUrls`, **`hydratedHeader`**, **`hydratedFooter`**, and **`sectionCrops[]`**) and `assets-map.json` (the URL → localPath / inline-SVG dictionary). If `fixHints` is given, also read `previousHtmlPath`.
41
-
42
- **CANONICAL VISUAL INPUT = `sectionCrops[]`.** Do NOT read `inspection.screenshot` (full-page) — it's downscaled when loaded as image and digits become unreadable. The crops are HD-readable per section. Composer uses one crop per section it composes.
43
-
44
- **§V03-4 — TERNARY section classification + ZERO-FABRICATION HARD ENFORCEMENT (replaces §V03-3).** Before composing ANY section, classify it into ONE of five categories. Pick the FIRST category that matches. Each category has a distinct compose recipe. Default = placeholder is FORBIDDEN; placeholder is only the last-resort for category (E).
45
-
46
- **Workflow when composing a body page** (anything NOT `--target-section=header|footer`):
47
-
48
- 1. **Find the section's crop.** For each section you compose, find the matching `inspection.sectionCrops[]` entry by `bbox.y` proximity. Read the crop via `Read({ file_path: cropEntry.path })`.
49
-
50
- 2. **Classify the section** into A / B / C / D / E:
51
-
52
- **(A) IMAGE-BLOCK** — the section is dominated by a single `<img>` element. Detect by either:
53
- - `inspection.domLive` (or `dom`) — the section's root node has a child `<img>` whose `bbox` covers ≥80% of the section's `bbox`, OR
54
- - Wrapper class match: ancestor has class containing `product-info__image`, `image-with-text`, `hero-image`, `trust-badges`, `features-points`, `mothers-day`, `single-image`, `banner-image`, or similar "single asset" pattern.
55
- - In Shopify Dawn/OS 2.0: **`<div class="product-info__image">` is THE canonical flag** — every match is an image-block.
56
-
57
- Recipe: **DO NOT reproduce the image's contents in HTML/CSS.** Emit:
58
- ```html
59
- <section class="es-{section-slug}">
60
- <img src="{assetsMap.assets[url].localPath}" alt="{img.alt || section description}" loading="lazy" width="{img.width}" height="{img.height}" />
61
- </section>
62
- ```
63
- Pull the URL from `inspection.imgUrls` matching the section's bbox / src pattern. If asset wasn't downloaded (failed extract or didn't appear in imgUrls), emit `<img src="" alt="..." data-todo="asset-missing">` + `<!-- TODO: image asset not in assetsMap (URL: …) — download manually -->`. No CSS reproduction. No SVG re-creation. No text "interpretation" of what's inside the image.
64
-
65
- **Examples from real feedback (everstride PDP)**: `mothers-day-new.svg` banner, `trust-badges-shipping.svg`, `features-points.svg`, `60-day-fit.svg` cards — all category (A).
66
-
67
- **(B) PURE MARKUP** — the section is text + structured layout, no dominant image. Examples: FAQ accordion (`<details>/<summary>`), comparison tables (`<table>` with cells), pricing tiers, trust copy blocks, headings with paragraphs.
68
-
69
- Recipe: READ the crop carefully + cross-validate every literal against `inspection.domLive` text nodes. Emit real semantic markup (`<table>`, `<dl>`, `<details>`, `<ul>` of `<li>`, etc.) that mirrors the live DOM structure.
70
-
71
- **Cross-validation is MANDATORY for every literal**:
72
- - Numeric (`$NN`, `NN reviews`, percentages, ratings): grep `inspection.domLive` recursively for a text node containing the exact substring. NOT found → emit `<!-- TODO: literal "$29.00" read from crop but not in DOM; verify -->` + placeholder.
73
- - Headings/copy/labels: same rule. "What Customers Say", "All rights reserved", "Mix & Match Colors" — if not in DOM verbatim, do NOT emit.
74
- - Counts: count items visible in crop AND cross-validate against `domLive` children of the container. If mismatch, prefer DOM count (DOM is authoritative for structure) + log discrepancy.
75
-
76
- **(C) MIXED (image + text)** — the section has BOTH a meaningful image AND text content (heading + body paragraph + image side-by-side). Common Shopify pattern: `<images-with-text-scrolling>` custom element.
77
-
78
- Recipe: emit `<section>` with `<img>` (rule A for the image part — use real asset URL when available) + `<h*>` heading + `<p>` paragraph(s) literal from crop, cross-validated. Do NOT collapse to image-only or text-only — preserve both.
79
-
80
- **(D) WIDGET-RENDERED** — third-party widget (Loox reviews, "you may also like" carousel, Instagram feed) where the crop SHOWS the widget already rendered with content (reviews with names + photos + stars + quotes; product cards with prices + titles).
81
-
82
- Recipe: READ the crop and emit real markup for each visible card/item. Cross-validate against DOM when widget exposes content as JSON-LD or hidden HTML. Mark container with `data-source="widget-name"` so the deploy team knows where to integrate the real widget later. **Do NOT emit empty mount-div when content is visible.** The "deploy time placeholder" excuse is ONLY for (E).
83
-
84
- **(E) WIDGET-EMPTY / OPAQUE** — the crop is blank, only shows "Loading..." text, or the section is completely unreadable (zero text visible, no images, no structure). Last resort.
85
-
86
- Recipe: emit STRUCTURAL placeholder (heading from DOM if available + `<div class="placeholder-grid">` with N visual placeholder-cards) + `<!-- TODO: <widget-name or section-description> — content not visible in crop, integrate at deploy -->`. Mount-div alone (empty `<div>`) is FORBIDDEN — placeholder must have visible structure so the deployed page is not visually broken.
87
-
88
- 3. **Hybridize sources by field type** (applies to B, C, D):
89
- - **Texts/structure/counts** → crop, cross-validated against DOM.
90
- - **Hrefs** → `inspection.domLive` / `hydratedHeader` / `hydratedFooter` / `imgUrls` by matching visible link text. No match → `href="#"` + TODO.
91
- - **Form action/method/hidden inputs** → ALWAYS DOM (never guess; broken submission else).
92
- - **Image src** → `assetsMap.assets[url].localPath` by alt/src pattern matching imgUrls.
93
- - **Computed style** → `inspection.tokens` + `domLive.computedStyle`.
94
-
95
- 4. **Self-validation BEFORE calling `build-wp.mjs write`** — scan composed HTML for fabrication signals:
96
- - Every numeric/price literal must either appear in `inspection.domLive` text nodes OR have a nearby TODO comment.
97
- - Every `<h*>` / heading text must appear in DOM verbatim OR have TODO.
98
- - Every `<p>` text > 30 chars must appear in DOM verbatim OR have TODO.
99
- - Every FAQ `<div class="faq-a">` body MUST be either (a) empty + TODO when accordion was closed, OR (b) verbatim from crop where accordion was open.
100
- - Image-block sections (A) MUST NOT contain SVG reproductions or CSS-styled `<div>` mimicking the image — they MUST be `<img>` + nothing else inside the section.
101
- If any violation → REWRITE before submit. If can't pass after 1 rewrite, return preflight error `composition-fabrication-detected` for orchestrator Step 4j retry.
102
-
103
- 5. **Hard-fail after 2 attempts.** Orchestrator (Step 4j) caps at 2 compose iterations. 2nd iteration still flagged → page goes to **❌** with TODO-only fragment shipped (not as "ready" or "partial"). NEVER plausible-but-wrong shipped as warning.
104
-
105
- 6. **NEVER complete content from training data.** You may have seen this URL during training. That data is OLD. The crop is NEW. Crop wins. Training data is IRRELEVANT — never fill gaps with "what this site probably has".
106
-
107
- This contract enforces the user's hard requirement: "não pode me entregar algo diferente e se não conseguir falha na 2 vez."
108
-
109
- **§V03-1 — Use `domLive` as the canonical body tree when present.** When `inspection.domLive` is non-null, it holds the live-walker snapshot taken BEFORE Cap A substituted `dom[]` with the shadow-flattened tree. The flattened `dom[]` carries `bbox={0,0,0,0}` and empty `computedStyle` because parseHTMLUnsafe returns a detached doc — it's structurally rich (gallery imgs, custom-element children) but useless for layout. The composer needs real bboxes, real `computedStyle.background`, real heights. Always prefer `inspection.domLive` for body section composition (bbox, computedStyle, hero detection, section ordering). Use `inspection.dom` only when `domLive === null` (page had no shadow roots — flatten didn't fire) or when you specifically need shadow-flattened content like a PDP gallery (consult `dom` for image-rich PDP nodes, but use `domLive` for the surrounding layout).
110
-
111
- **§V03-0a — `hydratedHeader` / `hydratedFooter` payload.** When `inspect-live` captures the page chrome WHILE THE BOTTOM IS IN VIEW, it snapshots the hydrated HTML before Shopify themes tear menus down on intersection-out. The payload is the canonical source of truth for header/footer composition — prefer it over `dom[]` subtrees, which may be empty `<ul>` shells if the live walker ran after tear-down. Each one has:
112
- - `html`: outerHTML of the chrome subtree (string).
113
- - `bbox`: `{x,y,w,h}` of the chrome at snapshot time (may reflect bottom-scroll position; the walker emits its own subtree bbox on `dom[]` if you need the at-top layout).
114
- - `links[]`: every `<a href>` inside, as `{href, text, label}`. Use these for menu items, policy links, social, etc.
115
- - `headings[]`: every `<h1>`...`<h6>` and `<p class="bold">`, as `{tag, text}`. Use these for column titles ("More Information", "Collections", "Need Help?").
116
- - `inputs[]` / `forms[]`: full input + form metadata for newsletter signup or contact widgets — preserve `action`/`method` verbatim so the form actually submits.
117
- - `images[]`: every `<img>` with src + alt + dimensions.
118
-
119
- When composing a `--target-section=header` or `--target-section=footer` fragment AND the matching `hydratedHeader`/`hydratedFooter` is non-null, BUILD FROM THE STRUCTURED PAYLOAD, not from `dom[]`. This is mandatory whenever the payload is present — it eliminates the entire class of "shadow-dom-opaque" / lazy-light-DOM fabrication risks that previously forced image-slice fallback.
120
-
121
- 1.5. **Pre-dispatch checklist.** Before composing, run:
122
-
123
- ```bash
124
- node {skill-root}/scripts/build-wp.mjs preflight --inspection-path <inspectionPath>
125
- ```
126
-
127
- The script verifies that `inspection.json` carries the minimum the composer needs: at least one classified section with bbox + `computedStyle.background`, and a screenshot present on disk for fallback color sampling. Exit 0 means proceed. Exit 4 means the inspection is incomplete — read the JSON in stderr (it lists what's missing: `dom-empty`, `screenshot-missing`, `sections-unclassified`, `sections-no-computed-style`), surface that to the orchestrator, and halt. Do NOT compose against a partial schema — the output will silently fabricate to fill the gaps.
128
-
129
- 2. **Load the build rules.** Read `references/wp-build-rules.md` — this file contains the composition contract, defensive-specificity rule, !important policy, full-bleed escape, mobile-first breakpoint, asset-substitution rule, hero rules (no 100vh), Google Fonts inlining, accessibility/performance defaults, the canonical patterns A-H, and the anti-patterns #1-9. Treat it as your reference manual for this build.
130
-
131
- 3. **Layer in user-curated knowledge if present.** When `<plugin>/memory/patterns.md`, `<plugin>/memory/anti-patterns.md`, or `<plugin>/presets/wp-elementor.yaml` exist in the host project, load them and let them OVERRIDE the matching defaults from `wp-build-rules.md` — they reflect the user's most recent learning. Skip silently when missing (the framework is early; these get populated over time).
132
-
133
- 4. **Compose the HTML.** Pick the pattern that matches `inspection.sectionType` (and the markup shape — inspect `dom` and `pseudoElements`). Apply every non-negotiable from `wp-build-rules.md`: defensive specificity on every critical selector, `!important` on font-family/font-size/font-weight/color, universal reset at the top, full-bleed container if the section spans viewport-edge, mobile-first base + desktop media query, every `<img src>` resolved through `assetsMap.assets[url].localPath`, SVGs inlined from `assetsMap.assets[url].inline`, no fabricated SVG, no `100vh` for hero, Google Fonts preconnect + display=swap, hero preload link, alt on every image, type="button" on every button, heading hierarchy unbroken. The `designKnowledge` subset (when passed) extends or refines these defaults — apply it.
27
+ A single `.html` file at the requested path. The fragment contains:
28
+ 1. `<link rel="preconnect">` + Google Fonts stylesheet
29
+ 2. `<link rel="preload">` for the hero image (when present)
30
+ 3. Scoped `<style>` block following the rules in `references/wp-build-rules.md`
31
+ 4. Section markup
32
+ 5. Optional inline `<script>` for interactivity (drawer toggle, accordion expand, etc.)
134
33
 
135
- 5. **If `fixHints` are present, patch surgically.** Read the previous output, apply each fixHint to the specific selector or markup it names, and keep everything else untouched. The reviewer's diff should be the only diff. High-severity hints are mandatory; low-severity are optional unless they conflict.
34
+ ## On activation
136
35
 
137
- 6. **Validate.** Write the composed HTML to a temp file and run:
36
+ 1. **Read the inputs.** Parse `inspection.json` (capture `tokens`, `dom`, `imgUrls`, `sectionCrops`) and `assets-map.json`. When composing a specific section, identify which `sectionCrops[]` entry corresponds to it (by bbox.y or sectionType) and READ that crop via the Read tool — the image enters your context as visual content.
138
37
 
139
- ```bash
140
- node {skill-root}/scripts/build-wp.mjs validate --html-file <tmp>
141
- ```
38
+ 2. **Build the section** following `references/wp-build-rules.md`. Universal rules:
39
+ - **Mobile-first**: base CSS at 390px viewport; desktop refinements in `@media (min-width: 750px)`.
40
+ - **Defensive specificity**: chain the section's scope class to itself for every critical rule (`.es-card.es-card { border-radius: 12px !important; }`). The chained-class pattern (`.scope.scope`) beats single-class theme rules; pair with `!important` on properties that themes commonly reset.
41
+ - **`!important` policy**: apply on text-critical (`font-family`, `font-size`, `font-weight`, `color`) AND visual-critical (`border-radius`, `box-shadow`, `background-color`, `gap`, `padding`) properties when they encode the section's visual identity.
42
+ - **Full-bleed pattern**: `width: 100vw; margin-left: calc(50% - 50vw);` to escape Elementor container width constraints.
43
+ - **No `100vh`**: use `aspect-ratio` instead for hero proportions (Elementor iframes don't respect viewport units).
44
+ - **Inline SVG only**: never reference uploaded SVGs (WP blocks SVG uploads by default).
45
+ - **Asset resolution**: every `<img src>` resolved through `assetsMap.assets[url].localPath`. SVG icons either inlined via `assetsMap.assets[url].inline` or, when an image dominates a region (e.g. a card or banner that's actually a single `<img>` in the live DOM), emit `<img src="...">` directly instead of trying to reproduce the visual content in CSS.
46
+ - **No fabrication**: emit only what you can see in the reference screenshot AND/OR find in `inspection.dom` text nodes. When unsure of an exact literal (price digit, count, copy line), emit a `<!-- TODO: <visual description> -->` comment + structural placeholder. Never invent a "plausible" value to fill a gap.
142
47
 
143
- The script reports structural errors (missing `<style>`, missing alt/type, `data:image/svg+xml` raster impostors, `100vh` on heights, fonts without `display=swap`) and warnings (no preload, no preconnect, no defensive-specificity selectors, no reset). Exit code 0 means errors are clean. If it exits 1, fix the flagged issues and re-validate. Warnings are advisory fix what you can without overstepping the inputs.
48
+ 3. **Self-check before write.** Cross-validate every numeric literal, heading, and paragraph in your output against `inspection.dom` text nodes (recursively) + `imgUrls` + `assets-map.json`. If a literal you emitted isn't in any of those, mark it with a TODO comment and replace with a placeholder. The `build-wp.mjs validate --inspection-path` command runs this check automatically use it.
144
49
 
145
- 7. **Persist.** Pipe the validated HTML into:
50
+ 4. **If `fixHints` are present** (retry iteration): the previous output had a measurable visual diff against the reference. Read the hints + the previous HTML + the reference screenshot, apply only the surgical changes the hints request. Don't refactor unrelated areas.
146
51
 
147
- ```bash
148
- node {skill-root}/scripts/build-wp.mjs write --output-path <outputPath>
149
- ```
52
+ 5. **Validate.** Run `node {skill-root}/scripts/build-wp.mjs validate --html-file <tmp> --inspection-path <inspection.json>`. Fix any errors reported. The validator checks structural rules (scoped `<style>`, alt on images, no `100vh`, no `data:image/svg+xml` impostors) AND fabrication (literals not found in the inspection corpus). Exit 0 = ship. Exit 3 = fix the listed issues, re-validate.
150
53
 
151
- The script formats with prettier when available (graceful skip when not installed) and writes to `outputPath`, creating parent directories as needed. It returns `{path, bytes, formatted}` JSON.
54
+ 6. **Persist.** Pipe the validated HTML into `node {skill-root}/scripts/build-wp.mjs write --output-path <path>`. The script formats with prettier when available and writes to disk.
152
55
 
153
- 8. **Return the result** to the orchestrator: the absolute path to the written file, the validator's report (errors + warnings), and a short note on which pattern was applied. Do not return the file's contents — the orchestrator reads the file when it needs them.
56
+ 7. **Return** the absolute path to the written file + the validator's report.
154
57
 
155
58
  ## Failure modes
156
59
 
157
- | Symptom | Likely cause | What to surface |
158
- | ----------------------------------------------- | --------------------------------------------- | -------------------------------------------------------------------------- |
159
- | Preflight exits 4 (`missing[]` populated) | Inspection lacks min for faithful clone | Halt and surface the missing[] list to the orchestrator. Do NOT compose re-run inspection (likely overlay polluted, shadow DOM opaque, or section detector missed). |
160
- | Validator returns errors after first compose | Missed a non-negotiable from the rules | Fix and re-validate (up to 2 attempts). If still failing, surface the report. |
161
- | `assetsMap.failed[]` includes a critical asset | Source 404 / network failure during extract | Drop the image (decorative) or use a literal-color placeholder (hero). Never fabricate. Comment in the output: `<!-- sb-build-wp: asset failed download -->`. |
162
- | `inspection.widgetBlocked: true` | Bot challenge on inspect DOM is unreliable | Stop. Tell the orchestrator the inspection is unusable. Do not compose from a blocked page. |
163
- | Pattern doesn't match any section in `inspection.dom` | Section type detected but markup is ambiguous | Pick the closest pattern from A-H, document the choice in a comment, and surface to the orchestrator. |
164
- | Prettier missing | Optional dep not installed | Non-fatal — `write` reports `formatted: false, formatterSkippedReason: "prettier-not-installed"`. Output is still written. |
165
-
166
- ## §V03-0a — Header / Footer composition from hydrated snapshot
167
-
168
- When `--target-section=header` or `--target-section=footer` is passed AND the corresponding `inspection.hydratedHeader` / `inspection.hydratedFooter` is non-null, follow this composition recipe instead of the generic A-H pattern lookup:
169
-
170
- **Footer recipe** (when `hydratedFooter` present):
171
-
172
- 1. **Outer shell.** `<footer class="es-footer">` with reset + `box-sizing: border-box` + the page background-color taken from `inspection.tokens.colors.background` (or fallback `#fafafa`).
173
- 2. **Grouping.** Split `hydratedFooter.links` into clusters by their adjacent heading in `hydratedFooter.headings`. Order of headings reflects the live DOM order. For each heading: emit a column with `<p class="footer__col-title">{heading.text}</p>` followed by `<ul>` of `<li><a href>` from the links bucketed under that heading.
174
- - Heuristic for bucketing: walk `hydratedFooter.html` once (in your head, you have the raw outerHTML in `hydratedFooter.html` if needed) — links that appear AFTER a heading and BEFORE the next heading belong to that heading. If you can't disambiguate, fall back to grouping by `href` prefix patterns (`/policies/` → "More Information", `/products/` → "Collections", `/pages/` → split by name).
175
- 3. **Newsletter.** If `hydratedFooter.forms[]` is non-empty AND `hydratedFooter.inputs[]` contains an `email`-typed input, emit a real `<form action="{form.action}" method="{form.method}">` with the email input, preserving `name`, `placeholder`, `required`, `aria-label`. Hidden inputs (`type=hidden`) are preserved verbatim — they're typically Shopify form-type tokens. Add a submit `<button type="submit">` even if not in the source.
176
- 4. **Social media.** Detect social links by `href` matching `/facebook|instagram|tiktok|youtube|x\.com|twitter|pinterest/`. Group in a separate `<ul class="footer__social">` with inline SVG icons (you can ship the standard FB/IG icons from the bundled patterns). Skip if no matches.
177
- 5. **Payment icons.** Detect by `hydratedFooter.images[]` with `alt` matching `/amazon|visa|mastercard|amex|apple pay|google pay|discover|diners|shop pay|paypal/i` — emit those as a `<ul class="footer__payments">` with `<img>` referencing the `assetsMap` (resolve src via the standard pipeline). If `images[]` is empty but you'd expect them, fall back to text labels.
178
- 6. **Copyright.** If any link or heading text matches `© YEAR, Brand.`, preserve verbatim at the bottom.
179
- 7. **NO image-slice fallback.** When hydrated data is present, never compose a single `<img>` of the footer. The hydrated payload gives everything needed for real markup.
180
-
181
- **Header recipe** (when `hydratedHeader` present):
182
-
183
- 1. **Outer shell.** `<header class="es-header">` with sticky position if the page tokens indicate so.
184
- 2. **Promo bar.** If any link's text matches a promotional pattern (`/sale|discount|free|% off/i`) OR `hydratedHeader.headings[]` contains a short standalone heading at the top, emit `<div class="es-header__promo">{text}</div>`.
185
- 3. **Brand.** If `hydratedHeader.images[]` includes one with `alt` matching the brand name (derived from URL or generic `logo`), emit `<a class="es-header__brand" href="/">` with that `<img>`.
186
- 4. **Nav.** Take `hydratedHeader.links` filtered to category-looking hrefs (`/products/...`, `/collections/...`, top-level pages). Emit as `<nav><ul>{links}</ul></nav>`. On mobile (`@media (max-width: 749px)`), collapse into a `<details><summary aria-label="Menu">` drawer — keep all category links inside.
187
- 5. **Utility icons.** Links with text "Open search", "Account", "Cart" (or matching hrefs `/search`, `/account`, `/cart`) get rendered as a right-side cluster of icon buttons.
188
-
189
- **Output contract for header/footer:**
190
- - Markup includes a top comment `<!-- sb-build-wp: composed from hydrated snapshot (V03-0a) -->`.
191
- - `fabricationRisks` array in the metadata is empty for these sections (hydrated data IS the truth — no fabrication concern).
192
- - If `hydratedHeader`/`hydratedFooter` is NULL or empty, fall back to the prior A-H pattern lookup OR — as last resort — image-slice from screenshot bbox.
60
+ | Symptom | Cause | Action |
61
+ |---|---|---|
62
+ | Preflight exits 4 (`missing[]`) | Inspection incomplete | Halt. Surface to orchestrator. Don't compose against partial schema. |
63
+ | Validator returns errors after compose | Missed a universal rule | Fix and re-validate (up to 2 attempts). Surface report on persistent failure. |
64
+ | `assetsMap.failed[]` includes a critical asset | Source 404 or network failure | Emit `<img src="" alt="...">` placeholder + `<!-- TODO: asset failed download (URL) -->`. Never fabricate URL. |
65
+ | Composer can't read literal from crop confidently | Resolution insufficient or text occluded | Emit TODO placeholder. Don't guess. |
193
66
 
194
67
  ## Conventions
195
68
 
@@ -51,7 +51,17 @@ The Elementor + active theme stack frequently injects `!important` on these prop
51
51
  - `font-weight` — set on `h1..h6`, `body`
52
52
  - `color` — set on links via `.elementor a`, on body via theme
53
53
 
54
- Use `!important` ONLY on these four properties when they appear on critical text. Do NOT spray `!important` everywhere overuse turns debugging into a nightmare. Specifically: do NOT `!important` margins, paddings, widths, heights, backgrounds, transforms, transitions.
54
+ ### v0.3.5 Expanded property list (after real-world "tudo liso no WordPress" bug 3.4):
55
+
56
+ The above 4-property list was insufficient — when output was published in WordPress, the products grid and other card-based layouts came out "tudo liso" (no border-radius, no shadow, no gaps). The Elementor theme stack also normalizes/overrides these visual properties on `*` selectors. Add `!important` ALSO to:
57
+
58
+ - `border-radius` — on cards, buttons, image containers (themes flatten cards otherwise)
59
+ - `box-shadow` — on cards and elevated elements
60
+ - `background-color` — on color-bearing blocks (`.es-card`, `.es-cta`, `.es-banner`) when the visual identity depends on it
61
+ - `gap` (and `column-gap`, `row-gap`) — on grid/flex containers (themes that reset child margins also reset gaps)
62
+ - `padding` — on card-like containers and section blocks where the live padding is visually critical
63
+
64
+ Apply via the same chained-scope pattern (`.es-card.es-card { border-radius: 12px !important; }`). Still DO NOT spray `!important` on margin, width, height, transforms, transitions — those rarely conflict with theme.
55
65
 
56
66
  ## Reset universal
57
67