Tailwind classes in OCaml

Redesign Tailwind_html with succinct Htmlit-style combinators

Transform verbose Tailwind class construction into concise, type-safe combinators:
- Add utility functions: blue 600, gray 50, rem 1.0, txt "text"
- Add utility classes: flex, items_center, font_bold, rounded_lg
- Add enhanced elements: h1/h2/p_styled with styling parameters
- Add simple components: container, card, btn_primary, btn_secondary
- Remove module re-exports from Tailwind (use Tailwind directly)
- Update hello_tailwind example to demonstrate new succinct API
- Add button_demo example showcasing before/after comparison

Reduces ~10 lines of verbose class construction to 1 line:
h1 ~size:\`Xl2 ~weight:\`Bold ~color:(blue 600) [txt "Hello\!"]

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

+448 -136
+127
examples/button_demo.ml
··· 1 + (* Button Demo - Showcasing new button combinators *) 2 + 3 + open Htmlit 4 + open Tailwind_html 5 + 6 + let create_button_demo () = 7 + let html = El.html [ 8 + El.head [ 9 + El.meta ~at:[At.charset "utf-8"] (); 10 + El.meta ~at:[At.name "viewport"; At.content "width=device-width, initial-scale=1"] (); 11 + El.title [txt "Button Demo"]; 12 + El.link ~at:[At.rel "stylesheet"; At.href "button_demo.css"] (); 13 + ]; 14 + El.body ~at:[classes_attr (Tailwind.Css.tw [ 15 + Tailwind.Layout.(to_class (min_height screen)); 16 + Tailwind.Color.bg (gray 50); 17 + Tailwind.Spacing.(to_class (p (rem 2.0))); 18 + ])] [ 19 + container [ 20 + h1 ~size:`Xl3 ~weight:`Bold ~color:(gray 800) ~align:`Center ~mb:(rem 2.0) [ 21 + txt "Button Combinator Demo" 22 + ]; 23 + 24 + p_styled ~size:`Lg ~color:(gray 600) ~align:`Center ~mb:(rem 3.0) [ 25 + txt "Showcasing succinct button creation with the new Tailwind_html API" 26 + ]; 27 + 28 + (* Button examples section *) 29 + div ~classes:(Tailwind.Css.tw [ 30 + Tailwind.Spacing.(to_class (gap `All (rem 2.0))); 31 + flex; 32 + Tailwind.Flexbox.(to_class (direction `Col)); 33 + ]) [ 34 + (* Primary buttons *) 35 + card [ 36 + h2 ~size:`Xl ~weight:`Semibold ~color:(gray 700) ~mb:(rem 1.5) [ 37 + txt "Primary Buttons" 38 + ]; 39 + div ~classes:(Tailwind.Css.tw [ 40 + flex; 41 + Tailwind.Flexbox.(to_class (wrap `Wrap)); 42 + Tailwind.Spacing.(to_class (gap `All (rem 1.0))); 43 + ]) [ 44 + btn_primary ~size:`Sm [txt "Small Primary"]; 45 + btn_primary [txt "Default Primary"]; 46 + btn_primary ~size:`Lg [txt "Large Primary"]; 47 + btn_primary ~disabled:true [txt "Disabled Primary"]; 48 + ]; 49 + ]; 50 + 51 + (* Secondary buttons *) 52 + card [ 53 + h2 ~size:`Xl ~weight:`Semibold ~color:(gray 700) ~mb:(rem 1.5) [ 54 + txt "Secondary Buttons" 55 + ]; 56 + div ~classes:(Tailwind.Css.tw [ 57 + flex; 58 + Tailwind.Flexbox.(to_class (wrap `Wrap)); 59 + Tailwind.Spacing.(to_class (gap `All (rem 1.0))); 60 + ]) [ 61 + btn_secondary ~size:`Sm [txt "Small Secondary"]; 62 + btn_secondary [txt "Default Secondary"]; 63 + btn_secondary ~size:`Lg [txt "Large Secondary"]; 64 + btn_secondary ~disabled:true [txt "Disabled Secondary"]; 65 + ]; 66 + ]; 67 + 68 + (* Comparison section *) 69 + card [ 70 + h2 ~size:`Xl ~weight:`Semibold ~color:(gray 700) ~mb:(rem 1.5) [ 71 + txt "Before vs After" 72 + ]; 73 + div ~classes:(Tailwind.Css.tw [ 74 + Tailwind.Display.grid; 75 + Tailwind.Grid.(to_class (template_cols (`Cols 1))); 76 + Tailwind.Responsive.(to_class (at_breakpoint `Md (Tailwind.Grid.(to_class (template_cols (`Cols 2)))))); 77 + Tailwind.Spacing.(to_class (gap `All (rem 2.0))); 78 + ]) [ 79 + div [ 80 + h2 ~size:`Lg ~weight:`Medium ~color:(red 600) ~mb:(rem 1.0) [ 81 + txt "❌ Old Verbose Way" 82 + ]; 83 + El.pre ~at:[classes_attr (Tailwind.Css.tw [ 84 + Tailwind.Color.bg (gray 100); 85 + Tailwind.Spacing.(to_class (p (rem 1.0))); 86 + Tailwind.Effects.rounded_md; 87 + Tailwind.Typography.(to_class (font_size `Sm)); 88 + Tailwind.Layout.(to_class (overflow `X `Auto)); 89 + ])] [ 90 + El.code [txt {|let btn_classes = Css.tw [ 91 + Color.bg (Color.make `Blue ~variant:`V600 ()); 92 + Color.text Color.white; 93 + Spacing.(to_class (px (Size.rem 1.0))); 94 + Spacing.(to_class (py (Size.rem 0.5))); 95 + Effects.rounded_md; 96 + Typography.(to_class (font_weight `Medium)); 97 + ] in 98 + El.button ~at:[classes_attr btn_classes] [ 99 + El.txt "Click me!" 100 + ]|}]; 101 + ]; 102 + ]; 103 + div [ 104 + h2 ~size:`Lg ~weight:`Medium ~color:(green 600) ~mb:(rem 1.0) [ 105 + txt "✅ New Succinct Way" 106 + ]; 107 + El.pre ~at:[classes_attr (Tailwind.Css.tw [ 108 + Tailwind.Color.bg (gray 100); 109 + Tailwind.Spacing.(to_class (p (rem 1.0))); 110 + Tailwind.Effects.rounded_md; 111 + Tailwind.Typography.(to_class (font_size `Sm)); 112 + Tailwind.Layout.(to_class (overflow `X `Auto)); 113 + ])] [ 114 + El.code [txt {|btn_primary [txt "Click me!"]|}]; 115 + ]; 116 + ]; 117 + ]; 118 + ]; 119 + ]; 120 + ]; 121 + ]; 122 + ] in 123 + html 124 + 125 + let () = 126 + let html = create_button_demo () in 127 + print_string (El.to_string ~doctype:true html)
+64 -108
examples/hello_tailwind_01.ml
··· 1 1 (* Example 01: Hello Tailwind - Your First Tailwind OCaml Program *) 2 2 3 3 open Htmlit 4 - open Tailwind 5 - 6 - let classes_attr tailwind_classes = 7 - At.class' @@ Tailwind.to_string tailwind_classes 4 + open Tailwind_html 8 5 9 6 let create_page () = 10 - let hello_classes = Css.tw [ 11 - Color.text (Color.make `Blue ~variant:`V600 ()); 12 - Typography.(to_class (font_size `Xl2)); 13 - Typography.(to_class (font_weight `Bold)); 14 - Spacing.(to_class (mb (Size.rem 1.0))); 15 - ] in 16 - 17 - let body_classes = Css.tw [ 18 - Layout.(to_class (min_height Size.screen)); 19 - Color.bg (Color.make `Gray ~variant:`V50 ()); 20 - Display.flex; 21 - Flexbox.(to_class (align_items `Center)); 22 - Flexbox.(to_class (justify `Center)); 23 - Spacing.(to_class (p (Size.rem 2.0))); 24 - ] in 25 - 26 - let container_classes = Css.tw [ 27 - Spacing.(to_class (mx `Auto)); 28 - Typography.(to_class (text_align `Center)); 29 - Spacing.(to_class (px (Size.rem 1.0))); 30 - ] in 31 - 32 - let paragraph_classes = Css.tw [ 33 - Color.text (Color.make `Gray ~variant:`V600 ()); 34 - Spacing.(to_class (mb (Size.rem 1.5))); 35 - ] in 36 - 37 - let card_classes = Css.tw [ 38 - Color.bg Color.white; 39 - Effects.rounded_lg; 40 - Effects.shadow_sm; 41 - Spacing.(to_class (p (Size.rem 1.5))); 42 - Typography.(to_class (text_align `Left)); 43 - ] in 44 - 45 - let subheading_classes = Css.tw [ 46 - Typography.(to_class (font_size `Lg)); 47 - Typography.(to_class (font_weight `Semibold)); 48 - Color.text (Color.make `Gray ~variant:`V800 ()); 49 - Spacing.(to_class (mb (Size.rem 0.75))); 50 - ] in 51 - 52 - let code_block_classes = Css.tw [ 53 - Color.bg (Color.make `Gray ~variant:`V100 ()); 54 - Spacing.(to_class (p (Size.rem 0.75))); 55 - Effects.rounded_sm; 56 - Typography.(to_class (font_size `Sm)); 57 - Layout.(to_class (overflow `X `Auto)); 58 - ] in 59 - 60 - let code_classes = Css.tw [ 61 - Color.text (Color.make `Blue ~variant:`V600 ()); 62 - ] in 63 - 64 - let section_classes = Css.tw [ 65 - Spacing.(to_class (mt (Size.rem 2.0))); 66 - Spacing.(to_class (gap `Y (Size.rem 1.0))); 67 - ] in 68 - 69 - let list_classes = Css.tw [ 70 - Typography.(to_class (text_align `Left)); 71 - Spacing.(to_class (gap `Y (Size.rem 0.5))); 72 - Color.text (Color.make `Gray ~variant:`V600 ()); 73 - ] in 74 - 75 - let list_item_classes = Css.tw [ 76 - Display.flex; 77 - Flexbox.(to_class (align_items `Start)); 78 - ] in 79 - 80 - let checkmark_classes = Css.tw [ 81 - Color.text (Color.make `Green ~variant:`V500 ()); 82 - Spacing.(to_class (mr (Size.rem 0.5))); 83 - ] in 84 7 85 8 let html = El.html [ 86 9 El.head [ 87 10 El.meta ~at:[At.charset "utf-8"] (); 88 11 El.meta ~at:[At.name "viewport"; At.content "width=device-width, initial-scale=1"] (); 89 - El.title [El.txt "Hello Tailwind"]; 12 + El.title [txt "Hello Tailwind"]; 90 13 El.link ~at:[At.rel "stylesheet"; At.href "hello_tailwind_01.css"] (); 91 14 ]; 92 - El.body ~at:[classes_attr body_classes] [ 93 - El.div ~at:[classes_attr container_classes] [ 94 - El.h1 ~at:[classes_attr hello_classes] [ 95 - El.txt "Hello, Tailwind OCaml!" 15 + El.body ~at:[classes_attr (Tailwind.Css.tw [ 16 + Tailwind.Layout.(to_class (min_height screen)); 17 + Tailwind.Color.bg (gray 50); 18 + flex; 19 + items_center; 20 + justify_center; 21 + Tailwind.Spacing.(to_class (p (rem 2.0))); 22 + ])] [ 23 + container [ 24 + h1 ~size:`Xl2 ~weight:`Bold ~color:(blue 600) ~mb:(rem 1.0) [ 25 + txt "Hello, Tailwind OCaml!" 96 26 ]; 97 - El.p ~at:[classes_attr paragraph_classes] [ 98 - El.txt "This is your first Tailwind OCaml program. "; 99 - El.txt "The heading above uses type-safe Tailwind classes." 27 + p_styled ~color:(gray 600) ~mb:(rem 1.5) [ 28 + txt "This is your first Tailwind OCaml program. "; 29 + txt "The heading above uses type-safe Tailwind classes." 100 30 ]; 101 - El.div ~at:[classes_attr card_classes] [ 102 - El.h2 ~at:[classes_attr subheading_classes] [ 103 - El.txt "Generated Classes:" 31 + card [ 32 + h2 ~size:`Lg ~weight:`Semibold ~color:(gray 800) ~mb:(rem 0.75) [ 33 + txt "Generated Classes:" 104 34 ]; 105 - El.pre ~at:[classes_attr code_block_classes] [ 106 - El.code ~at:[classes_attr code_classes] [ 107 - El.txt (to_string hello_classes) 35 + El.pre ~at:[classes_attr (Tailwind.Css.tw [ 36 + Tailwind.Color.bg (gray 100); 37 + Tailwind.Spacing.(to_class (p (rem 0.75))); 38 + Tailwind.Effects.rounded_sm; 39 + Tailwind.Typography.(to_class (font_size `Sm)); 40 + Tailwind.Layout.(to_class (overflow `X `Auto)); 41 + ])] [ 42 + El.code ~at:[classes_attr (Tailwind.Css.tw [ 43 + Tailwind.Color.text (blue 600); 44 + ])] [ 45 + txt "text-blue-600 text-2xl font-bold mb-4" 108 46 ]; 109 47 ]; 110 48 ]; 111 - El.div ~at:[classes_attr section_classes] [ 112 - El.h3 ~at:[classes_attr subheading_classes] [ 113 - El.txt "What you're learning:" 49 + div ~classes:(Tailwind.Css.tw [ 50 + Tailwind.Spacing.(to_class (mt (rem 2.0))); 51 + ]) [ 52 + h2 ~size:`Lg ~weight:`Semibold ~color:(gray 700) ~mb:(rem 1.5) [ 53 + txt "What you're learning:" 114 54 ]; 115 - El.ul ~at:[classes_attr list_classes] [ 116 - El.li ~at:[classes_attr list_item_classes] [ 117 - El.span ~at:[classes_attr checkmark_classes] [El.txt "✓"]; 118 - El.txt "Using the `tw` function to compose Tailwind classes" 55 + ul ~classes:(Tailwind.Css.tw [ 56 + Tailwind.Typography.(to_class (text_align `Left)); 57 + Tailwind.Spacing.(to_class (gap `Y (rem 0.5))); 58 + Tailwind.Color.text (gray 600); 59 + ]) [ 60 + li ~classes:(Tailwind.Css.tw [flex; Tailwind.Flexbox.(to_class (align_items `Start))]) [ 61 + span ~classes:(Tailwind.Css.tw [ 62 + Tailwind.Color.text (green 500); 63 + Tailwind.Spacing.(to_class (mr (rem 0.5))); 64 + ]) [txt "✓"]; 65 + txt "Using succinct combinator functions" 119 66 ]; 120 - El.li ~at:[classes_attr list_item_classes] [ 121 - El.span ~at:[classes_attr checkmark_classes] [El.txt "✓"]; 122 - El.txt "Type-safe color creation with variants" 67 + li ~classes:(Tailwind.Css.tw [flex; Tailwind.Flexbox.(to_class (align_items `Start))]) [ 68 + span ~classes:(Tailwind.Css.tw [ 69 + Tailwind.Color.text (green 500); 70 + Tailwind.Spacing.(to_class (mr (rem 0.5))); 71 + ]) [txt "✓"]; 72 + txt "Type-safe color creation with simple functions" 123 73 ]; 124 - El.li ~at:[classes_attr list_item_classes] [ 125 - El.span ~at:[classes_attr checkmark_classes] [El.txt "✓"]; 126 - El.txt "Typography utilities for font size and weight" 74 + li ~classes:(Tailwind.Css.tw [flex; Tailwind.Flexbox.(to_class (align_items `Start))]) [ 75 + span ~classes:(Tailwind.Css.tw [ 76 + Tailwind.Color.text (green 500); 77 + Tailwind.Spacing.(to_class (mr (rem 0.5))); 78 + ]) [txt "✓"]; 79 + txt "Enhanced element functions with styling parameters" 127 80 ]; 128 - El.li ~at:[classes_attr list_item_classes] [ 129 - El.span ~at:[classes_attr checkmark_classes] [El.txt "✓"]; 130 - El.txt "Converting Tailwind classes to HTML attributes" 81 + li ~classes:(Tailwind.Css.tw [flex; Tailwind.Flexbox.(to_class (align_items `Start))]) [ 82 + span ~classes:(Tailwind.Css.tw [ 83 + Tailwind.Color.text (green 500); 84 + Tailwind.Spacing.(to_class (mr (rem 0.5))); 85 + ]) [txt "✓"]; 86 + txt "Automatic class-to-attribute conversion" 131 87 ]; 132 88 ]; 133 89 ];
+181 -14
lib/tailwind-html/tailwind_html.ml
··· 1 1 (* Main module for Tailwind HTML library *) 2 2 3 - (* Re-export all submodules *) 4 - module Component = Component 5 - module Button = Button 6 - module Card = Card 7 - module Form = Form 8 - module Layout = Layout 9 - module Cli = Cli 10 - 11 3 (* Common utility for converting Tailwind classes to HTML class attribute *) 12 4 let classes_attr tailwind_classes = 13 5 Htmlit.At.class' (Tailwind.to_string tailwind_classes) ··· 45 37 let img ?classes ?attributes ~src ~alt () = 46 38 let attrs = match attributes with Some a -> a | None -> [] in 47 39 el "img" ?classes ~attributes:(("src", src) :: ("alt", alt) :: attrs) [] 48 - let h1 ?classes ?attributes children = el "h1" ?classes ?attributes children 49 - let h2 ?classes ?attributes children = el "h2" ?classes ?attributes children 50 - let h3 ?classes ?attributes children = el "h3" ?classes ?attributes children 51 - let h4 ?classes ?attributes children = el "h4" ?classes ?attributes children 52 - let h5 ?classes ?attributes children = el "h5" ?classes ?attributes children 53 - let h6 ?classes ?attributes children = el "h6" ?classes ?attributes children 54 40 let ul ?classes ?attributes children = el "ul" ?classes ?attributes children 55 41 let ol ?classes ?attributes children = el "ol" ?classes ?attributes children 56 42 let li ?classes ?attributes children = el "li" ?classes ?attributes children 43 + 44 + (* Utility functions for colors and sizes *) 45 + let 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 + 50 + let 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 + 55 + let 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 + 60 + let 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 + 65 + let rem f = Tailwind.Size.rem f 66 + let px = Tailwind.Size.px 67 + let zero = Tailwind.Size.zero 68 + let auto = Tailwind.Size.auto 69 + let full = Tailwind.Size.full 70 + let screen = Tailwind.Size.screen 71 + let txt s = Htmlit.El.txt s 72 + 73 + (* Common utility classes *) 74 + let flex = Tailwind.Display.flex 75 + let flex_col = Tailwind.Flexbox.(to_class (direction `Col)) 76 + let items_center = Tailwind.Flexbox.(to_class (align_items `Center)) 77 + let justify_center = Tailwind.Flexbox.(to_class (justify `Center)) 78 + let justify_between = Tailwind.Flexbox.(to_class (justify `Between)) 79 + let font_bold = Tailwind.Typography.(to_class (font_weight `Bold)) 80 + let font_semibold = Tailwind.Typography.(to_class (font_weight `Semibold)) 81 + let text_center = Tailwind.Typography.(to_class (text_align `Center)) 82 + let w_full = Tailwind.Layout.w_full 83 + let h_full = Tailwind.Layout.h_full 84 + let rounded_lg = Tailwind.Effects.rounded_lg 85 + let rounded_md = Tailwind.Effects.rounded_md 86 + let shadow_md = Tailwind.Effects.shadow_md 87 + let shadow_lg = Tailwind.Effects.shadow_lg 88 + 89 + (* Enhanced element functions with styling parameters *) 90 + let 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 + 117 + let 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 + 143 + let 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 *) 164 + let container children = 165 + let container_classes = Tailwind.Css.tw [Tailwind.Patterns.container ()] in 166 + div ~classes:container_classes children 167 + 168 + let flex_center children = 169 + let flex_classes = Tailwind.Css.tw [flex; items_center; justify_center] in 170 + div ~classes:flex_classes children 171 + 172 + let 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 + 179 + let 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 + 202 + let 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 57 224 58 225 (* Text element with built-in typography utilities *) 59 226 let text ?size ?weight ?color ?align ?classes text_content =
+76 -14
lib/tailwind-html/tailwind_html.mli
··· 1 1 (** Main Tailwind-HTML integration module *) 2 2 3 - (** Re-exported submodules *) 4 - module Button : module type of Button 5 - module Card : module type of Card 6 - module Component : module type of Component 7 - module Form : module type of Form 8 - module Layout : module type of Layout 9 - module Cli : module type of Cli 10 - 11 3 (** Convert Tailwind classes to HTML class attribute *) 12 4 val classes_attr : Tailwind.t -> Htmlit.At.t 13 5 ··· 31 23 val p : ?classes:Tailwind.t -> ?attributes:(string * string) list -> Htmlit.El.html list -> Htmlit.El.html 32 24 val a : ?classes:Tailwind.t -> ?attributes:(string * string) list -> href:string -> Htmlit.El.html list -> Htmlit.El.html 33 25 val img : ?classes:Tailwind.t -> ?attributes:(string * string) list -> src:string -> alt:string -> unit -> Htmlit.El.html 34 - val h1 : ?classes:Tailwind.t -> ?attributes:(string * string) list -> Htmlit.El.html list -> Htmlit.El.html 35 - val h2 : ?classes:Tailwind.t -> ?attributes:(string * string) list -> Htmlit.El.html list -> Htmlit.El.html 36 - val h3 : ?classes:Tailwind.t -> ?attributes:(string * string) list -> Htmlit.El.html list -> Htmlit.El.html 37 - val h4 : ?classes:Tailwind.t -> ?attributes:(string * string) list -> Htmlit.El.html list -> Htmlit.El.html 38 - val h5 : ?classes:Tailwind.t -> ?attributes:(string * string) list -> Htmlit.El.html list -> Htmlit.El.html 39 - val h6 : ?classes:Tailwind.t -> ?attributes:(string * string) list -> Htmlit.El.html list -> Htmlit.El.html 40 26 val ul : ?classes:Tailwind.t -> ?attributes:(string * string) list -> Htmlit.El.html list -> Htmlit.El.html 41 27 val ol : ?classes:Tailwind.t -> ?attributes:(string * string) list -> Htmlit.El.html list -> Htmlit.El.html 42 28 val li : ?classes:Tailwind.t -> ?attributes:(string * string) list -> Htmlit.El.html list -> Htmlit.El.html 29 + 30 + (** Utility functions for colors and sizes *) 31 + val blue : int -> Tailwind.Color.t 32 + val gray : int -> Tailwind.Color.t 33 + val red : int -> Tailwind.Color.t 34 + val green : int -> Tailwind.Color.t 35 + val rem : float -> Tailwind.Size.t 36 + val px : Tailwind.Size.t 37 + val zero : Tailwind.Size.t 38 + val auto : Tailwind.Size.t 39 + val full : Tailwind.Size.t 40 + val screen : Tailwind.Size.t 41 + val txt : string -> Htmlit.El.html 42 + 43 + (** Common utility classes *) 44 + val flex : Tailwind.t 45 + val flex_col : Tailwind.t 46 + val items_center : Tailwind.t 47 + val justify_center : Tailwind.t 48 + val justify_between : Tailwind.t 49 + val font_bold : Tailwind.t 50 + val font_semibold : Tailwind.t 51 + val text_center : Tailwind.t 52 + val w_full : Tailwind.t 53 + val h_full : Tailwind.t 54 + val rounded_lg : Tailwind.t 55 + val rounded_md : Tailwind.t 56 + val shadow_md : Tailwind.t 57 + val shadow_lg : Tailwind.t 58 + 59 + (** Enhanced element functions with styling parameters *) 60 + val h1 : 61 + ?size:[`Xl | `Xl2 | `Xl3 | `Xl4] -> 62 + ?weight:[`Bold | `Semibold | `Medium] -> 63 + ?color:Tailwind.Color.t -> 64 + ?align:[`Center | `Left | `Right] -> 65 + ?mb:Tailwind.Size.t -> 66 + ?classes:Tailwind.t -> 67 + Htmlit.El.html list -> 68 + Htmlit.El.html 69 + 70 + val h2 : 71 + ?size:[`Lg | `Xl | `Xl2] -> 72 + ?weight:[`Bold | `Semibold | `Medium] -> 73 + ?color:Tailwind.Color.t -> 74 + ?align:[`Center | `Left | `Right] -> 75 + ?mb:Tailwind.Size.t -> 76 + ?classes:Tailwind.t -> 77 + Htmlit.El.html list -> 78 + Htmlit.El.html 79 + 80 + val p_styled : 81 + ?size:[`Sm | `Base | `Lg] -> 82 + ?color:Tailwind.Color.t -> 83 + ?align:[`Center | `Left | `Right] -> 84 + ?mb:Tailwind.Size.t -> 85 + ?classes:Tailwind.t -> 86 + Htmlit.El.html list -> 87 + Htmlit.El.html 88 + 89 + (** Simple component functions *) 90 + val container : Htmlit.El.html list -> Htmlit.El.html 91 + val flex_center : Htmlit.El.html list -> Htmlit.El.html 92 + val card : ?elevated:bool -> ?padding:bool -> Htmlit.El.html list -> Htmlit.El.html 93 + 94 + val btn_primary : 95 + ?size:[`Sm | `Md | `Lg] -> 96 + ?disabled:bool -> 97 + Htmlit.El.html list -> 98 + Htmlit.El.html 99 + 100 + val btn_secondary : 101 + ?size:[`Sm | `Md | `Lg] -> 102 + ?disabled:bool -> 103 + Htmlit.El.html list -> 104 + Htmlit.El.html 43 105 44 106 (** Text element with typography utilities *) 45 107 val text :