Skip to content

html — typed HTML5 for nimony

▶️ Try aoughwl/html live in the Playground — clones the repo into the in-browser IDE, no install.

A registry of every HTML5 element and its attributes (with void / deprecated / experimental markers), plus a document-tree builder and a correct, escaping, void-aware renderer. Pure logic for nimony / Nim 3.0 — standard library only, no dependencies. It sits under web alongside css: html answers "what is valid HTML" and builds the tree, css validates styles, and web composes both.

Status — Stable and self-contained. Covers the full HTML5 element/attribute table and a complete build-and-render pipeline out to a string. It is output-only: there is no HTML parser (no string → HTMLNode), and validity queries are advisory — the renderer never rejects an invalid tag or attribute.

Quickstart

nim
import html

# --- registry: ask the baked HTML5 table ---
echo isElement("section")           # true
echo isVoidElement("br")            # true
echo isDeprecated("marquee")        # true
echo isExperimental("fencedframe")  # true
echo isAttribute("a", "href")       # true
echo isAttribute("div", "data-x")   # true  (data-* / aria-* / on* are global)
echo isAttribute("div", "href")     # false (href is not valid on <div>)

# --- document tree: build and render ---
let page = el("section", @[attr("id", "main")], @[
  el("h1", @[text("Welcome")]),
  el("p",  @[text("hello & <world>")]),
  el("input", @[attr("type", "checkbox"), flag("checked")], @[]),
])

echo $page
# <section id="main"><h1>Welcome</h1><p>hello &amp; &lt;world&gt;</p><input type="checkbox" checked></section>

API

The umbrella module html re-exports everything from html/registry and html/nodes; import html is all a caller needs.

Registry — element metadata

Backed by a baked HTML5 table (142 elements) parsed once at import time. All lookups are case-insensitive.

symbolsignaturewhat it does
isElementproc isElement(name: string): boolIs name a known HTML5 element.
isVoidElementproc isVoidElement(name: string): boolVoid element — no children, no closing tag (br, img, hr, …).
isDeprecatedproc isDeprecated(name: string): boolObsolete / deprecated element (center, font, marquee, …).
isExperimentalproc isExperimental(name: string): boolNot yet baseline (fencedframe, …).

Registry — attribute metadata

symbolsignaturewhat it does
isGlobalAttributeproc isGlobalAttribute(attr: string): boolAttribute valid on any element — the 33 named globals (id, class, style, …) plus the open-ended data-*, aria-* and on* event-handler families.
isAttributeproc isAttribute(element, attr: string): boolIs attr valid on element — true for any global, or an attribute listed for that specific element. Unknown elements accept only globals.
elementAttributesproc elementAttributes(element: string): seq[string]The element-specific attributes declared for element (excludes globals).

The parsed tables are also exported for direct inspection:

symbolsignaturewhat it does
elementslet elements: Table[string, string]tag → flag string (v void / d deprecated / x experimental, combined).
globalAttrslet globalAttrs: Table[string, string]global-attribute name → "" (membership set).
elementAttrslet elementAttrs: Table[string, string]tag → comma-separated element-specific attribute list.

Document tree — types

symbolsignaturewhat it does
HTMLNodeKindenum hnElement, hnText, hnComment, hnRawThe four node kinds.
Attrobject name, value: string; boolean: boolAn attribute; boolean marks a bare flag attribute (disabled).
HTMLNoderef object (variant over HTMLNodeKind)Element (tag, attrs, children) or a text / comment / raw node (text).
HTMLseq[HTMLNode]A document or fragment — a sequence of nodes.

Document tree — builders

symbolsignaturewhat it does
elproc el(tag: string, attrs: seq[Attr], children: seq[HTMLNode]): HTMLNodeElement with attributes and children.
elproc el(tag: string, children: seq[HTMLNode]): HTMLNodeElement with children, no attributes.
elproc el(tag: string): HTMLNodeEmpty element.
textproc text(s: string): HTMLNodeAn escaped text run.
commentproc comment(s: string): HTMLNodeA comment node (<!-- … -->).
rawNodeproc rawNode(s: string): HTMLNodeVerbatim markup, emitted unescaped.
attrproc attr(name, value: string): AttrA name="value" attribute.
flagproc flag(name: string): AttrA boolean attribute, rendered bare when present.

Document tree — mutation

symbolsignaturewhat it does
addproc add(parent: HTMLNode, child: HTMLNode)Append a child to an element node (no-op on non-elements).
setAttrproc setAttr(node: HTMLNode, name, value: string)Set / replace an attribute on an element node (no-op on non-elements).

Rendering & escaping

symbolsignaturewhat it does
renderproc render(n: HTMLNode): stringRender a node to an HTML string.
renderproc render(nodes: seq[HTMLNode]): stringRender a fragment (sequence of nodes).
$proc `$`(n: HTMLNode): stringAlias for render(n).
treeReprproc treeRepr(n: HTMLNode): stringAn indented structural view of the tree, for debugging.
escapeTextproc escapeText(s: string): stringEscape text content — &, <, >.
escapeAttrproc escapeAttr(s: string): stringEscape an attribute value — &, <, >, ".

Design notes

  • Baked table, no JSON. The HTML5 data lives in html/data.nim as three tab/newline blobs — elementBlob (tag → v/d/x flags), globalAttrBlob (global-attribute membership set) and elementAttrBlob (tag → attribute list). html/registry walks them one character at a time and builds the lookup tables once at import time — no std/json, no raising string ops. The blobs are hand-maintained; there is no code generator.
  • Advisory, not enforcing. isElement / isAttribute are for callers (a DSL, a linter, web) to consult. The renderer itself never validates — it will happily emit an unknown tag or a bogus attribute. The one place registry data reaches the renderer is isVoidElement, which suppresses children and the closing tag.
  • Correct escaping by context. Text nodes escape & < >; attribute values additionally escape ". rawNode is the deliberate escape hatch for pre-rendered markup, and boolean attributes (flag) render bare.
  • Output-only. The library builds and renders HTML; it does not parse HTML in. There is no HTMLNode-from-string path.

Requirements

  • nimony / Nim 3.0 toolchain. Standard library only (std/tables); no third-party dependencies, no C FFI, no build step — put the repo on the import path and import html.
  • Pairs with css (MDN-typed CSS validation); together they back the web DSL.

aoughwl — self-hosted platform for things n stuff. Contact / Support on Discord for access to the private backends.