(define tw-spacing-props {:ml "margin-left:{v}" :mr "margin-right:{v}" :mt "margin-top:{v}" :mb "margin-bottom:{v}" :pl "padding-left:{v}" :gap-y "row-gap:{v}" :m "margin:{v}" :gap-x "column-gap:{v}" :my "margin-top:{v};margin-bottom:{v}" :px "padding-left:{v};padding-right:{v}" :pb "padding-bottom:{v}" :pr "padding-right:{v}" :p "padding:{v}" :gap "gap:{v}" :py "padding-top:{v};padding-bottom:{v}" :pt "padding-top:{v}" :mx "margin-left:{v};margin-right:{v}"}) (define tw-displays {:flex "flex" :table "table" :grid "grid" :inline-block "inline-block" :table-row "table-row" :inline "inline" :hidden "none" :block "block" :contents "contents" :inline-flex "inline-flex" :inline-grid "inline-grid" :table-cell "table-cell"}) (define tw-max-widths {:xs "20rem" :3xl "48rem" :7xl "80rem" :sm "24rem" :xl "36rem" :full "100%" :md "28rem" :6xl "72rem" :prose "65ch" :max "max-content" :5xl "64rem" :min "min-content" :lg "32rem" :2xl "42rem" :4xl "56rem" :none "none" :screen "100vw" :fit "fit-content"}) (define tw-min-widths {:full "100%" :0 "0px" :max "max-content" :min "min-content" :fit "fit-content"}) (define tw-resolve-layout (fn (token) (let ((parts (split token "-")) (head (first parts)) (rest (slice parts 1))) (cond (and (= (len parts) 1) (not (nil? (get tw-displays head)))) (str "display:" (get tw-displays head)) (and (= (len parts) 2) (not (nil? (get tw-displays token)))) (str "display:" (get tw-displays token)) (and (get tw-spacing-props head) (= (len rest) 1)) (let ((tmpl (get tw-spacing-props head)) (v (tw-spacing-value (first rest)))) (if (nil? v) nil (tw-template tmpl v))) (and (= (len rest) 2) (get tw-spacing-props (str head "-" (first rest)))) (let ((tmpl (get tw-spacing-props (str head "-" (first rest)))) (v (tw-spacing-value (last rest)))) (if (nil? v) nil (tw-template tmpl v))) (and (= head "space") (= (len rest) 2) (or (= (first rest) "x") (= (first rest) "y"))) (let ((v (tw-spacing-value (last rest))) (dir (first rest))) (if (nil? v) nil (if (= dir "x") {:suffix ">*+*" :css (str "margin-left:" v)} {:suffix ">*+*" :css (str "margin-top:" v)}))) (and (= head "flex") (empty? rest)) "display:flex" (and (= head "flex") (= (len rest) 1)) (case (first rest) "row" "flex-direction:row" "col" "flex-direction:column" "wrap" "flex-wrap:wrap" "nowrap" "flex-wrap:nowrap" "1" "flex:1 1 0%" "auto" "flex:1 1 auto" "initial" "flex:0 1 auto" "none" "flex:none" :else nil) (and (= head "flex") (= (len rest) 2)) (case (join "-" rest) "row-reverse" "flex-direction:row-reverse" "col-reverse" "flex-direction:column-reverse" "wrap-reverse" "flex-wrap:wrap-reverse" :else nil) (= head "grow") (if (empty? rest) "flex-grow:1" (if (= (first rest) "0") "flex-grow:0" nil)) (= head "shrink") (if (empty? rest) "flex-shrink:1" (if (= (first rest) "0") "flex-shrink:0" nil)) (and (= head "basis") (= (len rest) 1)) (let ((val (first rest))) (cond (= val "auto") "flex-basis:auto" (= val "full") "flex-basis:100%" (= val "0") "flex-basis:0px" (contains? val "/") (let ((frac (split val "/"))) (if (= (len frac) 2) (let ((num (parse-int (first frac) nil)) (den (parse-int (nth frac 1) nil))) (if (or (nil? num) (nil? den)) nil (str "flex-basis:" (* (/ num den) 100) "%"))) nil)) :else (let ((n (parse-int val nil))) (if (nil? n) nil (str "flex-basis:" (* n 0.25) "rem"))))) (and (= head "justify") (= (len rest) 1)) (case (first rest) "start" "justify-content:flex-start" "end" "justify-content:flex-end" "center" "justify-content:center" "between" "justify-content:space-between" "around" "justify-content:space-around" "evenly" "justify-content:space-evenly" "stretch" "justify-content:stretch" :else nil) (and (= head "items") (= (len rest) 1)) (case (first rest) "start" "align-items:flex-start" "end" "align-items:flex-end" "center" "align-items:center" "baseline" "align-items:baseline" "stretch" "align-items:stretch" :else nil) (and (= head "self") (= (len rest) 1)) (case (first rest) "auto" "align-self:auto" "start" "align-self:flex-start" "end" "align-self:flex-end" "center" "align-self:center" "stretch" "align-self:stretch" "baseline" "align-self:baseline" :else nil) (and (= head "content") (= (len rest) 1)) (case (first rest) "start" "align-content:flex-start" "end" "align-content:flex-end" "center" "align-content:center" "between" "align-content:space-between" "around" "align-content:space-around" "evenly" "align-content:space-evenly" "stretch" "align-content:stretch" :else nil) (and (= head "order") (= (len rest) 1)) (let ((val (first rest))) (cond (= val "first") "order:-9999" (= val "last") "order:9999" (= val "none") "order:0" :else (let ((n (parse-int val nil))) (if (nil? n) nil (str "order:" n))))) (and (= head "grid") (empty? rest)) "display:grid" (and (= head "grid") (>= (len rest) 2) (= (first rest) "cols")) (let ((val (join "-" (slice rest 1)))) (cond (= val "none") "grid-template-columns:none" (= val "subgrid") "grid-template-columns:subgrid" :else (let ((n (parse-int val nil))) (if (nil? n) nil (str "grid-template-columns:repeat(" n ",minmax(0,1fr))"))))) (and (= head "grid") (>= (len rest) 2) (= (first rest) "rows")) (let ((val (join "-" (slice rest 1)))) (cond (= val "none") "grid-template-rows:none" (= val "subgrid") "grid-template-rows:subgrid" :else (let ((n (parse-int val nil))) (if (nil? n) nil (str "grid-template-rows:repeat(" n ",minmax(0,1fr))"))))) (and (= head "grid") (>= (len rest) 2) (= (first rest) "flow")) (case (nth rest 1) "row" "grid-auto-flow:row" "col" "grid-auto-flow:column" "dense" "grid-auto-flow:dense" :else nil) (and (= head "col") (>= (len rest) 2)) (let ((sub (first rest)) (val (join "-" (slice rest 1)))) (cond (and (= sub "span") (= val "full")) "grid-column:1 / -1" (= sub "span") (let ((n (parse-int val nil))) (if (nil? n) nil (str "grid-column:span " n " / span " n))) (= sub "start") (str "grid-column-start:" val) (= sub "end") (str "grid-column-end:" val) :else nil)) (and (= head "row") (>= (len rest) 2)) (let ((sub (first rest)) (val (join "-" (slice rest 1)))) (cond (and (= sub "span") (= val "full")) "grid-row:1 / -1" (= sub "span") (let ((n (parse-int val nil))) (if (nil? n) nil (str "grid-row:span " n " / span " n))) (= sub "start") (str "grid-row-start:" val) (= sub "end") (str "grid-row-end:" val) :else nil)) (and (= head "auto") (>= (len rest) 2)) (let ((sub (first rest)) (val (join "-" (slice rest 1)))) (cond (and (= sub "cols") (= val "auto")) "grid-auto-columns:auto" (and (= sub "cols") (= val "min")) "grid-auto-columns:min-content" (and (= sub "cols") (= val "max")) "grid-auto-columns:max-content" (and (= sub "cols") (= val "fr")) "grid-auto-columns:minmax(0,1fr)" (and (= sub "rows") (= val "auto")) "grid-auto-rows:auto" (and (= sub "rows") (= val "min")) "grid-auto-rows:min-content" (and (= sub "rows") (= val "max")) "grid-auto-rows:max-content" (and (= sub "rows") (= val "fr")) "grid-auto-rows:minmax(0,1fr)" :else nil)) (and (= (len parts) 1) (or (= head "relative") (= head "absolute") (= head "fixed") (= head "sticky") (= head "static"))) (str "position:" head) (and (or (= head "top") (= head "right") (= head "bottom") (= head "left")) (= (len rest) 1)) (let ((v (tw-spacing-value (first rest)))) (if (nil? v) nil (str head ":" v))) (and (= head "inset") (= (len rest) 1)) (let ((v (tw-spacing-value (first rest)))) (if (nil? v) nil (str "inset:" v))) (and (= head "inset") (= (len rest) 2)) (let ((dir (first rest)) (v (tw-spacing-value (nth rest 1)))) (if (nil? v) nil (case dir "x" (str "left:" v ";right:" v) "y" (str "top:" v ";bottom:" v) :else nil))) (and (= head "z") (= (len rest) 1)) (if (= (first rest) "auto") "z-index:auto" (let ((n (parse-int (first rest) nil))) (if (nil? n) nil (str "z-index:" n)))) (and (or (= head "w") (= head "h")) (= (len rest) 1)) (let ((prop (if (= head "w") "width" "height")) (val (first rest))) (cond (= val "full") (str prop ":100%") (= val "screen") (str prop (if (= head "w") ":100vw" ":100vh")) (= val "auto") (str prop ":auto") (= val "min") (str prop ":min-content") (= val "max") (str prop ":max-content") (= val "fit") (str prop ":fit-content") (contains? val "/") (let ((frac (split val "/"))) (if (= (len frac) 2) (let ((num (parse-int (first frac) nil)) (den (parse-int (nth frac 1) nil))) (if (or (nil? num) (nil? den)) nil (str prop ":" (* (/ num den) 100) "%"))) nil)) :else (let ((n (parse-int val nil))) (if (nil? n) nil (str prop ":" (* n 0.25) "rem"))))) (and (= head "max") (>= (len rest) 2) (= (first rest) "w")) (let ((val-name (join "-" (slice rest 1))) (val (get tw-max-widths val-name))) (if (nil? val) nil (str "max-width:" val))) (and (= head "max") (>= (len rest) 2) (= (first rest) "h")) (let ((val (first (slice rest 1)))) (cond (= val "full") "max-height:100%" (= val "screen") "max-height:100vh" (= val "none") "max-height:none" :else (let ((n (parse-int val nil))) (if (nil? n) nil (str "max-height:" (* n 0.25) "rem"))))) (and (= head "min") (>= (len rest) 2) (= (first rest) "w")) (let ((val-name (join "-" (slice rest 1))) (val (get tw-min-widths val-name))) (if (nil? val) nil (str "min-width:" val))) (and (= head "min") (>= (len rest) 2) (= (first rest) "h")) (let ((val (first (slice rest 1)))) (cond (= val "0") "min-height:0px" (= val "full") "min-height:100%" (= val "screen") "min-height:100vh" :else nil)) (and (= head "overflow") (= (len rest) 1)) (str "overflow:" (first rest)) (and (= head "overflow") (= (len rest) 2)) (str "overflow-" (first rest) ":" (nth rest 1)) (and (= head "aspect") (= (len rest) 1)) (case (first rest) "auto" "aspect-ratio:auto" "square" "aspect-ratio:1 / 1" "video" "aspect-ratio:16 / 9" :else nil) (and (= head "object") (= (len rest) 1)) (str "object-fit:" (first rest)) (and (= (len parts) 1) (= head "visible")) "visibility:visible" (and (= (len parts) 1) (= head "invisible")) "visibility:hidden" (and (= (len parts) 1) (= head "collapse")) "visibility:collapse" (and (= (len parts) 1) (= head "container")) "width:100%;max-width:100%" (and (= (len parts) 1) (= head "isolate")) "isolation:isolate" :else nil))))