CSS-in-JS for ClojureScript
- Why write CSS in ClojureScript?
- Features
- How it works
- Installation
- Usage
- Production build
- Roadmap
- Contributing
- License
Writing styles this way has the same benefits as writing components that keep together view logic and presentation. It all comes to developer efficiency and maintainability.
Thease are some resources that can give you more context:
- “A Unified Styling Language” by Mark Dalgleish
- “A Unified Styling Language” (talk) by Mark Dalgleish
- “The road to styled components: CSS in component-based systems” by Glen Maddern
- Automatic scoped styles by generating unique names
- CSS pseudo-classes and pseudo-elements
- CSS animations via
@keyframes
at-rule - Injects styles into
<style>
tag at run-time - Debuggable styles in development (set via
goog.DEBUG
)
defstyles
macro expands into a function which accepts arbitrary number of arguments and returns a string of auto-generated class names that references both static and dynamic styles.
(defstyles button [bg]
{:font-size "14px"
:background-color bg})
(button "#000")
;; "-css-43696 -vars-43696"
Dynamic styles are updated via CSS Variables (see browser support).
defstyled
macro accepts var name, HTML element tag name as a keyword and a hash of styles.
The macro expands into a function which accepts optional hash of attributes and child components, and returns React element. It is available for Om, Rum and Reagent libraries. Each of them are in corresponding namespaces: cljss.om/defstyled
, cljss.rum/defstyled
and cljss.reagent/defstyled
.
A hash of attributes with dynamic CSS values as well as normal HTML attributes can be passed into underlying React element. Reading from attributes hash map can be done via anything that satisfies cljs.core/ifn?
predicate (Fn
and IFn
protocols, and normal functions). It is recommended to use keywords.
(defstyled h1 :h1
{:font-family "sans-serif"
:font-size :size
:color #(-> % :color {:light "#fff" :dark "#000"})})
(h1 {:size "32px" :color :dark} "Hello, world!")
;; (js/React.createElement "h1" #js {:className "css-43697 vars-43697"} "Hello, world!")
:css
attribute allows to define styles inline and still benefit from CSS-in-JS approach.
NOTE: This feature is supported only for Rum/Sablono elements
(def color "#000")
[:button {:css {:color color}} "Button"]
;; (js/React.createElement "button" #js {:className "css-43697 vars-43697"} "Button")
defkeyframes
macro expands into a function which accepts arbitrary number of arguments and returns a string that is both an animation name and a class name that refers to all CSS variables with dynamic values that corresponds to those arguments.
(defkeyframes spin [from to]
{:from {:transform (str "rotate(" from "deg)")
:to {:transform (str "rotate(" to "deg)")}})
[:div {:class (spin 0 180)
:style {:animation (str (spin 0 180) "500ms ease infinite")}}]
;; (js/React.createElement "div" #js {:className "animation-43697"})
Add to project.clj: [org.roman01la/cljss "1.5.0"]
(defstyles name [args] styles)
name
name of a var[args]
argumentsstyles
a hash map of styles definition
(ns example.core
(:require [cljss.core :refer [defstyles]]))
(defstyles button [bg]
{:font-size "14px"
:background-color bg})
[:div {:class (button "#fafafa")}]
(defstyled name tag-name styles)
name
name of a vartag-name
HTML tag name as a keywordstyles
a hash map of styles definition
Using Sablono templating for React
(ns example.core
(:require [sablono.core :refer [html]]
[cljss.rum :refer [defstyled]]))
(defstyled Button :button
{:padding "16px"
:margin-top :v-margin
:margin-bottom :v-margin})
(html
(Button {:v-margin "8px"
:on-click #(console.log "Click!")}))
Dynamically injected CSS:
.css-43697 {
padding: 16px;
margin-top: var(--css-43697-0);
margin-bottom: var(--css-43697-1);
}
.vars-43697 {
--css-43697-0: 8px;
--css-43697-1: 8px;
}
Set goog.DEBUG
to false
to enable fast path styles injection.
{:compiler
{:closure-defines {"goog.DEBUG" false}}}
- Media Queries syntax
- Keyframe Animation syntax
- Server-side rendering
- Pick an issue with
help wanted
label (make sure no one is working on it) - Stick to project's code style as much as possible
- Make small commits with descriptive commit messages
- Submit a PR with detailed description of what was done
Copyright © 2017 Roman Liutikov
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.