Python
Home-UI is server-rendered HTML plus two static files, so it fits Django or Flask with no client build: a base template loads the pair, the template writes the elements, and the view reads their values on POST.
Load the two files
Section titled “Load the two files”A Django base template:
<!doctype html><html lang="en" class="{{ theme }}"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="https://home-ui.harriverse.com/dist/latest/home-ui.css" /> <script type="module" src="https://home-ui.harriverse.com/dist/latest/home-ui.js"></script> </head> <body class="hui-surface"> {% block content %}{% endblock %} </body></html>In Flask, Jinja2 is the same shape with url_for('static', filename='home-ui.css') if you self-host. class="light"
or class="dark" is read server-side so there is no flash of the wrong theme; hui-surface opts
the page into the tokens (see Dark mode).
Those URLs load the current build from this site. For something you ship, download the files and serve them yourself - see Getting the files.
Components are attributes
Section titled “Components are attributes”A component is configured entirely by attributes, so a template writes them like any other tag:
<hui-field> <span data-label>Project name</span> <hui-input data-control name="project" value="{{ project.name }}" {% if errors.project %}aria-invalid="true"{% endif %}></hui-input> {% if errors.project %}<span data-error>{{ errors.project }}</span>{% endif %}</hui-field>Reading values on POST
Section titled “Reading values on POST”Every form-associated control submits under its name, so the view reads it like any native field:
# Djangodef save(request): if request.method == "POST": project = request.POST["project"] volume = request.POST["volume"] # from hui-slider, a string# Flask@app.post("/save")def save(): project = request.form["project"] volume = request.form["volume"]A hui-slider in range mode submits one joined "low,high" value under its name, as a native
control would.
Validation errors
Section titled “Validation errors”Business validation is the server’s. On a failed POST, re-render the same template with the
submitted values and an error per field; the template above marks the control aria-invalid and
wires the message to it. The client does not reconstruct that.
<form method="post"> {% csrf_token %} <!-- hui-field, hui-input and the rest --> <hui-button type="submit">Save</hui-button></form>hui-button type="submit" drives the ancestor form, so it can replace <button type="submit">
wherever you want the component’s styling.
Partial updates
Section titled “Partial updates”To update part of the page, render a fragment and swap it in. Any technique works - fetch,
HTMX, or a framework of your own; the element is created from the new
markup and re-associates with its form. Keep endpoints, tokens and record ids on the server: there
is no attribute for them, deliberately. The full model is in
server-side mechanics.