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]