Last active
August 13, 2018 09:43
-
-
Save danielneal/6c5b7b5bdad63742574035d4f97a07ef to your computer and use it in GitHub Desktop.
Clojure tachyons for react native
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns onions.core | |
"Clojure tachyons for react native. | |
Provides a shorthand to a constrained set of atomic css properties i.e. | |
(style [:flx-row :aifs :jcsb :bg-ui0 :br1]) ; => | |
{:flexDirection "row", | |
:alignItems "flex-start", | |
:justifyContent "space-between", | |
:backgroundColor "#2a2a2a", | |
:borderRadius 2} | |
Rationale (from https://tachyons.io/) | |
Design Systems break as they scale (either scaling org or scaling product) | |
because new components/variants of a component are introduced. Those variants | |
sometimes (read: often) go undocumented, leading to duplication when that | |
component/variant is needed (and created) again. Even when the component is | |
documented, documenting effectively often means dozens/hundreds of instances | |
to capture all states/variants. Systems like Tachyons et al. approach this | |
problem by instead documenting and limiting *properties* of components. (I | |
like to think of this as “subatomic” design.) | |
-- Daniel Eden (Facebook)" | |
(:require [clojure.test :refer [is]])) | |
(def rem 18) | |
(def font-rem 18) | |
(defn kwd | |
"Forms a single keyword by concatenating strings or keywords" | |
[& strs] | |
(keyword (apply str (map #(if (keyword? %) (name %) %) strs)))) | |
(def flex | |
"Flex properties" | |
{:flx-1 {:flex 1} | |
:flx-row {:flexDirection "row"} | |
:flx-row-reverse {:flexDirection "row-reverse"} | |
:flx-col-reverse {:flexDirection "column-reverse"} | |
:flx-wrap {:flexWrap "wrap"} | |
:aifs {:alignItems "flex-start"} | |
:aic {:alignItems "center"} | |
:aife {:alignItems "flex-end"} | |
:jcc {:justifyContent "center"} | |
:jcfe {:justifyContent "flex-end"} | |
:jcsb {:justifyContent "space-between"} | |
:jcsa {:justifyContent "space-around"} | |
:asfs {:alignSelf "flex-start"} | |
:asfe {:alignSelf "flex-end"} | |
:asc {:alignSelf "center"} | |
:ass {:alignSelf "stretch"}}) | |
(def spacing | |
"Margin and padding properties | |
ma0 ... ma8 margin: 0|0.25|0.5|1|2|4|8|16|32 rem | |
ml|mr|mb|mt [0-8] marginLeft, marginRight, marginBottom, marginTop | |
mh [0-8] marginHorizontal | |
mv [0-8] marginVertical" | |
(let [scale [["0" 0] | |
["1" 0.25] | |
["2" 0.5] | |
["3" 1] | |
["4" 2] | |
["5" 4] | |
["6" 8] | |
["7" 16] | |
["8" 32]]] | |
(into {} | |
(for [[pre k] [[:ma :margin] | |
[:ml :marginLeft] | |
[:mr :marginRight] | |
[:mt :marginTop] | |
[:mb :marginBottom] | |
[:mh :marginHorizontal] | |
[:mv :marginVertical] | |
[:p :padding] | |
[:pl :paddingLeft] | |
[:pr :paddingRight] | |
[:pt :paddingTop] | |
[:pb :paddingBottom] | |
[:ph :paddingHorizontal] | |
[:ph :paddingVertical]] | |
[s fac] scale] | |
[(kwd pre s) {k (int (* fac rem))}])))) | |
(def heights | |
"Heights and widths | |
h1 ... h6 height: 1|2|4|8|16|32 rem | |
w1 ... w6 width: 1|2|4|8|16|32 rem | |
min-h1 ... min-h6 minHeight: 1|2|4|8|16|32 rem | |
max-h1 ... max-h6 maxHeight: 1|2|4|8|16|32 rem" | |
(let [scale [["1" 1] | |
["2" 2] | |
["3" 4] | |
["4" 8] | |
["5" 16] | |
["6" 32]]] | |
(into {} | |
(for [[pre k] [[:h :height] | |
[:w :width] | |
[:min-h :minHeight] | |
[:min-w :minWidth]] | |
[s fac] scale] | |
[(kwd pre s) {k (int (* fac rem))}])))) | |
(def absolute | |
"Absolute positioning and offsets | |
absolute position: absolute | |
top|right|bottom|left-0 top|right|bottom|left: 0 rem | |
... 1 ... 1 rem | |
... 2 ... 2 rem | |
absolute-fill position: absolute, top/left/right/bottom: 0 " | |
(let [scale [["0" 0] | |
["1" 1] | |
["2" 2]]] | |
(into {:absolute {:position "absolute"} | |
:absolute-fill {:position "absolute" | |
:top 0 | |
:left 0 | |
:right 0 | |
:bottom 0}} | |
(for [[pre k] [[:top- :top] | |
[:right- :right] | |
[:left- :left] | |
[:bottom- :bottom]] | |
[s fac] scale] | |
[(kwd pre s) {k (int (* fac rem))}])))) | |
(def border-width | |
"Border width properties | |
ba borderWidth: 1 | |
bl|br|bt|bb borderLeftWidth: 1 | borderRightWidth: 1 ..." | |
(into {} | |
(for [[k m] [[:ba {:borderWidth 1}] | |
[:bl {:borderLeftWidth 1}] | |
[:br {:borderRightWidth 1}] | |
[:bt {:borderTopWidth 1}] | |
[:bb {:borderBottomWidth 1}]]] | |
[k m]))) | |
(def border-radius | |
"Border radius properties | |
br0 ... br5 borderRadius: 0|0.125|0.25|0.5|1]2 rem" | |
(let [scale [["0" 0] | |
["1" 0.125] | |
["2" 0.25] | |
["3" 0.5] | |
["4" 1] | |
["5" 2]]] | |
(into {} | |
(for [[s fac] scale] | |
[(kwd :br s) {:borderRadius (int (* fac rem))}])))) | |
(def font-size | |
"Font size properties | |
f5 fontSize: 1 rem | |
f1 ... f6 fontSize: 3|2.25|1.5|1.25|1|0.875 rem | |
f-headline fontSize: 6 rem | |
f-subheadline fontSize: 5 rem" | |
(let [scale [["1" 3] | |
["2" 2.25] | |
["3" 1.5] | |
["4" 1.25] | |
["5" 1] | |
["6" 0.875]]] | |
(into {:f-headline {:fontSize (int (* 6 font-rem))} | |
:f-subheadline {:fontSize (int (* 6 font-rem))}} | |
(for [[s fac] scale] | |
[(kwd :f s) {:fontSize (int (* fac font-rem))}])))) | |
(def text-align | |
"Text align properties | |
tl|tc|tr|tj textAlign: left|center|right|justify" | |
{:tl {:textAlign "left"} | |
:tc {:textAlign "center"} | |
:tr {:textAlign "right"} | |
:tj {:textAlign "justify"}}) | |
(def font-family | |
"Font family & weight properties" | |
{:ffsb {:fontFamily "WorkSans-SemiBold"} | |
:ffm {:fontFamily "WorkSans-Medium"} | |
:ffr {:fontFamily "WorkSans-Regular"} | |
:ffl {:fontFamily "WorkSans-Light"} | |
:ff-title {:fontFamily "RiverfordTitle-Regular"}}) | |
(def opacity | |
"Opacity properties | |
o10|20|...|100 opacity: 0.1|0.2|...|1 | |
o05 opacity: 0.05 | |
o025 opacity: 0.025" | |
(let [scale [["025" 0.025] | |
["05" 0.05] | |
["10" 0.1] | |
["20" 0.2] | |
["30" 0.3] | |
["40" 0.4] | |
["50" 0.5] | |
["60" 0.6] | |
["70" 0.7] | |
["80" 0.8] | |
["90" 0.9] | |
["100" 1]]] | |
(into {} | |
(for [[s o] scale] | |
[(kwd :o- s) {:opacity o}])))) | |
(def colors | |
"Font family & weight properties" | |
(let [palette [[:plum0 "#962d5d"] | |
[:plum1 "#ad2871"] | |
[:moss0 "#488b45"] | |
[:teal0 "#149998"] | |
[:ui0 "#2a2a2a"] | |
[:ui1 "#4a4a4a"] | |
[:ui2 "#6969696"] | |
[:ui3 "#e6e6e6"] | |
[:ui4 "#fbfbfb"] | |
[:ui5 "#ffffff"] | |
[:status0 "#5b9851"] | |
[:status1 "#f5a623"] | |
[:status2 "#b5071c"]]] | |
(into {} | |
(for [[c hex] palette | |
[pre prop] [[nil :color] | |
[:b-- :borderColor] | |
[:bg- :backgroundColor]]] | |
[(kwd pre c) {prop hex}])))) | |
(def line-heights | |
{:lh-solid {:lineHeight 1} | |
:lh-title {:lineHeight 1.25} | |
:lh-copy {:lineHeight 1.5}}) | |
(def tachyons | |
(merge flex | |
spacing | |
heights | |
absolute | |
border-width | |
border-radius | |
font-size | |
text-align | |
font-family | |
opacity | |
colors | |
line-heights)) | |
(defn scale-line-height | |
"Postprocess step to scale lineheight according to | |
provided font size." | |
[m] | |
(let [line-heights {:lh-solid 1 | |
:lh-title 1.25 | |
:lh-copy 1.5} | |
{:keys [fontSize lineHeight]} m] | |
(cond | |
(and lineHeight fontSize) (assoc m :lineHeight (int (* fontSize lineHeight))) | |
(and lineHeight (not fontSize)) (throw (ex-info "Font size must be provided if lineheight is required" m)) | |
:else m))) | |
(defn style | |
"Constructs a style map from a collection of keywords" | |
[coll] | |
(let [f (fn [x] (or (when (map? x) x) | |
(when-let [m (get tachyons x)] m) | |
(throw (ex-info (str "Style " x " not found") {:style coll}))))] | |
(-> (reduce merge (map f coll)) | |
(scale-line-height)))) | |
(is (= {:backgroundColor "#fbfbfb", | |
:borderColor "#2a2a2a", | |
:borderRadius 9, | |
:textAlign "center"} | |
(style [:bg-ui4 :b--ui0 :br3 :tc]))) | |
(is (= {:position "absolute", | |
:top 0, | |
:left 0, | |
:right 0, | |
:bottom 0, | |
:justifyContent "center", | |
:alignItems "center"} | |
(style [:absolute-fill :jcc :aic]))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment