Skip to content

Instantly share code, notes, and snippets.

@roman01la
Last active February 28, 2019 21:34
Show Gist options
  • Save roman01la/b7742a0b6455a5e8c81d7d251465e694 to your computer and use it in GitHub Desktop.
Save roman01la/b7742a0b6455a5e8c81d7d251465e694 to your computer and use it in GitHub Desktop.
(ns css.core
(:require [garden.core :refer [css]]
[garden.stylesheet :refer [at-keyframes at-media]]))
(def styles (atom {})) ;; styles cache
;; ignore this code, it generates unique short class names
(def cls->id (atom {}))
(def vc* (atom {:id 0
:offset 10
:msb 35
:power 1}))
(defn unique-class []
(let [{:keys [id offset msb power]} @vc*
vc (+ id offset)]
(swap! vc* #(cond-> (assoc % :id (inc id))
(= vc msb) (assoc :offset (+ offset (* 9 (inc msb))))
(= vc msb) (assoc :msb (dec (Math/pow 36 (inc power))))
(= vc msb) (assoc :power (inc power))))
(Integer/toString vc 36)))
;; </>
(defn reset-css! []
(reset! styles {})
(reset! cls->id {})
(reset! vc* {:id 0
:offset 10
:msb 35
:power 1}))
(defn css! [styles-map]
(let [h (hash styles-map)]
(if-let [cls-name (get @cls->id h)]
cls-name
(let [cls-name (unique-class)
styles-map (if (vector? styles-map)
(into [:$css$] styles-map)
[:$css$ styles-map])
css-string (-> (css {:pretty-print? false} styles-map)
(clojure.string/replace #"\$css\$" (str "." cls-name)))]
(swap! styles assoc cls-name css-string)
(swap! cls->id assoc h cls-name)
cls-name))))
(defn with-media [query styles-map cls]
(let [css-string (css {:pretty-print? false} (at-media query [(str "." cls) styles-map]))
key (hash css-string)]
(swap! styles assoc key css-string)
cls))
(defn media [query styles-map]
(with-meta [query styles-map] {::query? true}))
(defn inline-css [& styles-maps]
(let [media-queries (filter #(-> % meta ::query?) styles-maps)
styles-map (->> styles-maps
(filter #(and (or (map? %) (vector? %))
(-> % meta ::query? not)))
first)
cls-name (css! styles-map)
cls-name (reduce
(fn [cls [query styles-map]]
(with-media query styles-map cls))
cls-name
media-queries)]
cls-name))
(defmacro defstyles [sym args & styles-maps]
`(defn ~sym ~args
(let [styles-maps# (list ~@styles-maps)
class-names# (filter string? styles-maps#)
media-queries# (filter #(-> % meta ::query?) styles-maps#)
styles-map# (->> styles-maps#
(filter #(and (or (map? %) (vector? %))
(-> % meta ::query? not)))
first)
cls-name# (css! styles-map#)
cls-name# (reduce
(fn [cls# [query# styles-map#]]
(with-media query# styles-map# cls#))
cls-name#
media-queries#)]
(->> (conj class-names# cls-name#)
(clojure.string/join " ")))))
(defmacro defkeyframes [sym args & keyframes]
(let [keyframes-name (str "@" `~sym)]
`(defn ~sym ~args
(let [css-string# (css {:pretty-print? false} (at-keyframes ~(keyword (str `~sym)) ~@keyframes))]
(swap! styles assoc ~keyframes-name css-string#)
~(str `~sym)))))
;; Usage
(defstyles flex-row []
{:display "flex"})
(defstyles flex-column []
{:display "flex"
:flex-direction "column"})
(defstyles flex-center []
{:align-items "center"
:justify-content "center"})
(defstyles css-side-bar []
(flex-fill) ;; compose styles
(flex-column)
{:max-width "280px"
:border-right "1px solid #eee"
:padding "32px 16px 0 32px"
:background "#fff"
:overflow-y "auto"
:-webkit-overflow-scrolling "touch"}
(media {:max-width "460px"} ;; media queries
{:position "fixed"
:top 0
:left 0
:height "100vh"
:box-shadow "0 2px 16px rgba(0, 0, 0, 0.1)"}))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment