bri.html
HTML is a function over data: hiccup-style vectors in, escaped HTML out. No template language, no layouts, no partials, no asset pipeline — html/form is the deliberate outer boundary of the surface, and CSS is a file in public/ served by (bri.http/dir "public") (see bri.http).
Elements
Section titled “Elements”(html/render [:h1 "hello"]) ; => "<h1>hello</h1>"(html/render [:a {:href "/x"} "go"]) ; attrs map second(html/render [:p.hint#top "y"]) ; tag sugar: class/id(html/render [:ul (for [x xs] [:li x])]) ; seqs splice- Strings and numbers are text nodes, escaped — always.
nilrenders nothing (whencompositions just work).- Boolean attrs:
{:disabled true}→disabled;false/nilomit. - Void tags (
br,img,input,link,meta, …) self-close per the HTML spec.
XSS-safe by construction
Section titled “XSS-safe by construction”Every text node and attribute value is escaped. The opt-out is explicit and ugly, on purpose:
(html/unsafe-raw-html trusted-markup) ; you own every byteDocuments
Section titled “Documents”(html/page {:title "myapp"} ; opts map optional [:main [:h1 "It's alive."]])emits the full <!doctype html> document — charset, viewport, title, and the stylesheet link (default /static/app.css, override with :stylesheet).
Forms carry the token
Section titled “Forms carry the token”(html/form {:post "/signup"} [:input {:name "email"}] [:button "Sign up"])html/form mints the CSRF token (a hidden __csrf field) from the request the csrf middleware bound — which is why the browser POST passes the default middleware stack. {:get "/search"} makes a GET form (no token needed); other opts keys pass through as attributes. It returns hiccup data: compose it, then render/page it.
JSON is equally first-class: return a map/vector body and the negotiate middleware encodes it (see bri.http).
Where next
Section titled “Where next”- bri.http — the handlers and middleware these pages flow through
- bri.config — profiles, env overlay, the schema
- Tutorial — build the app these pieces belong to
Questions or feedback on this page? Comment below with your GitHub account — comments are public and live in the project's GitHub Discussions.