4.9 KiB
Intro to Hiccup
Introduction
hiccup is a html DSL, used extensively in the clojure's eco-system, there are others as well but hiccup is the most widely used.
In hiccup everything is a list, this means you can easily compose html using standard language constructs. To render the hiccup to html elements we call the (html) function from the hiccup library for server side with reagent or client side code this will likely be a different function like (render).
There are a few variant's the main ones being https://github.com/weavejester/hiccup and https://github.com/lambdaisland/hiccup , the lambdaisland one is particular helpful if you want to share html between the frontend and backend as its closer to reagents builtin hiccup.
Simple examples
If using reagent you don't need to pass into h/html but this is server side reagent also has some helpers to work with react nicer.
[:span "bar"]
Styles classes and ID's
Attributes are added as a map of values styles are also a map
[:span {:class "class1 class2" :title "my title" :style {:color "red"}} "bar"]
You can use shorthand to add id's and classes
[:span#id.class1.class2 "bar"]
Example of manipulating hiccup with clojure functions
You can use clojure core language to manipulate these vectors.
place parts inside another containing element
(into [:div.container]
[[:span "span 1"]
[:span "span 2"]])
You could also use merge, in this example the spans are merged inside the div vector.
(merge [:div] [:span "span 1"] [:span "span 2"] [:span "span 3"])
We can take advantage of lazyness if we like
(defn navbar-link [{:keys [href title text] :or {text href title nil} :as link}]
[:a.link.dim.white.dib.mr3 {:key href :href href :title title} text])
[:div (into [:nav.f6.fw6.ttu.tracked] (vec (take 2 (mapv navbar-link
[{:key "link1" :href "link1" :title "title here"}
{:key "link2" :href "link2" :title nil}
{:key "link3" :href "link3" :text "link text"}
{:key "link4" :href "link4"}]))))]
Compossible components
The main advantage comes from the ability to compose the parts together, so we can break our html apart and recombine using all the function at our disposal. In this example our navigation is defined as a hash map, the data is separated out from the html, we can then pass the data to our component to render it. In this example we have a link component and a nav component the link component take the values as key, value pairs and uses de structuring while also setting default if values are not set.
(defn navbar-link [{:keys [href title text] :or {text href title nil} :as link}]
[:a.link.dim.white.dib.mr3 {:key href :href href :title title} text])
(defn navbar [links]
[:header.bg-black-90.w-100.ph3.pv3.pv4-ns.ph4-m.ph5-l
(into [:nav.f6.fw6.ttu.tracked]
(mapv navbar-link links))])
[navbar [{:href "link1" :title "title here"}
{:href "link2" :title nil}
{:href "link3" :text "link text"}
{:href "link4"}]]
In this example we create some simple top trump style cards using a map with a vector of nested maps for the stats. We use map and into to convert the stats value
(defn playing-card
[{:keys [title image stats]}]
[:article.fl.br2.ba.dark-gray.b--black-10.ma2.w-100.h-200.w-50-m.w-25-l.mw5
[:div.pa2.ph3-ns.pb3-ns
[:div.dt.w-100.mt1
[:div [:h1.f5.f4-ns.mv0 title]]
[:img.db.w-100.h-100.br2.br--top {:src image}]
#_[:div.dtc.tr [:h2.f5.mv0 "amount"]]]
(into [:ul.ma2.pa2]
(mapv (fn build-stats [[stat value]]
[:li [:div stat [:div.fr (str value)]]])
stats))
[:p.f6.lh-copy.measure.mt2.mid-gray ""]]])
[:div
[playing-card
{:title "Bee"
:image "https://loremflickr.com/300/300/bee"
:stats {:strength "6"
:lifespan "3" ;; 6 weeks
:danger "5"
:mobility "9"}}]
[playing-card
{:title "Spider"
:image "https://loremflickr.com/300/300/spider"
:stats {:strength "4"
:lifespan "5" ;; 1 year
:danger "8"
:mobility "5"}}]
[playing-card
{:title "Ant"
:image "https://loremflickr.com/300/300/ant"
:stats {:strength "8"
:lifespan "8" ;; 7 years
:danger "4"
:mobility "3"}}]
[playing-card
{:title "Dragonfly"
:image "https://loremflickr.com/300/300/dragonfly"
:stats {:strength "2"
:lifespan "1" ;;2 weeks
:danger "1"
:mobility "9"}}]]