/fulcro-garden-css

Garden CSS co-location on Fulcro (3+) Components

Primary LanguageClojure

fulcro garden css

This library adds co-located CSS support to Fulcro 3 components. Fulcro 2.x and earlier have this integrated by default, but 3+ enables this to be a pure library concern which allows more room for others to provide alternate approaches to solving the same problem.

  1. Add this library to your project.

  2. Add Garden CSS to your components

  3. Use localized-dom, 4th arg destructuring, or direct css/get-classnames to access/use the generated CSS class names.

(ns some-app
  (:require
    [com.fulcrologic.fulcro-css.localized-dom :as dom]
    [com.fulcrologic.fulcro-css.css-injection :as inj]
    [com.fulcrologic.fulcro-css.css :as css]))

...

;; OPTION 1: 4th arg destructing (requires adding props middleware)
(defsc UIElement [this props computed {:keys [red]}]
  {:query ...
   :css   [[:.red {:color "red"}]]}

  ;; OPTION 2: Destructure them explicitly
  (let [{:keys [red]} (css/get-classnames UIElement)]
    ;; OPTION 3: Use `localized-dom` keyword classes instead of `dom` for elements
    (dom/div :.red
      (dom/li {:classes [red]})))
  ...)

(defsc Root [this props]
  {...normal options ...}
  (dom/div {}
    ;; Auto-scan the query to find components with CSS and inject it
    (inj/style-element {:component Root})
    ...))

To include CSS from components not present in the parent’s query, use :css-include:

(defsc UtilityComponent [this props comp-props]
 {:css [[:.red {:color "red"}]]}
 (dom/div :.red))

(def ui-utility-component ...)


(defsc Root [this props]
  {...normal options ...
   ;; Include untracked component
   :css-include [UtilityComponent]}
  (dom/div {}
    (ui-utility-component ...)
    (inj/style-element {:component Root})
    ...))

The styles produced by :css will get a name unique to the component, which prevents conflicts with any other components declaring a style of the same name. You can also produce a global style (i.e. the name will be exactly as provided) by using the key :css-global. Both can be used together.

Fulcro 3 gives support for library to add things to the extra-props (4th) argument to components. This can be syntactically convenient, but beware that it adds a slight bit of overhead to every component in your system.

This library does not require you to add the middleware unless you want the 4th argument to contain your munged CSS classnames as simple names. To add it use Fulcro’s wrap-update-extra-props middleware utility:

(ns fulcro-todomvc.main
  (:require
    [com.fulcrologic.fulcro.components :as comp]
    [com.fulcrologic.fulcro-css.css :as css]))

(def app (app/fulcro-app {:props-middleware (comp/wrap-update-extra-props
                                               (fn [cls extra-props]
                                                 (merge extra-props (css/get-classnames cls))))
                          ...}))

The localized DOM API allows you to use the keyword shortcut support to specify the localized classnames:

(ldom/div :.red ...)

If you need to access global classnames without munging, use a $ as a prefix instead of .:

(ldom/div :.red$big ...)

The localized DOM can be mixed with non-localized by requiring them under different namespaces.

;; Use Fulcro's normal dom to get <div class="big">
(dom/div :.big ...)

;; Use this library's dom to get <div class="some_namespace_Component__red">
(ldom/div :.red ...)

The overhead of most of these features should be unnoticeable in most applications. In case you find a situation where you need more performance it can be gained by:

  1. Do not install the extra props middleware

  2. Use manual destructuring once per class in a let.

  3. Use the :classes argument in the normal Fulcro DOM.

(let [{:keys [red]} (css/get-classnames UIElement)]
  (dom/div {:classes [red] ...))