# Web grammars (HTML + CSS) + HTML injections — framing **Revision 4 — implemented on branch `web-grammars` (PR #146). Ground truth: canonical `main` @ `4daa1b8` (after LaTeX #144 and inline-math-docs #145), 2026-07-23.** Rev 2 settled the capture set, injection scope, and LSP claim (round 1); rev 3 corrected the `#match?` predicate analysis (round 2); rev 4 names the intended `@attribute` retro-paint on the already-bundled rust/lua/yaml grammars — verified on a Rust buffer and pinned by a test (round 3). See §0.1. ## 0.1 Revision history ### Round 1 (rev 1 → rev 2) - **F1 (high).** Q#WEB4 deferred the capture mapping. The upstream queries settle it: HTML and CSS need exactly **`tag`** and **`attribute`**; `@tag.error` prefix-walks to `tag`; there is **no** `@tag.delimiter` (rev-1 speculation). Q#WEB4 now names both mappings and their styles, and acceptance 6 paints an HTML **attribute** explicitly (a tag/property-only test could pass with `attribute` unverified since `property` is already recognized). - **F2 (medium).** The HTML injections query has **exactly two** patterns — `script_element`→javascript, `style_element`→css. Q#WEB5 no longer claims event-handler / `style=` coverage; inline-attribute injection is now a named deferral. - **F3 (medium).** No HTML/CSS language server is bundled: `lsp.lua`'s default config list ends at YAML (`:247`) and `:521` returns `nil` without a configured command. §2 and §5 corrected — the grammars give stable language IDs so a *user-supplied* config attaches automatically, but nothing ships or starts by default. ### Round 2 (rev 2 → rev 3) - **R2-1 (medium).** The rev-2 claim that pmacs *ignores* `#match?` was wrong. `src/syntax.rs:1701` passes the source to `QueryCursor::captures`, so tree-sitter evaluates the standard `#match?`/`#eq?`/`#any-of?` text predicates natively (pmacs special-cases only `#is? local`, `:1703`). Corrected in §2 and Q#WEB4: an ordinary CSS property has a single `@property` capture; only a `--custom-property` also receives `@variable` (a benign double-capture). Acceptance 6 no longer claims an ordinary property pins custom-property precedence — it just verifies `color` → `@property` paints. - **R2-2 (low).** The folding footprint in §0 was stale. At `githubsucks/folding` @ `036a994` the branch touches `src/fold.rs`, `editor.rs`, `editor_core.rs`, `lib.rs`, `lua_bindings/{fold.rs,mod.rs}`, `semantic_render.rs`, `fold.lua`, and tests — not `overlay.rs`/`daemon.rs`/`syntax.rs`. §0 now lists the real set; the zero-overlap conclusion is unchanged (stronger, if anything). ### Round 3 (rev 3 → rev 4) - **R3-1 (medium).** Q#WEB4 verified which captures pmacs *lacks* but not the reverse: which already-bundled grammars *use* `@attribute`. Three do — rust (`attribute_item`), lua (``), yaml (directives) — so the new `("attribute", fg(3))` entry retro-paints their previously-unpainted spans yellow. Named in Q#WEB4 as intended (verified on a Rust buffer: uniform yellow, an improvement over unpainted), and pinned by `rust_attribute_repaints_via_shared_attribute_capture`. `tag` is unaffected (HTML/CSS only). Add tree-sitter **HTML** and **CSS** grammars so `.html`/`.htm` and `.css` buffers get lexical highlighting, and — the north-star payoff — light up HTML's embedded-language **injections**: `` parses as JavaScript and `` parses as CSS, via HTML's crate-exported injections query riding the #122 injection engine. CSS is registered here so the `` and `` produces a `ParseTreeBundle` whose child layers resolve to `css` and `javascript`, and a grid-paint asserts an injected CSS property and JS keyword paint **inside** the embedded regions (mirroring `grid_paints_injected_child_keyword`). 6. **Paint (non-vacuous highlighting)**: parse `` and assert **both** the `` tag (`@tag`) **and** the `href` attribute name (`@attribute`) paint the two new non-default styles — the attribute assertion is load-bearing, since a tag-only test could pass with `@attribute` unverified. Also assert a CSS selector (`@tag`) paints the new `tag` style and an ordinary CSS property (`color` → `@property`) paints non-default (an ordinary property has a single unconditional capture, so no precedence subtlety — Q#WEB4). 7. **Full gate suite** per `CLAUDE.md`. ## 7. Prior art in pmacs - **LaTeX lane #144** (`docs/latex-grammar-math-substrate-framing.md`) — the grammar-add mechanics, the table-guard/smoke/paint test conventions, and the compile-gate-plus-paint-test discipline. (Its overlay convention is not needed here.) - **Multi-language injections #122** (`docs/multi-language-injections-framing.md`) — the `ParseTreeBundle` + `Layer` engine HTML's injections ride. - **markdown → rust fenced code** (`src/syntax.rs:848`, tested by `src/highlight.rs`'s `grid_paints_injected_child_keyword`) — the working injection precedent this lane's acceptance 5 mirrors.