Skip to content

Reject content that escapes the content root - #299

Merged
serenakeyitan merged 1 commit into
mainfrom
fix/content-must-stay-in-the-root
Aug 28, 2026
Merged

serenakeyitan merged 1 commit into
mainfrom
fix/content-must-stay-in-the-root

Conversation

@serenakeyitan

Copy link
Copy Markdown
Collaborator

A doc I generated rendered full-bleed on tdoc.dev, and every check passed on the way out.

One stray </div> closed .wrap at byte 4189 of a 37KB document. The remaining 89% became siblings of the root, so :where(body > .wrap) — which supplies the 720px column — stopped applying to it. Measured against the previous version at the same viewport:

content width at 1280px
v2 671px
v4 1261px

Nothing else was wrong. The doc had a content root, its CSS was in contract, its figures were in bounds. The nesting was the only tell, and nothing looked at it.

The check

The parser already counted content roots at body level. It now also records the elements it finds beside one, and rejects them by name and count.

It fires only when a root exists. A doc with no root at all is a supported shape — the template applies the column to body itself through :has() — and its headings are the document rather than escapees. ~/tdocs/hello is exactly that, and it was the one false positive in a sweep of every doc on this machine before that condition went in. Zero after.

<script>, <style>, <template> and <noscript> beside the root are not content and do not trip it.

Tier

Structural, not stylistic, so it runs on both paths — beside the existing "exactly one content root" rule, which would otherwise be trivially satisfiable by a document whose root wraps only its first paragraph.

46 offline suites green; the new test covers the escaped case, the correctly nested case, and a <template> sibling.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Xw9Mt3rV3ujnejPr41pqvz

A doc generated by this skill rendered full-bleed on tdoc.dev, and every
check passed on the way out.

One stray </div> closed .wrap at byte 4189 of a 37KB document, so the
remaining 89% became siblings of the root and :where(body > .wrap) — which
supplies the 720px column — stopped applying. Content measured 1261px wide
at a 1280px viewport where the previous version measured 671px. Nothing else
was wrong: the doc had a content root, its CSS was in contract, its figures
were in bounds. Nesting was the only tell and nothing looked at it.

The parser already counted content roots at body level; it now also records
what it finds beside one.

The check fires only when a root exists. A doc with no root at all is a
supported shape — the template applies the column to body itself through
:has() — and its headings are the document rather than escapees. ~/tdocs/hello
is exactly that, and it was the single false positive in a sweep of every doc
on this machine before that condition went in.

Structural rather than stylistic, so it runs on both paths, beside the
existing one-content-root rule that a document whose root wraps only its
first paragraph would otherwise satisfy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Preview

Open this: https://pr-299-tdoc-preview.jyshi1107.workers.dev/d/conway-life/v/2

This link is unique to this PR. New commits update the same URL. It is not tdoc.dev.

Preview has no Durable Object — concurrent comments use the KV fallback. Data expires in 14 days.

@serenakeyitan
serenakeyitan merged commit dfaff39 into main Aug 28, 2026
4 checks passed
@serenakeyitan
serenakeyitan deleted the fix/content-must-stay-in-the-root branch August 28, 2026 09:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant