Tailwind classes in OCaml
1(* Main module for Tailwind HTML library *) 2 3(* Common utility for converting Tailwind classes to HTML class attribute *) 4let classes_attr tailwind_classes = 5 Htmlit.At.class' (Tailwind.to_string tailwind_classes) 6 7(* Apply Tailwind classes to an existing HTML element by wrapping it *) 8let with_classes classes element = 9 let open Htmlit in 10 El.span ~at:[classes_attr classes] [element] 11 12(* Conditionally apply Tailwind classes *) 13let with_classes_if condition classes element = 14 if condition then with_classes classes element else element 15 16(* Create an element with Tailwind classes and optional attributes *) 17let el tag ?classes ?attributes children = 18 let open Htmlit in 19 let base_attrs = match classes with 20 | Some c -> [classes_attr c] 21 | None -> [] 22 in 23 let custom_attrs = match attributes with 24 | Some attrs -> List.map (fun (k, v) -> At.v k v) attrs 25 | None -> [] 26 in 27 let all_attrs = base_attrs @ custom_attrs in 28 El.v tag ~at:all_attrs children 29 30(* Common HTML elements with Tailwind class support *) 31let div ?classes ?attributes children = el "div" ?classes ?attributes children 32let span ?classes ?attributes children = el "span" ?classes ?attributes children 33let p ?classes ?attributes children = el "p" ?classes ?attributes children 34let a ?classes ?attributes ~href children = 35 let attrs = match attributes with Some a -> a | None -> [] in 36 el "a" ?classes ~attributes:(("href", href) :: attrs) children 37let img ?classes ?attributes ~src ~alt () = 38 let attrs = match attributes with Some a -> a | None -> [] in 39 el "img" ?classes ~attributes:(("src", src) :: ("alt", alt) :: attrs) [] 40let ul ?classes ?attributes children = el "ul" ?classes ?attributes children 41let ol ?classes ?attributes children = el "ol" ?classes ?attributes children 42let li ?classes ?attributes children = el "li" ?classes ?attributes children 43 44(* Utility functions for colors and sizes *) 45let blue variant = Tailwind.Color.make `Blue ~variant:(match variant with 46 | 50 -> `V50 | 100 -> `V100 | 200 -> `V200 | 300 -> `V300 | 400 -> `V400 47 | 500 -> `V500 | 600 -> `V600 | 700 -> `V700 | 800 -> `V800 | 900 -> `V900 48 | _ -> `V600) () 49 50let gray variant = Tailwind.Color.make `Gray ~variant:(match variant with 51 | 50 -> `V50 | 100 -> `V100 | 200 -> `V200 | 300 -> `V300 | 400 -> `V400 52 | 500 -> `V500 | 600 -> `V600 | 700 -> `V700 | 800 -> `V800 | 900 -> `V900 53 | _ -> `V600) () 54 55let red variant = Tailwind.Color.make `Red ~variant:(match variant with 56 | 50 -> `V50 | 100 -> `V100 | 200 -> `V200 | 300 -> `V300 | 400 -> `V400 57 | 500 -> `V500 | 600 -> `V600 | 700 -> `V700 | 800 -> `V800 | 900 -> `V900 58 | _ -> `V600) () 59 60let green variant = Tailwind.Color.make `Green ~variant:(match variant with 61 | 50 -> `V50 | 100 -> `V100 | 200 -> `V200 | 300 -> `V300 | 400 -> `V400 62 | 500 -> `V500 | 600 -> `V600 | 700 -> `V700 | 800 -> `V800 | 900 -> `V900 63 | _ -> `V600) () 64 65let rem f = Tailwind.Size.rem f 66let px = Tailwind.Size.px 67let zero = Tailwind.Size.zero 68let auto = Tailwind.Size.auto 69let full = Tailwind.Size.full 70let screen = Tailwind.Size.screen 71let txt s = Htmlit.El.txt s 72 73(* Common utility classes *) 74let flex = Tailwind.Display.flex 75let flex_col = Tailwind.Flexbox.(to_class (direction `Col)) 76let items_center = Tailwind.Flexbox.(to_class (align_items `Center)) 77let justify_center = Tailwind.Flexbox.(to_class (justify `Center)) 78let justify_between = Tailwind.Flexbox.(to_class (justify `Between)) 79let font_bold = Tailwind.Typography.(to_class (font_weight `Bold)) 80let font_semibold = Tailwind.Typography.(to_class (font_weight `Semibold)) 81let text_center = Tailwind.Typography.(to_class (text_align `Center)) 82let w_full = Tailwind.Layout.w_full 83let h_full = Tailwind.Layout.h_full 84let rounded_lg = Tailwind.Effects.rounded_lg 85let rounded_md = Tailwind.Effects.rounded_md 86let shadow_md = Tailwind.Effects.shadow_md 87let shadow_lg = Tailwind.Effects.shadow_lg 88 89(* Enhanced element functions with styling parameters *) 90let h1 ?size ?weight ?color ?align ?mb ?classes children = 91 let base_styles = [Tailwind.Typography.(to_class (font_size `Xl2)); font_bold] in 92 let size_styles = match size with 93 | Some `Xl -> [Tailwind.Typography.(to_class (font_size `Xl))] 94 | Some `Xl2 -> [Tailwind.Typography.(to_class (font_size `Xl2))] 95 | Some `Xl3 -> [Tailwind.Typography.(to_class (font_size `Xl3))] 96 | Some `Xl4 -> [Tailwind.Typography.(to_class (font_size `Xl4))] 97 | None -> [] 98 in 99 let weight_styles = match weight with 100 | Some `Bold -> [font_bold] 101 | Some `Semibold -> [font_semibold] 102 | Some `Medium -> [Tailwind.Typography.(to_class (font_weight `Medium))] 103 | None -> [] 104 in 105 let color_styles = match color with Some c -> [Tailwind.Color.text c] | None -> [] in 106 let align_styles = match align with 107 | Some `Center -> [text_center] 108 | Some `Left -> [Tailwind.Typography.(to_class (text_align `Left))] 109 | Some `Right -> [Tailwind.Typography.(to_class (text_align `Right))] 110 | None -> [] 111 in 112 let spacing_styles = match mb with Some s -> [Tailwind.Spacing.(to_class (mb s))] | None -> [] in 113 let final_classes = Tailwind.Css.tw (base_styles @ size_styles @ weight_styles @ color_styles @ align_styles @ spacing_styles @ 114 (match classes with Some c -> [c] | None -> [])) in 115 Htmlit.El.h1 ~at:[classes_attr final_classes] children 116 117let h2 ?size ?weight ?color ?align ?mb ?classes children = 118 let base_styles = [Tailwind.Typography.(to_class (font_size `Xl)); font_semibold] in 119 let size_styles = match size with 120 | Some `Lg -> [Tailwind.Typography.(to_class (font_size `Lg))] 121 | Some `Xl -> [Tailwind.Typography.(to_class (font_size `Xl))] 122 | Some `Xl2 -> [Tailwind.Typography.(to_class (font_size `Xl2))] 123 | None -> [] 124 in 125 let weight_styles = match weight with 126 | Some `Bold -> [font_bold] 127 | Some `Semibold -> [font_semibold] 128 | Some `Medium -> [Tailwind.Typography.(to_class (font_weight `Medium))] 129 | None -> [] 130 in 131 let color_styles = match color with Some c -> [Tailwind.Color.text c] | None -> [] in 132 let align_styles = match align with 133 | Some `Center -> [text_center] 134 | Some `Left -> [Tailwind.Typography.(to_class (text_align `Left))] 135 | Some `Right -> [Tailwind.Typography.(to_class (text_align `Right))] 136 | None -> [] 137 in 138 let spacing_styles = match mb with Some s -> [Tailwind.Spacing.(to_class (mb s))] | None -> [] in 139 let final_classes = Tailwind.Css.tw (base_styles @ size_styles @ weight_styles @ color_styles @ align_styles @ spacing_styles @ 140 (match classes with Some c -> [c] | None -> [])) in 141 Htmlit.El.h2 ~at:[classes_attr final_classes] children 142 143let p_styled ?size ?color ?align ?mb ?classes children = 144 let base_styles = [Tailwind.Typography.(to_class (font_size `Base))] in 145 let size_styles = match size with 146 | Some `Sm -> [Tailwind.Typography.(to_class (font_size `Sm))] 147 | Some `Base -> [Tailwind.Typography.(to_class (font_size `Base))] 148 | Some `Lg -> [Tailwind.Typography.(to_class (font_size `Lg))] 149 | None -> [] 150 in 151 let color_styles = match color with Some c -> [Tailwind.Color.text c] | None -> [] in 152 let align_styles = match align with 153 | Some `Center -> [text_center] 154 | Some `Left -> [Tailwind.Typography.(to_class (text_align `Left))] 155 | Some `Right -> [Tailwind.Typography.(to_class (text_align `Right))] 156 | None -> [] 157 in 158 let spacing_styles = match mb with Some s -> [Tailwind.Spacing.(to_class (mb s))] | None -> [] in 159 let final_classes = Tailwind.Css.tw (base_styles @ size_styles @ color_styles @ align_styles @ spacing_styles @ 160 (match classes with Some c -> [c] | None -> [])) in 161 Htmlit.El.p ~at:[classes_attr final_classes] children 162 163(* Simple component functions *) 164let container children = 165 let container_classes = Tailwind.Css.tw [Tailwind.Patterns.container ()] in 166 div ~classes:container_classes children 167 168let flex_center children = 169 let flex_classes = Tailwind.Css.tw [flex; items_center; justify_center] in 170 div ~classes:flex_classes children 171 172let card ?elevated ?padding children = 173 let base_classes = [Tailwind.Color.bg Tailwind.Color.white; rounded_lg] in 174 let shadow_classes = if elevated = Some true then [shadow_lg] else [Tailwind.Effects.shadow_sm] in 175 let padding_classes = if padding <> Some false then [Tailwind.Spacing.(to_class (p (rem 1.5)))] else [] in 176 let card_classes = Tailwind.Css.tw (base_classes @ shadow_classes @ padding_classes) in 177 div ~classes:card_classes children 178 179let btn_primary ?size ?disabled children = 180 let base_classes = [ 181 flex; items_center; justify_center; rounded_md; 182 Tailwind.Typography.(to_class (font_size `Sm)); 183 Tailwind.Typography.(to_class (font_weight `Medium)); 184 Tailwind.Color.bg (blue 600); 185 Tailwind.Color.text Tailwind.Color.white; 186 Tailwind.Variants.hover (Tailwind.Color.bg (blue 700)); 187 Tailwind.Effects.transition `Colors; 188 ] in 189 let size_classes = match size with 190 | Some `Sm -> [Tailwind.Spacing.(to_class (px (rem 0.75))); Tailwind.Spacing.(to_class (py (rem 0.375)))] 191 | Some `Lg -> [Tailwind.Spacing.(to_class (px (rem 2.0))); Tailwind.Spacing.(to_class (py (rem 0.75)))] 192 | _ -> [Tailwind.Spacing.(to_class (px (rem 1.0))); Tailwind.Spacing.(to_class (py (rem 0.5)))] 193 in 194 let disabled_classes = if disabled = Some true then [ 195 Tailwind.Css.make "disabled:opacity-50"; 196 Tailwind.Css.make "disabled:cursor-not-allowed" 197 ] else [] in 198 let btn_classes = Tailwind.Css.tw (base_classes @ size_classes @ disabled_classes) in 199 let attrs = [classes_attr btn_classes] @ (if disabled = Some true then [Htmlit.At.disabled] else []) in 200 Htmlit.El.button ~at:attrs children 201 202let btn_secondary ?size ?disabled children = 203 let base_classes = [ 204 flex; items_center; justify_center; rounded_md; 205 Tailwind.Typography.(to_class (font_size `Sm)); 206 Tailwind.Typography.(to_class (font_weight `Medium)); 207 Tailwind.Color.bg (gray 200); 208 Tailwind.Color.text (gray 900); 209 Tailwind.Variants.hover (Tailwind.Color.bg (gray 300)); 210 Tailwind.Effects.transition `Colors; 211 ] in 212 let size_classes = match size with 213 | Some `Sm -> [Tailwind.Spacing.(to_class (px (rem 0.75))); Tailwind.Spacing.(to_class (py (rem 0.375)))] 214 | Some `Lg -> [Tailwind.Spacing.(to_class (px (rem 2.0))); Tailwind.Spacing.(to_class (py (rem 0.75)))] 215 | _ -> [Tailwind.Spacing.(to_class (px (rem 1.0))); Tailwind.Spacing.(to_class (py (rem 0.5)))] 216 in 217 let disabled_classes = if disabled = Some true then [ 218 Tailwind.Css.make "disabled:opacity-50"; 219 Tailwind.Css.make "disabled:cursor-not-allowed" 220 ] else [] in 221 let btn_classes = Tailwind.Css.tw (base_classes @ size_classes @ disabled_classes) in 222 let attrs = [classes_attr btn_classes] @ (if disabled = Some true then [Htmlit.At.disabled] else []) in 223 Htmlit.El.button ~at:attrs children 224 225(* Text element with built-in typography utilities *) 226let text ?size ?weight ?color ?align ?classes text_content = 227 let base_styles = [] in 228 let size_styles = match size with Some s -> [Tailwind.Typography.(to_class (font_size s))] | None -> [] in 229 let weight_styles = match weight with Some w -> [Tailwind.Typography.(to_class (font_weight w))] | None -> [] in 230 let color_styles = match color with Some c -> [Tailwind.Color.text c] | None -> [] in 231 let align_styles = match align with Some a -> [Tailwind.Typography.(to_class (text_align a))] | None -> [] in 232 let text_classes = Tailwind.Css.tw (base_styles @ size_styles @ weight_styles @ color_styles @ align_styles) in 233 let final_classes = match classes with 234 | Some c -> Tailwind.Css.tw [text_classes; c] 235 | None -> text_classes 236 in 237 span ~classes:final_classes [Htmlit.El.txt text_content]