Skip to content

Declarative HTML Elements

LoboGuardian 🐺 edited this page Jul 11, 2026 · 9 revisions

HTeaLeaf provides a declarative Python API for building HTML pages — no templates, no strings. Each HTML tag is represented by a Python function, and attributes, styles, or child elements are defined using a clean, chainable syntax.

Basic example

from htealeaf.elements import html, head, body, h1, div, button

page = html(
    head(),
    body(
        h1("Welcome to HTeaLeaf!"),
        div("This content is rendered from Python."),
        button("Click me!")
    )
)

Each HTML tag in HTeaLeaf is represented by a Python function They all share the same pattern:

div(children...)          # create a <div>
div().attr(name=value)    # add an attribute
div().classes("a b")      # add CSS classes (one space-separated string)
div().style(color="red")  # inline style
div().id("main")          # set the id attribute

Attirbutes

Attributes are added using .attr() it accepts any standard HTML attribute or event handler:

button("Save").attr(id="saveBtn", onclick="alert('Saved!')")
form().action("/submit").method("POST")
textInput().attr(name="email", placeholder="Enter your email")

Classes and style

HTeaLeaf makes it easy to assign classes or inline styles. .classes() takes a single space-separated string; .style() converts snake_case keyword names to CSS kebab-case:

div("Hello").classes("card highlight")
h1("Title").style(color="green", text_align="center")

For layout, div has two flexbox shortcuts:

div(a, b, c).row()     # display: flex; flex-direction: row
div(a, b, c).column()  # display: flex; flex-direction: column

Stylesheets from Python

Beyond inline styles, the style() element accepts a nested dict and compiles it to CSS. Nested dicts become descendant selectors, and a leading & concatenates with the parent selector (like SCSS):

from htealeaf.elements import style

style({
    "body": {"background_color": "teal"},
    ".card": {
        "padding": "10px",
        "&:hover": {"box_shadow": "0 0 4px gray"},   # .card:hover
        "h2": {"margin": "0"},                        # .card h2
    },
})

style(href="...") links an external stylesheet instead.

Event handling

Since HTeaLeaf integrates with its reactivity layer, event attributes like onclick can be tied to Store actions or custom JavaScript code (JSCode and JS from Python).

from htealeaf import Store
from htealeaf.elements import div, h1, button

store = Store({"counter": 0})

div(
    button("-").attr(onclick=store.js.update("counter", store.read("counter") - 1)),
    h1(store.read("counter")),
    button("+").attr(onclick=store.js.update("counter", store.read("counter") + 1)),
)

Here, the buttons directly update a reactive Store; after the mutation the client re-fetches the route and reconciles the DOM, so the counter re-renders automatically in the browser. No manual JS required.

Dynamic content

You can insert any Python value or expression as a child:

user = "Alb"
div(f"Hello {user}!")  # produces <div>Hello Alb!</div>

Lists of elements are supported:

div(
    [h2(f"Item {i}") for i in range(5)]
)

Common helpers

HTeaLeaf includes a few specialized helpers that map to common patterns:

Function Description
textInput() creates a text <input>
submit(label) <input type="submit">label</input>
checkbox(checked=False) <input type="checkbox">
form(...).action(url).method(verb) <form> with chainable action/method
select(items) / option(value) <select> built from a list of strings
label(...) <label> tag
Table / thead / tr / th / td table building blocks (import from htealeaf.elements.elements)
link() <link> tag for stylesheets
script(code) <script> tag (content is emitted verbatim)
header() semantic layout tag
tl_if(condition, *childs) wraps children in a div whose display is driven by the condition (accepts JSCode, so it can react to client state); import from htealeaf.elements.elements

Standard containers and headings — html, head, body, div, h1h3, button — are importable from htealeaf.elements as well. Table/td/tr/th/thead and tl_if are not re-exported there yet, so they come from the inner htealeaf.elements.elements module. Tags without a helper class (e.g. p, footer) don't exist yet; compose with div or contribute the element upstream.

Component utility methods

Every element also supports:

card = div().classes("card")
card.append(h2("Title"))     # add a child at the end
card.prepend(link())         # add a child at the start
card.id("main-card")         # set id and return self
card.get_child("h2")         # find a direct child by tag name

Composition and reuse

You can define reusable components as simple Python functions:

def Card(title, content):
    return div(
        h2(title),
        div(content)
    ).classes("card").style(padding="10px")

@app.route("/")
def home():
    return html(
        body(
            Card("Welcome", "This is a HTeaLeaf component."),
            Card("Info", "Reusable and declarative!")
        )
    )

Next: Store

Clone this wiki locally