Skip to content

lonnycorp/htmlforge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

HTML structure

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.

Creating an HTML document

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 nodes

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 nodes

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")))

Text and Raw nodes

  • 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")

Define your own nodes

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()
    }
}