Skip to content
A nice tool to use in the Garden
Clojure Python HTML CSS
Branch: master
Clone or download

Latest commit

Fetching latest commit…
Cannot retrieve the latest commit at this time.


Type Name Latest commit message Commit time
Failed to load latest commit information.

spade Build Status Clojars Project cljdoc

A nice tool to use in the Garden


Spade is a lightweight css-modules-inspired CSS-in-clojurescript library. It is built on top of garden for powerful, intuitive, programmatic style generation.


Add to whichever dependency manager you prefer, substituting LATEST-VERSION for the one indicated at the top of this file:

; leiningen, shadow-cljs.edn, etc:
[net.dhleong/spade "LATEST-VERSION"]

; deps.edn:
{net.dhleong/spade {:mvn/version "LATEST-VERSION"}}

Basic usage

Similar to Herb, Spade leans on functions and macros to allow you to dynamically generate styles. Where Herb uses macros at the call site, however, Spade uses them at the declaration site to enable a much richer, more intuitive syntax:

(ns co.serenity
  (:require [spade.core :refer [defclass]]))

(defclass ship-style []
  {:background "#999"}
  [:.wing {:background "#777"}])

Notice how we don't have to return a single value from our defclass, but can instead return multiple statements. The first map will apply to whatever element gets the class, and the rest are used for the children. The above would translate naturally to garden syntax as:

[:.ship {:background "#999"}
 [:.wing {:background "#777"}]]

defclass creates a function, which is what that empty vector is for. If you wanted to be able to dynamically choose the ship's wing colors, you could write:

(defclass ship-style [wing-color]
  {:background "#999"}
  [:.wing {:background wing-color}])

When you call the function generated by defclass, it inserts a new <style> element into <head> if necessary, and returns a class name. This name is globally unique based on the parameters passed, so if you have many ships with the same wing color, they will all share the same class name and a single <style> declaration.

To use this in reagent, you might do:

(defn ship [wing-color]
  [:div {:class (ship-style wing-color)}

Since this pattern is quite common, Spade comes with a convenience:

(ns co.serenity
  (:require [spade.core :refer [defattrs]]))

; defattrs is identical in syntax to defclass, but returns an attributes
; map for use in hiccup-based frameworks like reagent
(defattrs ship-attrs [wing-color]
  {:background "#999"}
  [:.wing {:background wing-color}])

(defn ship [wing-color]
  [:div (ship-attrs wing-color)

Global styles

Sometimes you need to create global styles. No problem!

(ns co.serenity
  (:require [spade.core :refer [defglobal]]))

(defglobal window-styles
 [:body {:background "#333"}])

Global styles still require a unique name for hotswapping purposes, but cannot accept parameters. They are inserted into the DOM as soon as they are evaluated.

Style Composition

Spade supports composing styles just like css-modules:

(defclass stealth-ship []
  {:composes (ship-style "#111")
   :background "#111"})

The :composes key is only supported on the root element of a style.

Media queries

Spade supports @media queries in the exact same way you see them in the garden documentation:

(defclass carrier-style []
  (at-media {:min-width "750px"}
    {:padding "80px"})
  {:padding "8px"})


Spade even supports generating @keyframes just like you'd expect:

(ns co.serenity
  (:require [spade.core :refer [defkeyframes]]))

(defkeyframes anim-frames []
  ["0%" {:opacity 0}]
  ["100%" {:opacity 1}])

defkeyframes, like defclass, generates a function that inserts the appropriate CSS into the DOM on-demand, and returns the animation identifier:

(defclass serenity []
  {:animation [[(anim-frames) "560ms" 'ease-in-out]]})


To get an interactive development environment run:

lein figwheel

and open your browser at localhost:3449. This will auto compile and send all changes to the browser without the need to reload. After the compilation process is complete, you will get a Browser Connected REPL. An easy way to try it is:

(js/alert "Am I connected?")

and you should see an alert in the browser window.


Copyright © 2019 Daniel Leong

Distributed under the Eclipse Public License either version 1.0

You can’t perform that action at this time.