Skip to content

Instantly share code, notes, and snippets.

@tekacs
Created March 7, 2020 20:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tekacs/302eb450f99b511e609ca54de48eeb5e to your computer and use it in GitHub Desktop.
Save tekacs/302eb450f99b511e609ca54de48eeb5e to your computer and use it in GitHub Desktop.
Loading CSS into my application using shadow-cljs
(ns app.build.css
(:require [shadow.resource :as rc]
[clojure.string :as string]))
(defmacro load-from-classpath! [path & {:keys [friendly-name]}]
"Load CSS from a node module (as a resource) in the node_modules/ directory of these sources."
`(app.build.css/upsert! ~(or friendly-name path) (rc/inline ~(str path))))
(defmacro load-from-file! [style-name]
"Load CSS from a file (as a resource) in the css/ directory of these sources."
(let [friendly-name (string/replace (str style-name) #"^:" "")
filename (str friendly-name ".css")]
`(load-from-classpath! ~(str "app/css/" filename) :friendly-name ~friendly-name)))
(defmacro load-from-url! [ident url]
"Load CSS from a URL on the web, pulling it down at compile-time."
`(app.build.css/upsert! ~ident ~(slurp url)))
(defmacro load-from-unpkg! [path]
"Load CSS from unpkg.com, pulling it down at compile-time."
`(app.build.css/upsert! ~path ~(slurp (str "https://unpkg.com/" path))))
(ns app.build.css
(:require [app.access :as a])
(:require-macros [app.build.css]))
(defonce ^:private element-cache$ (atom {}))
(defn upsert! [ident css]
"Insert CSS into the head of the page, updating it if inserted previously. Called by app.build.css/load-*"
(let [css (str "/*# sourceURL=" ident " */" css)]
(if-let [[_ textNode] (@element-cache$ ident)]
(a/assoc! textNode :nodeValue css)
(let [style (a/document! :createElement "style")
textNode (a/document! :createTextNode css)]
(a/assoc! style :type "text/css")
(a/call! style :appendChild textNode)
(a/document! [:head :appendChild] style)
(swap! element-cache$ assoc ident [style textNode])
ident)))) ; Return the identifier that can be passed to unload to remove this later.
(defn unload! [ident]
"Unload a particular stylesheet by identifier."
(let [[style _] (@element-cache$ ident)]
(a/document! [:head :removeChild] style)
(swap! element-cache$ dissoc ident)))
(defn clear! []
"Clear CSS that was loaded using upsert-css! from the page, to reset."
(dorun (map unload! (keys @element-cache$))))
(ns app.hooks)
(defmacro useCSS [ident]
`(helix.hooks/use-effect*
#(let [ident# (app.build.css/load-from-file! ~ident)]
(fn [] (app.build.css/unload! ident#)))))
;; SNIP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment