https://github.com/escherize/huff
Juicy hiccup in pure Clojure
https://github.com/escherize/huff
Last synced: 9 months ago
JSON representation
Juicy hiccup in pure Clojure
- Host: GitHub
- URL: https://github.com/escherize/huff
- Owner: escherize
- License: epl-2.0
- Created: 2023-06-06T13:44:30.000Z (about 3 years ago)
- Default Branch: master
- Last Pushed: 2025-07-13T14:43:31.000Z (12 months ago)
- Last Synced: 2025-09-20T16:24:02.186Z (10 months ago)
- Language: Clojure
- Homepage:
- Size: 585 KB
- Stars: 117
- Watchers: 3
- Forks: 5
- Open Issues: 0
-
Metadata Files:
- Readme: readme.md
Awesome Lists containing this project
README
# huff
Hiccup in pure Clojure
## Usage
[](https://clojars.org/io.github.escherize/huff)
``` clojure
(require '[huff2.core :as h])
```
## Rationale
When it comes to hiccup libraries, there's a venn-diagram "has ergonomic and modern affordances" and "works on babashka". So [huff](https://github.com/escherize/huff) is my way of saying why not both?
- [Weavejester's hiccup library](https://github.com/weavejester/hiccup) runs on babashka, but is missing some of the newer features hiccup afficianados have come to demand.
- [Lambda Island's hiccup](https://github.com/lambdaisland/hiccup) also provides a modern api, but overall I'd still call it a subset of huff's features.
## Features
- 🏭 Use [**functions** as **components**](#use-functons-as-components)
- 🎨 Style maps work as you'd expect `[:div {:style {:font-size 30}}]`
- 🔀 Include classes and ids tags [in any order](#tag-parsing)
- `:div.a#id.b` or `:div.a.c#id` or `:#id.a.c` all work!
- 🔒️ HTML-encoded by default
- 👵 Runs on [babashka](https://github.com/babashka/babashka) (unlike `lambdaisland/hiccup`)
- 🏎 Performance: 22-48% faster than hiccup/hiccup for runtime-generated HTML [without pre-compilation](https://github.com/escherize/huff/issues/8)
- 🙂 Hiccup-style fragments to return multiple forms `(list [:li.a] [:li.b])`
- 🙃 Reagent-style fragments to return multiple forms `[:<> [:li.a] [:li.b]]`
- 🤐 Extreme shorthand syntax `[:. {:color :red}]` `
`
- 🦺 Tested against slightly modified hiccup 2 tests
- 🪗 [Extensible grammar](#extendable-grammar)
- 📦 [raw-strings](https://github.com/escherize/huff/issues/5)
### Tag Parsing
Parse tags for id and class (in any order).
```clojure
(h/html [:div.hello#world "!"])
;; =>
!
```
#### Nested tag parsing
```clojure
(println (h/html [:div.left-aligned>p#user-parent>span {:id "user-name"} "Jason"]))
;=>
Jason
```
### [reagent](https://github.com/reagent-project/reagent)-like fragment tags
```clojure
(h/html [:<> [:div "d"] [:<> [:<> [:span "s"]]]])
;; =>
ds
```
This is useful for returning multiple elements from a function:
```clojure
(defn twins [x] [:<>
[:div.a x]
[:div.b x]])
(h/html [:span.parent [twins "elements"]])
;;=>
elements
elements
```
Nest and combine them with lists to better convey intent to expand:
``` clojure
(h/html
[:ol
[:<> (for [x [1 2]]
[:li>p.green {:id (str "id-" x)} x])]])
;;=>
-
1
-
2
```
### Style map rendering
```clojure
(h/html [:div {:style {:border "1px red solid"
:background-color "#ff00ff"}}])
;; =>
(h/html [:. {:style {:width (h/px 3)}}])
;;=>
```
### Raw HTML tag:
This is nice if you want to e.g. render markdown in the middle of your hiccup. Note: We disable this by default and it must be manually enabled in the call to `html` or `page`,
``` clojure
(h/html [:hiccup/raw-html "
raw"])
;; =Throws=> :hiccup/raw-html is not allowed. Maybe you meant to set allow-raw to true?
(h/html {:allow-raw true} [:hiccup/raw-html "
raw"])
;;=> raw
```
Another nice-to-have is to disallow raw html in un-trusted data getting passed into to the compiler, but being able to do that as a dev.
``` clojure
(h/html [:div [:hiccup/raw-html (h/raw-string "