HTML grammar
Last Updated: 2026-05-30 Status: 🟢 APPROVATO — allineato al contratto v0.5.0 (renderer-side chain landed 2026-05-30). Maintainer: core team.
HTML5 grammar originally derived from the W3C schema. 113 elements, full parent/child validation.
Purpose
Produce HTML5 markup. Supports both XHTML-style self-closing void
tags (<img/>) and bare HTML5 (<img>), with optional pretty
printing. Pairs with HtmlRenderer for serialization. Pairs with
the CSS-kwarg machinery for inline styling.
Quick start
from genro_builders.contrib.html import HtmlBuilderHandler
class Page(HtmlBuilderHandler):
def main(self, root):
root.html().head().title("Hello")
body = root.html().body()
body.h1("Hello")
body.p("genro-builders")
p = Page(); p.create()
print(p.render(pretty=True))
Elements
The grammar was originally derived from the W3C HTML5 Validator schema (html5_elements.py). It contains 113 tags grouped by content category (flow content, phrasing content, etc.) plus their parent and sub-tag constraints.
Note: the file is no longer fully auto-generated. It was seeded from the W3C schema but is now hand-maintained: edits and additions go in directly. The schema-diff utility in
importer/html5_schema_builder.pyexists to spot W3C drift on demand, not to regenerate the file.
Highlights:
All 16 HTML5 void elements are present as leaves:
area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr,command,keygen.Container elements use the W3C content categories.
A subset of phrasing elements is exposed under abstracts like
phrasing,flow,interactive(no@prefix; abstracts live in the dedicated_abstractssub-bag of the class schema).
Refer to the auto-generated module for the exhaustive list.
Common patterns
Attributes with reserved Python keywords
Some HTML attribute names collide with Python keywords (class,
for). The grammar uses an underscore prefix to disambiguate:
body.div(class_="card")
body.label(_for="email")
The renderer strips the leading underscore on output.
Inline CSS via kwargs
HtmlRenderer recognizes 24 CSS root names as kwargs and emits
them in a style="..." attribute. Sub-properties use underscore
syntax:
body.div(
"content",
id="main", class_="card",
color="red",
background="#fff",
padding="12px",
padding_top=10,
font_size="14px",
text_align="center",
rounded=12,
transform_rotate=-3,
style="margin: 4px",
)
style_<prop> is the escape hatch for CSS properties outside the
curated 24:
body.div(style_aspect_ratio="16 / 9")
For the full kwarg / macro reference see ../builders/patterns.md and the renderer source.
Chaining leaves with ._
body.h1("Title")\
._.p("paragraph one")\
._.p("paragraph two")
Render
HtmlRenderer produces HTML5 markup through the universal walk:
RendererBase.render(source, **opts) recurses on the source bag,
and HtmlRenderer.rendered_item(node, item, runtime_attrs, **opts)
emits the fragment for each node.
Mode-specific kwargs available at handler.render(...):
Kwarg |
Default |
Effect |
|---|---|---|
|
|
Void tags as |
|
|
Two-space indentation and trailing newline per node. |
The xml mode is inherited from BagBuilderBase.renderer_xml. It is a
real render — it rides the universal walk, so pointers are resolved and
framework markers (node_id, …) are filtered out, exactly like the
html render. For the raw structural view of the source (markers and
unresolved pointers shown verbatim) call handler.source.to_xml() on
the bag directly: that is a different thing from render(mode="xml").
Validation rules
<rect>,<circle>,<path>and other SVG tags are not valid here. The grammar rejects them unless they appear inside a@subbuilder(SvgBuilder)boundary (see<svg>).<li>must appear inside<ul>or<ol>.<head>elements (title, meta, link, …) reject body content.Void tags reject children.
Validation errors raise ValueError at create() time.
Worked examples
Three ready-to-run examples live under
../../src/genro_builders/contrib/html/examples/,
each in the three-view format (.py, .ipynb, rendered .html):
01_introduction/— minimal HtmlBuilderHandler page.02_inline_styling/— CSS kwarg machinery, macros, escape syntax.03_subbuilders/— HTML hosting SVG and SVG hosting HTML via<foreignObject>.
Known limitations
The grammar covers HTML5 but not yet HTML5 templates, custom-element semantics, or shadow DOM.
No automatic doctype prepend. The user adds
<!DOCTYPE html>by hand when needed.HTML5-specific accessibility hints (
aria-*attributes) are not validated — they pass through as opaque kwargs.
References
Source:
src/genro_builders/contrib/html/.Renderer:
src/genro_builders/contrib/html/html_renderer.py.Schema (generated):
src/genro_builders/contrib/html/html5_elements.py.Contract:
roadmap/architecture-contract.md.