Output HTML
The engine serializes a BEMJSON tree as minimal HTML5 without formatting indentation. Newlines from text, code, and raw are preserved, so the output need not be a single line.
Minimization by 30 rules
The rules describe how the typed tree becomes minimal HTML. Structural validity depends on the input tree and consumer templates. Check the actual artifact with W3C Nu Html Checker; minimization tests do not prove that every document is valid.
Document wrapper
- With
document: true, the first bytes are<!doctype html>. - The
<html>start tag is omitted when it has no emitted attributes or classes and omission does not move a leading comment outside the root. - The
<head>start tag is omitted when it has no emitted attributes or classes and is empty or begins with an element. - The
<body>start tag is omitted when it has no emitted attributes or classes and is empty or begins with something other than ASCII whitespace, a comment, ormeta,noscript,link,script,style,template. - With
document: true, the engine appends two newlines. A fragment without that option does not receive them. A required paragraph or empty-wrapper boundary remains explicit so those newlines do not move into another element.
The serializer evaluates resolved children after flattening arrays and tag: false fragments; null positions and empty strings emit nothing. Attributes include BEM/mix classes, explicit cls and generated data-bem: an opening tag carrying any of them stays. Standalone head/body fragments retain their wrappers because their parent is unknown. Trusted raw is preserved verbatim; where its contents prevent proving omission safe, the neighboring tag stays explicit.
Void elements
area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr:
<name attributes>— without/>.- Inside SVG, empty
path,circle,ellipse,line,polygon,polyline,rect,use, andstopelements use/>. Other SVG elements retain an end tag.foreignObjectrestores HTML rules for its descendants; a nestedsvgrestores SVG rules.
The supported void-tag set also includes legacy param.
Tag and boolean-attribute matching ignores ASCII letter case while preserving authored spelling. Thus IMG remains a void tag; mixed-case SVG and foreignObject preserve their serialization contexts. Unicode attribute names are not folded into known boolean names.
Boolean attributes
muted, autoplay, playsinline, async, defer, disabled, checked, selected, …:
- Name only, no
=and no value.
hidden is enumerated: string values such as until-found are preserved. In attrs, hidden: true emits a bare attribute and hidden: false omits it.
Attribute quotes
The engine escapes HTML characters first, then chooses outer quotes for the encoded value:
- the value is empty →
alt=""(quotes are mandatory); - quotes are required if it contains any of these characters: whitespace,
",',`,=,<,>; - otherwise — unquoted:
name=value. - When quoting is required, use double quotes
"...".
A comma in a value does not require quotes — data-tags=a,b stays unquoted.
A logical ", <, or > becomes an HTML entity before this check and does not require outer quotes by itself. For example, the logical value " can be emitted as data-x=". Generated data-bem is the always-quoted exception described below.
Omitting optional closing tags
</html>and</body>are omitted unless a following comment would move to another parent.</head>stays before ASCII whitespace or a comment. Opaquerawboundaries are handled conservatively.</li>is omitted before anotherliand at the end oful,olormenu. This applies equally to Markdown and template lists. The legacytightListfield no longer changes serialization.</td>and</th>are omitted before anothertd/thor at the end of a row.</tr>is omitted before anothertror at the end of its table or row group.</thead>is omitted immediately beforetbodyortfoot.</tbody>is omitted before anothertbodyortfoot, or at the end of its table.</tfoot>is omitted at the end of its table.</p>is omitted before the tags listed in the HTML Standard and at the end of a known HTML parent other thana,audio,del,ins,map,noscript,videoor an autonomous custom element. It stays before text, whitespace, inline elements and opaqueraw, in foreign content and in a fragment with an unknown parent. Document-formatting newlines stay outside a final paragraph.- Other optional end tags follow the table below. Non-optional end tags remain explicit.
| End tag | Omitted when followed by |
|---|---|
dt | dt or dd |
dd | dt, dd, or the end of its definition group |
rt, rp | rt, rp, or the end of ruby |
option | option, optgroup, hr, or the end of its option container |
optgroup | optgroup, hr, or the end of its parent inside select |
caption, colgroup | Anything except ASCII whitespace or a comment; an unknown raw boundary keeps the tag |
The <colgroup> start tag is omitted without emitted attributes only when the first child is col and the preceding sibling is not another colgroup with an omitted end tag. The <tbody> start tag is omitted without emitted attributes only when the first child is tr and the preceding sibling is not thead, tbody or tfoot with an omitted end tag. Empty groups and groups beginning with text or comments retain their start tags. These guards keep adjacent groups distinct.
Optional-tag rules apply to HTML elements, not SVG or MathML elements. HTML descendants at SVG/MathML integration points use HTML rules again. Minimization assumes a conforming input tree; it does not repair an invalid content model. Regression tests compare the parsed structure, namespaces, attributes, text and comments against explicit-tag output.
Whitespace and comments
- The engine adds no spaces, indentation, or formatting newlines between elements.
- Literal spaces from the source and templates are preserved (they are not stripped inside text or attributes).
- HTML comments (
<!-- -->) are not dropped or reformatted — they are output as is. - Inside
<pre><code>, the inner text is preserved as is, including the trailing\n.
Special cases
rawnodes are output unchanged — no escaping, no minimization.- The serializer preserves inline HTML supplied as
raw, including single quotes. The Markdown bridge may rewrite image paths before serialization. - Scripts inside
raware preserved verbatim; ordinary stringcontentis escaped.
Edge cases
- The
dvalue of an SVG path (d="m14.5...c...") contains sequences likec/— these must not be parsed as tags. data-bemis always emitted in double quotes.- UTF-8 characters are inserted directly (
—, Cyrillic); the serializer escapes text and attribute values; trustedrawstays unchanged.
Why not html-minifier
The engine minimizes its typed tree during serialization, so it does not need a separate finished-string postprocessor. This architectural choice does not establish that other tools reject every document with omitted optional tags. For details, see the data model decision.
One mode
There is no separate development mode with "pretty" HTML. It is the same as production: the output is always minimal. Debugging a page is easier through the output: 'bemjson' mode (see two-pass rendering) than through formatted HTML.