htmlforge
    Preparing search index...

    htmlforge

    HTMLForge

    ci

    A minimal, zero-dependency library for building fully-styled HTML in TypeScript/JavaScript.

    Features

    • Zero dependencies.
    • Efficient and ergonomic inline styling (using de-duplicated dynamic classes).
    • Reusable "Component"-pattern for composing common UIs.

    Quick Look

    import { HTMLDocument, NodeElement, NodeText } from "htmlforge"

    const html = new HTMLDocument()
    html.attributeAdd("lang", "en-GB")
    html.head.childAdd(
    new NodeElement("title").childAdd(
    new NodeText("Acme Title")
    )
    )

    html.body.childAdd(
    new NodeElement("div")
    .styleAdd("width", "100%")
    .styleAdd("background-color", "blue")
    .styleAdd("background-color", "red", { pseudoSelector: ":hover" })
    .childAdd(new NodeText("Hello world"))
    )

    const validHTML = html.toString()

    Installation

    Install the package from npm:

    npm install htmlforge
    

    Usage

    An HTMLForge HTMLDocument instance is a tree of nodes. Nodes come in a few flavors:

    • NodeElement: represents tags (e.g., <div>, <span>), can hold attributes and inline styles, and can nest any other node as a child.
    • NodeText: holds plain text content that will be HTML-escaped.
    • NodeRaw: holds raw HTML without escaping.
    • NodeFragment: groups a collection of child nodes without introducing a wrapping element.

    Create a new HTML document using new HTMLDocument(), set attributes on the root <html> element via html.attributeAdd, and work directly with html.head and html.body to populate content. The constructor accepts optional parameters:

    • indentCount (default 4): number of spaces used for pretty-print indentation in toString().
    • signatureDisplay (default true): whether to include the <!-- Generated by HTMLForge --> signature comment.
    import { HTMLDocument, NodeElement, NodeText } from "htmlforge"

    const html = new HTMLDocument({ indentCount: 2, signatureDisplay: false })
    .attributeAdd("lang", "en")
    .attributeAdd("data-theme", "dark")

    html.body
    .styleAdd("margin", "auto")
    .childAdd(new NodeText("Hello world"))

    NodeElement supports:

    • attributeAdd(name, value) for HTML attributes
    • styleAdd(property, value, options?) for inline styles (with optional pseudoSelector, mediaQuery parameters)
    • childAdd(node) to nest children nodes

    These calls are chainable to keep element construction compact.

    import { NodeElement, NodeText } from "htmlforge"

    const card = new NodeElement("section")
    .attributeAdd("aria-label", "profile card")
    .styleAdd("border", "1px solid #ccc")
    .childAdd(
    new NodeElement("h2").childAdd(new NodeText("Ada Lovelace"))
    )
    .childAdd(
    new NodeElement("p")
    .styleAdd("color", "#555")
    .childAdd(new NodeText("First computer programmer."))
    )

    NodeFragment groups child nodes without adding a wrapper element. It only supports childAdd (also chainable).

    import { NodeFragment, NodeElement, NodeText } from "htmlforge"

    const listItems = new NodeFragment()
    .childAdd(new NodeElement("li").childAdd(new NodeText("One")))
    .childAdd(new NodeElement("li").childAdd(new NodeText("Two")))
    .childAdd(new NodeElement("li").childAdd(new NodeText("Three")))
    • NodeText holds HTML-escaped text content (no additional methods).
    • NodeRaw injects raw HTML as-is (no additional methods).
    import { NodeText, NodeRaw } from "htmlforge"

    const safeText = new NodeText("<em>Escaped</em> output")
    const rawHtml = new NodeRaw("<em>Unescaped</em> output")

    Implement the INode interface to build reusable components. Compose a private NodeElement (style/shape it however you like) and proxy its build() method. Anything that implements INode can be passed to childAdd on NodeElement or NodeFragment.

    import type { INode } from "htmlforge"
    import { NodeElement, NodeText } from "htmlforge"

    class Alert implements INode {
    private readonly el = new NodeElement("div")
    .attributeAdd("role", "alert")
    .styleAdd("padding", "12px 16px")
    .styleAdd("background-color", "#fffae6")

    constructor(message: string) {
    this.el.childAdd(new NodeText(message))
    }

    // Optional: expose childAdd to let callers inject arbitrary child nodes
    childAdd(child: INode) {
    this.el.childAdd(child)
    return this
    }

    build() {
    return this.el.build()
    }
    }