Eliminate horizontal overflow in every HTML plan — retrofit all 55 shipped files in docs/plans/ with a versioned 8-line CSS block via an idempotent script, and bake the same block into the plan skeleton so every future plan is born responsive.
Read and implement all steps in the plan at docs/plans/retrofit-responsive-plan-css.html — Retrofit responsive CSS into every HTML plan and harden the skeleton
Run as workflow — launch parallel subagents
Run a workflow to implement the plan at docs/plans/retrofit-responsive-plan-css.html — Retrofit responsive CSS into every HTML plan and harden the skeleton
retrofit-responsive-plan-css.html
docs/plans/retrofit-responsive-plan-css.html
Context
A 390px-viewport browser audit found 16 of the 54 HTML files in docs/plans/ overflow horizontally — the worst (add-multi-host-deploy-targets.html) by 294px of sideways scroll. Current-generation, digest-era plans are affected too: the defect is content-dependent, not template-generation-dependent.
Three root causes combine:
- Unbreakable tokens with no wrap rule. File paths and commands like
kit/plugins/docs-publisher/reference/.gitlab-ci.ymlinside inline<code>, list items, and bare divs have nooverflow-wrapanywhere in the template — a token wider than a phone screen simply cannot break. - Grid min-content propagation. Grid items default to
min-width: auto, so one unwrappable token inmaininflates the shared single-column track and stretches the sidebar with it.overflow-wrap: break-wordwraps visually but does not reduce min-content size — onlyanywheredoes, which is why bothmin-width: 0and inheritedoverflow-wrap: anywhereare needed. - Component stragglers. The three-column
.compare-griddoes not collapse below 600px in some template generations, and table cells have no wrap rule.
The fix is proven: injecting the block below into all 54 rendered files in a width-controlled iframe harness took the overflow count from 16 to 0, with no effect on already-clean files (anywhere only activates when a single word exceeds the line, so normal prose is untouched).
/* plan-responsive-fix v1 — injected by scripts/retrofit-responsive-plans.mjs */
body { overflow-wrap: anywhere; }
main, nav, aside { min-width: 0; }
.layout > *, .wrap > *, .compare-grid > * { min-width: 0; }
pre { white-space: pre-wrap; max-width: 100%; }
table { max-width: 100%; }
img, video { max-width: 100%; height: auto; }
@media (max-width: 600px) { .compare-grid { grid-template-columns: 1fr; } }
Because every plan is a self-contained single-file HTML document (embedded CSS, no external assets — by design, so plans survive file:// opens, copies, and email), skeleton improvements never reach already-shipped plans. The retroactive mechanism is a versioned injector script, mirroring the scripts/backfill-plan-digests.mjs (2.3.0) and scripts/backfill-save-pdf.mjs (2.4.0) precedents — this is the third injector in that family. The block is wrapped as <style id="plan-responsive-fix" data-version="1"> and inserted immediately before </head>; the id + version pair is the idempotency key (current → skip, older → replace, missing → inject). docs/plans/index.html is always excluded — it is regenerated from scratch by docs/plans/build-index.sh via the rebuild hook and already renders clean at phone width.
Files to Modify
scripts/retrofit-responsive-plans.mjsnew idempotent injector with --check gatedocs/plans/*.htmlmodified 55 files (incl. this plan) — v1 block injected before </head>- kit/plugins/plan-agent/
skills/implementation-plan/reference/SKELETON.htmlmodified same v1 block embedded nativelyCHANGELOG.mdmodified 2.4.1 entry.claude-plugin/marketplace.jsonmodified plan-agent 2.4.0 → 2.4.1tests/plugins/test-responsive-retrofit.shnew corpus + fixtures + skeleton-sync guard
Diagram
docs/plans/*.htmlid="plan-responsive-fix" + data-versionskip · replace · injectinjected / updated / skipped--check → exit 1- plans stay single-file, file:// portable
- deterministic — no LLM pass
- re-runnable: bump data-version for v2
- mirrors the backfill-*.mjs precedents
- breaks self-contained guarantee
- copied/emailed plans lose styling
- extra asset to deploy
- only 42/54 plans carry a digest
- loses visuals + checkbox state
- LLM cost, non-deterministic
| Flag | Default | Behavior |
|---|---|---|
| (none) | — | Inject or replace the v1 block in docs/plans/*.html; skips index.html and files already at v1 |
--check | off | No writes; exit 1 listing files missing or outdated, exit 0 when the corpus is current |
--dry-run | off | Print would-inject / would-replace actions without writing |
--dir <path> | docs/plans | Operate on a different plans directory |
--include-archive | off | Also process docs/plans/archive/ recursively — explicitly opt-in |
Steps
scripts/retrofit-responsive-plans.mjs — the idempotent injector
backfill-plan-digests.mjs / backfill-save-pdf.mjs conventions: Node ≥ 18, zero dependencies, summary output. Contract: wrap the canonical CSS constant as <style id="plan-responsive-fix" data-version="1">; insert immediately before </head> (case-insensitive); skip files already at the current version; replace the whole block when an older version is found; always exclude index.html; report and skip files with no </head> rather than half-writing them. Flags: --check, --dry-run, --dir <path>, --include-archive.Verify
node scripts/retrofit-responsive-plans.mjs --check exits 1 listing 55 files; --dry-run prints the same 55 as would-inject while git status stays clean; a synthetic fixture without </head> is reported as a skip, never modified.docs/plans/
index.html, including this plan document itself, which was built from the pre-fix skeleton) pick up the v1 block in a single reviewable diff.Verify
grep -L 'id="plan-responsive-fix"' docs/plans/*.html returns only index.html; an immediate second run reports 55 skipped and adds nothing to git diff (idempotency proven on the real corpus).SKELETON.html
--check natively — otherwise every freshly generated plan reintroduces the defect the retrofit just cleared. Insert the same <style id="plan-responsive-fix" data-version="1"> block immediately before </head> in kit/plugins/plan-agent/skills/implementation-plan/reference/SKELETON.html, byte-identical to the script's CSS constant (the script is the single source of truth; the skeleton carries a copy).Verify
grep 'plan-responsive-fix' kit/plugins/plan-agent/skills/implementation-plan/reference/SKELETON.html finds the block before </head>; the CSS extracted from the skeleton block diffs empty against the script constant.tests/plugins/test-responsive-retrofit.sh — corpus, fixtures, and sync guard
--check exits 0 over the repo's docs/plans/; (b) fixture injection — missing block injected exactly once before </head>, second run byte-identical; (c) data-version="0" fixture replaced with v1, no duplicate block; (d) index.html and no-</head> fixtures untouched; (e) skeleton block CSS byte-identical to the script constant; (f) head-only guarantee — the injector's output is byte-identical to its input from <body onward, so the retrofit can never alter markup, ARIA, or semantics.Verify
bash tests/plugins/test-responsive-retrofit.sh exits 0; stripping the block from a tmp corpus copy makes the --check assert exit 1 naming the stripped file..claude/rules/marketplace.md mandates a manual semver bump (patch for a fix) in .claude-plugin/marketplace.json plus a kit/plugins/plan-agent/CHANGELOG.md entry with every plugin change.Verify
marketplace.json shows plan-agent 2.4.1 (higher than main's 2.4.0) and passes the settings-hook JSON validation; CHANGELOG.md opens with a ## 2.4.1 entry dated 2026-06-12 describing the responsive block and the injector.scrollWidth in a width-controlled harness is the ground truth the investigation used — the retrofit is done when the overflow count is zero, not merely when markers are present. The 320px pass grounds the result in WCAG 2.1 AA reflow (1.4.10), which requires no horizontal scrolling at 320px CSS width.Verify
docs/ locally (bash scripts/serve-docs.sh or python3 -m http.server --directory docs), load every plan in a 390px-wide and then a 320px-wide iframe, and assert documentElement.scrollWidth ≤ iframe width for all 55 files at both widths; spot-open add-multi-host-deploy-targets.html (previously +294px) and confirm no horizontal scrollbar.Tests
File: tests/plugins/test-responsive-retrofit.sh
Type: smoke test (runs against the real corpus)
Asserts: every docs/plans/*.html except index.html carries <style id="plan-responsive-fix" data-version="1">, and the skeleton's embedded block is byte-identical to the script's CSS constant — i.e. every shipped plan and every future plan is responsive-hardened, which is this plan's stated objective.
Run: bash tests/plugins/test-responsive-retrofit.sh
File: tests/plugins/test-responsive-retrofit.sh (fixture section)
Targets: marker detection, version comparison, insertion point, and exclusions in scripts/retrofit-responsive-plans.mjs
Key cases: missing block → injected exactly once, immediately before </head>; data-version="0" → replaced with v1 without duplicating the block; current v1 → skipped byte-identical; index.html and a fixture with no </head> → untouched and reported; head-only guarantee → output byte-identical from <body onward (no markup, ARIA, or semantics changes ever).
File: tests/plugins/test-responsive-retrofit.sh (corpus section)
Targets: the full CLI — default run, --check, and --dry-run — against a tmp copy of docs/plans/
Key cases: first run injects every non-index file (55 at the time of writing); second run no-ops (tree byte-identical); --check exits 0 after the run and 1 when one file is stripped; --dry-run writes nothing.
Acceptance Criteria
Verification
Confirm the whole change end-to-end, in this order:
- Run
bash tests/plugins/test-responsive-retrofit.sh— exit 0 covers the corpus check, fixture cases, and the skeleton/script sync assert. - Serve
docs/locally and re-run the width-controlled iframe audit at 390px and 320px across all 55 plan files: load each file in an iframe of each width and comparedocumentElement.scrollWidthagainst the iframe width. Expected overflow count: 0 at both widths (it was 16 at 390px before the retrofit; 320px grounds WCAG 1.4.10 reflow). - Visually open
add-multi-host-deploy-targets.html(worst offender, previously +294px) andpersist-checkbox-state-in-html-attributes-review.html(table-heavy review file) at phone width — no horizontal scrollbar, long paths wrap, tables fit. - Run the injector a second time — it reports all files skipped and
git diffgains nothing. - Confirm
.claude-plugin/marketplace.jsonshows plan-agent2.4.1with a matchingCHANGELOG.mdentry, and that the JSON passed the settings-hook validation on save.
Completion Checklist
Completion Report
No items to report — all requirements met.