Skip to content

HTML export: descendant root-identity selectors (.dark .card / #app / [dir]) don't match after root-identity transfer — needs inner root-proxy #33

Description

@project820

Summary

The HTML-export content-root model cannot satisfy two valid ways a model references the document root's identity at once, because root identity is transferred onto the [data-he-content] wrapper itself while all other model selectors are descendant-scoped under [data-he-content].

Given a model document with identity on the root, e.g. <body class="dark" id="app" dir="rtl">:

  • Global-root-qualified selectors work (already fixed): body.dark, html#app, body[dir="rtl"] are rewritten by the global-selector pass to [data-he-content].dark / [data-he-content]#app / [data-he-content][dir="rtl"], which match the wrapper (identity was transferred onto it).
  • Descendant-identity selectors do NOT work (this bug): a plain .dark .card, #app .card, or [dir="rtl"] .card — where the model treats the root class/id/attr as an ancestor — is descendant-scoped to [data-he-content] .dark .card, i.e. it looks for a .dark descendant of the content root. But dark lives on [data-he-content] itself, not on a descendant, so the rule never matches and the themed/directional styling finalizes dead.

Root cause: identity on [data-he-content] serves compound/global-root selectors but not descendant selectors; the sanitizer's blind [data-he-content] <selector> descendant scoping cannot know that a leading .dark/#app/[dir] refers to the root.

Proposed fix (design-level)

Represent the model's <body> as an inner root proxy element carrying all transferred root identity, inside the content root:

<div data-he-content><div data-he-root class="dark" id="app" dir="rtl" lang="ko" style="...">…body children…</div></div>

Then:

  • Descendant-identity selectors work: [data-he-content] .dark .card matches (the proxy has .dark; .card is its descendant).
  • Global-root selectors retarget to the proxy: the global-selector rewrite maps body/html/:root (and *) and the transferred root inline style from [data-he-content] to the [data-he-root] proxy (e.g. [data-he-content] [data-he-root].dark), so body.dark etc. still match.
  • data-he-root becomes a reserved attribute (like data-he-content): model CSS/HTML cannot forge it.

Scope / risk

  • Touches the hardened global-selector rewrite in html-export-css-sanitize.ts (CRLF-escape, functional :root, sibling-combinator, :nth-child(of S) hardening; architect-CLEAR) and the root class/id/attr/style transfer in html-export-sanitize.ts + html-export-shell.ts, and changes the finalized-bytes structure (new inner wrapper). Requires re-running the full global-selector adversarial suite + direct harness + Grok-QA/architect security re-review, because it moves the reserved scoping boundary.
  • Deliberately not hot-patched under review pressure: the single-wrapper model was hardened over three rounds; the inner-proxy change should be designed and reviewed as a unit.

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions