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.
- package/package.json +1 -1
- package/templates/commands/build-page.md +62 -493
- package/templates/commands/build-site.md +98 -705
- package/templates/commands/clip-section.md +102 -501
- package/templates/skills/sb-build-wp/SKILL.md +37 -164
- package/templates/skills/sb-build-wp/references/wp-build-rules.md +11 -1
- package/templates/skills/sb-inspect-live/scripts/inspect-live.mjs +4 -367
- package/templates/skills/sb-review-checks/SKILL.md +0 -113
- package/templates/skills/sb-review-checks/references/review-rules.md +0 -195
- package/templates/skills/sb-review-checks/scripts/lib/anti-patterns.mjs +0 -385
- package/templates/skills/sb-review-checks/scripts/lib/cross-reference.mjs +0 -115
- package/templates/skills/sb-review-checks/scripts/lib/design-quality.mjs +0 -541
- package/templates/skills/sb-review-checks/scripts/review-checks.mjs +0 -250
- package/templates/skills/sb-review-checks/scripts/tests/test-anti-patterns.mjs +0 -366
- package/templates/skills/sb-review-checks/scripts/tests/test-cross-reference.mjs +0 -170
- package/templates/skills/sb-review-checks/scripts/tests/test-design-quality.mjs +0 -493
- package/templates/skills/sb-review-checks/scripts/tests/test-review-checks.mjs +0 -267
- package/templates/skills/sb-test-interactivity/SKILL.md +0 -133
- package/templates/skills/sb-test-interactivity/scripts/test-interactivity.mjs +0 -970
- package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/aria-controls-broken.html +0 -32
- package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/aria-controls-good.html +0 -47
- package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/deferred-listeners.html +0 -67
- package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/details-good.html +0 -25
- package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/dialog-good.html +0 -38
- package/templates/skills/sb-test-interactivity/scripts/tests/fixtures/no-interactive.html +0 -15
- 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
|
-
##
|
|
8
|
+
## Mental model
|
|
9
9
|
|
|
10
|
-
|
|
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
|
-
|
|
12
|
+
## Overview
|
|
13
13
|
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
34
|
+
## On activation
|
|
136
35
|
|
|
137
|
-
|
|
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
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
+
7. **Return** the absolute path to the written file + the validator's report.
|
|
154
57
|
|
|
155
58
|
## Failure modes
|
|
156
59
|
|
|
157
|
-
| Symptom
|
|
158
|
-
|
|
159
|
-
| Preflight exits 4 (`missing[]`
|
|
160
|
-
| Validator returns errors after
|
|
161
|
-
| `assetsMap.failed[]` includes a critical asset
|
|
162
|
-
|
|
|
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
|
-
|
|
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
|
|