.. currentmodule:: minihtml .. TODO The outline can be improved. .. _basics: Basics ====== Installation ------------ Install the `minihtml` package from PyPI: .. code-block:: console $ pip install minihtml or: .. code-block:: console $ uv add minihtml .. _prototypes: Prototypes ---------- All minihtml HTML elements are created from a *Prototype*. A prototype is an immutable element factory that produces :ref:`elements`. You can import the prototypes for all HTML5 elements from :ref:`minihtml.tags `: >>> from minihtml.tags import html, head, title >>> html Converting an element to a string returns its HTML representation. There are several ways to produce an element from a prototype. Calling the prototype produces a new element: >>> elem = html() >>> elem >>> print(elem) Positional arguments add content to the element. Content can be other elements or strings: >>> elem = html(head(title("hello, world!"))) >>> print(elem) hello, world! Keyword arguments are converted to HTML :ref:`attributes `: >>> elem = html(lang="en") >>> print(elem) Indexing a prototype with a string is a convenient way to set the new element's ``class`` and/or ``id`` attributes: >>> from minihtml.tags import div >>> elem = div["#my-id class-a class-b"] >>> print(elem)
Using a prototype as a context manager creates an *element context*: New elements created within the context are added as children to the parent element (the element returned by the context manager): >>> with html as elem: # creates an element context with parent element `elem` (html) ... with head: # creates a nested element context ... title("hello, world!") <...> >>> print(elem) hello, world! As you can see in the example above, it does not matter how the elements are created, from a context manager (``with head:``) or by calling (``title()``), they are always added to the parent element of the containing context. There is one important exception: Elements passed as positional arguments to another element or prototype are exempt from this, so this works as expected: >>> from minihtml.tags import p, em >>> with div as elem: ... p(em("this em element is a child of p, not div")) <...> >>> print(elem)

this em element is a child of p, not div

Finally, you can create new prototypes with the :func:`make_prototype` factory function: >>> from minihtml import make_prototype >>> custom_element = make_prototype("custom-element") >>> custom_element >>> elem = custom_element() >>> print(elem) .. _elements: Elements -------- Elements provide the same APIs as :ref:`prototypes`, but they are mutable. Calling, indexing or using an element as a context manager returns the element itself, so all operations can be chained to modify the element further: >>> from minihtml.tags import a >>> elem = a["repo"](href="https://github.com/trendels/minihtml")("minihtml") >>> print(elem) minihtml >>> with html(lang="en") as elem: ... with div["#main"]: ... p("content") <...> >>> print(elem)

content

.. _attributes: Attributes ---------- When setting attributes via keyword arguments, the names of keyword arguments are modified according to these rules: - If the name is ``_`` (a single underscore), it is not changed. - Any trailing underscores are stripped from the name. - Any remaining underscores are converted to hyphens (``-``). Examples: >>> print(div(_="something"))
>>> print(div(foo_bar="x"))
>>> from minihtml.tags import label >>> print(label(for_="target")("Label")) >>> print(div(__foo="bar"))
Attribute values are quoted (HTML-escaped) automatically: >>> print(div(attr='a value with "quotes" and a '))
Boolean attributes ^^^^^^^^^^^^^^^^^^ A boolean HTML attribute is one that does not have a value. For example, to mark an input field as required, you can write ````. There is also an alternative syntax, which minihtml uses: ````. To set a boolean attribute, pass ``True`` as the value. ``False`` is also a valid value, and causes the attribute to be omitted (this can be useful if you set the attribute from a variable): >>> from minihtml.tags import input >>> print(input(required=True, disabled=False)) .. _content: Content ------- We have already seen that elements and strings can be passed to elements as content. String content is also HTML-escaped automatically. To include strings as HTML, use the :func:`safe` helper function. Only use this if the content comes from a trusted source, to prevent `Cross Site Scripting (XSS) vulnerabilities `_! >>> print(div("content with a "))
content with a <tag>
>>> from minihtml import safe >>> print(div(safe("inline html")))
inline html
To add text content to the parent element inside an element context, you can use the :func:`text` helper function (:func:`safe` can be used in the same way): >>> from minihtml import text >>> with div as elem: ... text("text") ... text(" and more text") <...> >>> print(elem)
text and more text
Sometimes you might need to pass around a group of elements that do not share a common parent element. This is called a *Fragment*. Fragments are created using the :func:`fragment` function: >>> from minihtml import fragment >>> f = fragment(p("one"), p("two"), "three") >>> print(f)

one

two

three :func:`fragment` can also be used in an element context: >>> with div as elem: ... fragment(p("one"), p("two"), "three") <...> >>> print(elem)

one

two

three