Tailwind classes in OCaml

Fix Tailwind CSS integration and improve examples

- Add comprehensive dune build system integration with Tailwind CLI v4
- Create input.css for centralized CSS configuration without inline styles
- Add CLI helper module for Tailwind CSS processing via npx @tailwindcss/cli
- Fix examples to output valid HTML instead of text descriptions
- Convert hello_tailwind_01.ml to use pure library API instead of raw class strings
- Update build system to generate both HTML and CSS files from OCaml examples
- Add proper HTML generation pipeline: OCaml -> HTML -> Tailwind CSS processing
- Fix effects_and_variants_05.ml and patterns_and_components_06.ml output issues
- Remove problematic responsive classes and @apply directives causing v4 CLI errors

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

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

+1123 -3
+195
examples/colors_and_typography_02.ml
··· 1 + (* Example 02: Colors and Typography - Exploring the Type System *) 2 + 3 + open Htmlit 4 + open Tailwind 5 + 6 + let classes_attr tailwind_classes = 7 + At.class' (Tailwind.to_string tailwind_classes) 8 + 9 + let create_color_demo () = 10 + (* Color variants demonstration *) 11 + let color_examples = [ 12 + ("Blue 400", Color.make `Blue ~variant:`V400 ()); 13 + ("Blue 600", Color.make `Blue ~variant:`V600 ()); 14 + ("Green 500", Color.make `Green ~variant:`V500 ()); 15 + ("Red 500", Color.make `Red ~variant:`V500 ()); 16 + ("Purple 600", Color.make `Purple ~variant:`V600 ()); 17 + ("Gray 700", Color.make `Gray ~variant:`V700 ()); 18 + ] in 19 + 20 + let typography_examples = [ 21 + ("Extra Small", Typography.(to_class (font_size `Xs))); 22 + ("Small", Typography.(to_class (font_size `Sm))); 23 + ("Base", Typography.(to_class (font_size `Base))); 24 + ("Large", Typography.(to_class (font_size `Lg))); 25 + ("Extra Large", Typography.(to_class (font_size `Xl))); 26 + ("2X Large", Typography.(to_class (font_size `Xl2))); 27 + ] in 28 + 29 + (* Create HTML demonstration *) 30 + let html_doc = El.html [ 31 + El.head [ 32 + El.meta ~at:[At.charset "utf-8"] (); 33 + El.meta ~at:[At.name "viewport"; At.content "width=device-width, initial-scale=1"] (); 34 + El.title [El.txt "Colors and Typography"]; 35 + El.link ~at:[At.rel "stylesheet"; At.href "colors_and_typography_02.css"] (); 36 + ]; 37 + El.body ~at:[At.class' "min-h-screen bg-gray-50 p-8"] [ 38 + El.div ~at:[At.class' "max-w-4xl mx-auto"] [ 39 + El.h1 ~at:[classes_attr (tw [ 40 + Typography.(to_class (font_size `Xl2)); 41 + Typography.(to_class (font_weight `Bold)); 42 + Color.text (Color.make `Gray ~variant:`V800 ()); 43 + ]); At.class' "mb-8 text-center"] [El.txt "Colors and Typography Demo"]; 44 + 45 + El.p ~at:[classes_attr (tw [ 46 + Typography.(to_class (font_size `Lg)); 47 + Color.text (Color.make `Gray ~variant:`V600 ()); 48 + ]); At.class' "text-center mb-12"] [ 49 + El.txt "Explore the type-safe color system and typography utilities in Tailwind OCaml." 50 + ]; 51 + 52 + (* Color Palette Section *) 53 + El.section ~at:[At.class' "mb-12"] [ 54 + El.h2 ~at:[classes_attr (tw [ 55 + Typography.(to_class (font_size `Xl)); 56 + Typography.(to_class (font_weight `Semibold)); 57 + Color.text (Color.make `Gray ~variant:`V700 ()); 58 + ]); At.class' "mb-8"] [El.txt "Color Palette"]; 59 + 60 + El.div ~at:[classes_attr (tw [ 61 + Display.grid; 62 + Grid.(to_class (template_cols (`Cols 1))); 63 + Responsive.(to_class (at_breakpoint `Md (Grid.(to_class (template_cols (`Cols 2)))))); 64 + Responsive.(to_class (at_breakpoint `Lg (Grid.(to_class (template_cols (`Cols 3)))))); 65 + Spacing.(to_class (gap `All (Size.rem 1.5))); 66 + ])] (List.map (fun (name, color) -> 67 + El.div ~at:[classes_attr (tw [ 68 + Color.bg Color.white; 69 + Spacing.(to_class (p (Size.rem 1.5))); 70 + Effects.rounded_lg; 71 + Effects.shadow_sm; 72 + ]); At.class' "text-center"] [ 73 + El.div ~at:[classes_attr (tw [ 74 + Color.text color; 75 + Typography.(to_class (font_size `Xl2)); 76 + Typography.(to_class (font_weight `Bold)); 77 + ]); At.class' "mb-4"] [El.txt "Aa"]; 78 + El.h3 ~at:[classes_attr (tw [ 79 + Typography.(to_class (font_size `Lg)); 80 + Typography.(to_class (font_weight `Semibold)); 81 + Color.text (Color.make `Gray ~variant:`V800 ()); 82 + ]); At.class' "mb-2"] [El.txt name]; 83 + El.p ~at:[classes_attr (tw [ 84 + Typography.(to_class (font_size `Sm)); 85 + Color.text (Color.make `Gray ~variant:`V500 ()); 86 + ]); At.class' "mb-3"] [El.txt (to_string (tw [Color.text color]))]; 87 + El.p ~at:[classes_attr (tw [Color.text color])] [ 88 + El.txt "The quick brown fox jumps over the lazy dog" 89 + ]; 90 + ] 91 + ) color_examples); 92 + ]; 93 + 94 + (* Typography Scale Section *) 95 + El.section ~at:[At.class' "mb-12"] [ 96 + El.h2 ~at:[classes_attr (tw [ 97 + Typography.(to_class (font_size `Xl)); 98 + Typography.(to_class (font_weight `Semibold)); 99 + Color.text (Color.make `Gray ~variant:`V700 ()); 100 + ]); At.class' "mb-8"] [El.txt "Typography Scale"]; 101 + 102 + El.div ~at:[classes_attr (tw [ 103 + Color.bg Color.white; 104 + Spacing.(to_class (p (Size.rem 2.0))); 105 + Effects.rounded_lg; 106 + Effects.shadow_sm; 107 + ]); At.class' "mb-8"] (List.map (fun (name, typ_class) -> 108 + El.div ~at:[At.class' "mb-6 last:mb-0"] [ 109 + El.div ~at:[classes_attr (tw [ 110 + Display.flex; 111 + Flexbox.(to_class (align_items `Center)); 112 + Flexbox.(to_class (justify `Between)); 113 + Spacing.(to_class (gap `All (Size.rem 1.0))); 114 + ]); At.class' "mb-2"] [ 115 + El.span ~at:[classes_attr (tw [ 116 + Typography.(to_class (font_size `Sm)); 117 + Typography.(to_class (font_weight `Medium)); 118 + Color.text (Color.make `Gray ~variant:`V500 ()); 119 + ])] [El.txt name]; 120 + El.code ~at:[classes_attr (tw [ 121 + Typography.(to_class (font_size `Xs)); 122 + Color.bg (Color.make `Gray ~variant:`V100 ()); 123 + Spacing.(to_class (px (Size.rem 0.5))); 124 + Spacing.(to_class (py (Size.rem 0.25))); 125 + Effects.rounded_sm; 126 + ])] [El.txt (to_string (tw [typ_class]))]; 127 + ]; 128 + El.p ~at:[classes_attr (tw [typ_class])] [ 129 + El.txt "The quick brown fox jumps over the lazy dog" 130 + ]; 131 + ] 132 + ) typography_examples); 133 + ]; 134 + 135 + (* Font Weights Section *) 136 + El.section [ 137 + El.h2 ~at:[classes_attr (tw [ 138 + Typography.(to_class (font_size `Xl)); 139 + Typography.(to_class (font_weight `Semibold)); 140 + Color.text (Color.make `Gray ~variant:`V700 ()); 141 + ]); At.class' "mb-8"] [El.txt "Font Weights"]; 142 + 143 + let weight_examples = [ 144 + ("Light", Typography.(to_class (font_weight `Light))); 145 + ("Normal", Typography.(to_class (font_weight `Normal))); 146 + ("Medium", Typography.(to_class (font_weight `Medium))); 147 + ("Semibold", Typography.(to_class (font_weight `Semibold))); 148 + ("Bold", Typography.(to_class (font_weight `Bold))); 149 + ("Extrabold", Typography.(to_class (font_weight `Extrabold))); 150 + ] in 151 + 152 + El.div ~at:[classes_attr (tw [ 153 + Color.bg Color.white; 154 + Spacing.(to_class (p (Size.rem 2.0))); 155 + Effects.rounded_lg; 156 + Effects.shadow_sm; 157 + ])] (List.map (fun (name, weight_class) -> 158 + El.div ~at:[At.class' "mb-6 last:mb-0"] [ 159 + El.div ~at:[classes_attr (tw [ 160 + Display.flex; 161 + Flexbox.(to_class (align_items `Center)); 162 + Flexbox.(to_class (justify `Between)); 163 + Spacing.(to_class (gap `All (Size.rem 1.0))); 164 + ]); At.class' "mb-2"] [ 165 + El.span ~at:[classes_attr (tw [ 166 + Typography.(to_class (font_size `Sm)); 167 + Typography.(to_class (font_weight `Medium)); 168 + Color.text (Color.make `Gray ~variant:`V500 ()); 169 + ])] [El.txt name]; 170 + El.code ~at:[classes_attr (tw [ 171 + Typography.(to_class (font_size `Xs)); 172 + Color.bg (Color.make `Gray ~variant:`V100 ()); 173 + Spacing.(to_class (px (Size.rem 0.5))); 174 + Spacing.(to_class (py (Size.rem 0.25))); 175 + Effects.rounded_sm; 176 + ])] [El.txt (to_string (tw [weight_class]))]; 177 + ]; 178 + El.p ~at:[classes_attr (tw [ 179 + weight_class; 180 + Typography.(to_class (font_size `Lg)); 181 + ])] [ 182 + El.txt "The quick brown fox jumps over the lazy dog" 183 + ]; 184 + ] 185 + ) weight_examples); 186 + ]; 187 + ]; 188 + ]; 189 + ] in 190 + html_doc 191 + 192 + let () = 193 + (* Output HTML to stdout *) 194 + let html_doc = create_color_demo () in 195 + print_string (El.to_string ~doctype:true html_doc)
+166 -3
examples/dune
··· 1 + ;; Notebook-style examples - progressive learning path 1 2 (executables 2 - (public_names basic_usage module_usage advanced_features) 3 - (names basic_usage module_usage advanced_features) 3 + (public_names 4 + index 5 + hello_tailwind 6 + colors_and_typography 7 + layout_and_spacing 8 + responsive_design 9 + effects_and_variants 10 + patterns_and_components 11 + comprehensive_showcase) 12 + (names 13 + index_00 14 + hello_tailwind_01 15 + colors_and_typography_02 16 + layout_and_spacing_03 17 + responsive_design_04 18 + effects_and_variants_05 19 + patterns_and_components_06 20 + comprehensive_showcase_07) 21 + (package tailwind) 22 + (libraries tailwind tailwind-html htmlit unix)) 23 + 24 + ;; Legacy examples (maintained for compatibility) 25 + (executables 26 + (public_names basic_usage module_usage advanced_features simple_html_example complete_demo tailwind_html_example improved_api_demo) 27 + (names basic_usage module_usage advanced_features simple_html_example complete_demo tailwind_html_example improved_api_demo) 28 + (package tailwind) 29 + (libraries tailwind htmlit unix)) 30 + 31 + ;; Generate HTML files from examples 32 + (rule 33 + (target hello_tailwind_01.html) 34 + (deps (:exe hello_tailwind_01.exe)) 35 + (action (with-stdout-to %{target} (run %{exe})))) 36 + 37 + (rule 38 + (target colors_and_typography_02.html) 39 + (deps (:exe colors_and_typography_02.exe)) 40 + (action (with-stdout-to %{target} (run %{exe})))) 41 + 42 + (rule 43 + (target layout_and_spacing_03.html) 44 + (deps (:exe layout_and_spacing_03.exe)) 45 + (action (with-stdout-to %{target} (run %{exe})))) 46 + 47 + (rule 48 + (target responsive_design_04.html) 49 + (deps (:exe responsive_design_04.exe)) 50 + (action (with-stdout-to %{target} (run %{exe})))) 51 + 52 + (rule 53 + (target effects_and_variants_05.html) 54 + (deps (:exe effects_and_variants_05.exe)) 55 + (action (with-stdout-to %{target} (run %{exe})))) 56 + 57 + (rule 58 + (target patterns_and_components_06.html) 59 + (deps (:exe patterns_and_components_06.exe)) 60 + (action (with-stdout-to %{target} (run %{exe})))) 61 + 62 + (rule 63 + (target comprehensive_showcase_07.html) 64 + (deps (:exe comprehensive_showcase_07.exe)) 65 + (action (with-stdout-to %{target} (run %{exe})))) 66 + 67 + ;; Generate CSS files using Tailwind CLI 68 + (rule 69 + (targets hello_tailwind_01.css) 70 + (deps 71 + input.css 72 + hello_tailwind_01.html) 73 + (action 74 + (run npx @tailwindcss/cli --input input.css --output %{targets} --content hello_tailwind_01.html --minify))) 75 + 76 + (rule 77 + (targets colors_and_typography_02.css) 78 + (deps 79 + input.css 80 + colors_and_typography_02.html) 81 + (action 82 + (run npx @tailwindcss/cli --input input.css --output %{targets} --content colors_and_typography_02.html --minify))) 83 + 84 + (rule 85 + (targets layout_and_spacing_03.css) 86 + (deps 87 + input.css 88 + layout_and_spacing_03.html) 89 + (action 90 + (run npx @tailwindcss/cli --input input.css --output %{targets} --content layout_and_spacing_03.html --minify))) 91 + 92 + (rule 93 + (targets responsive_design_04.css) 94 + (deps 95 + input.css 96 + responsive_design_04.html) 97 + (action 98 + (run npx @tailwindcss/cli --input input.css --output %{targets} --content responsive_design_04.html --minify))) 99 + 100 + (rule 101 + (targets effects_and_variants_05.css) 102 + (deps 103 + input.css 104 + effects_and_variants_05.html) 105 + (action 106 + (run npx @tailwindcss/cli --input input.css --output %{targets} --content effects_and_variants_05.html --minify))) 107 + 108 + (rule 109 + (targets patterns_and_components_06.css) 110 + (deps 111 + input.css 112 + patterns_and_components_06.html) 113 + (action 114 + (run npx @tailwindcss/cli --input input.css --output %{targets} --content patterns_and_components_06.html --minify))) 115 + 116 + (rule 117 + (targets comprehensive_showcase_07.css) 118 + (deps 119 + input.css 120 + comprehensive_showcase_07.html) 121 + (action 122 + (run npx @tailwindcss/cli --input input.css --output %{targets} --content comprehensive_showcase_07.html --minify))) 123 + 124 + ;; Alias to build all HTML files 125 + (alias 126 + (name examples-html) 127 + (deps 128 + hello_tailwind_01.html 129 + colors_and_typography_02.html 130 + layout_and_spacing_03.html 131 + responsive_design_04.html 132 + effects_and_variants_05.html 133 + patterns_and_components_06.html 134 + comprehensive_showcase_07.html)) 135 + 136 + ;; Alias to build all CSS files (requires Tailwind CLI) 137 + ;; Run: npm install -D @tailwindcss/cli 138 + (alias 139 + (name examples-css) 140 + (deps 141 + (alias_rec examples-html) 142 + hello_tailwind_01.css 143 + colors_and_typography_02.css 144 + layout_and_spacing_03.css 145 + responsive_design_04.css 146 + effects_and_variants_05.css 147 + patterns_and_components_06.css 148 + comprehensive_showcase_07.css)) 149 + 150 + ;; Install generated files 151 + (install 152 + (section doc) 4 153 (package tailwind) 5 - (libraries tailwind)) 154 + (files 155 + (hello_tailwind_01.html as examples/hello_tailwind_01.html) 156 + (hello_tailwind_01.css as examples/hello_tailwind_01.css) 157 + (colors_and_typography_02.html as examples/colors_and_typography_02.html) 158 + (colors_and_typography_02.css as examples/colors_and_typography_02.css) 159 + (layout_and_spacing_03.html as examples/layout_and_spacing_03.html) 160 + (layout_and_spacing_03.css as examples/layout_and_spacing_03.css) 161 + (responsive_design_04.html as examples/responsive_design_04.html) 162 + (responsive_design_04.css as examples/responsive_design_04.css) 163 + (effects_and_variants_05.html as examples/effects_and_variants_05.html) 164 + (effects_and_variants_05.css as examples/effects_and_variants_05.css) 165 + (patterns_and_components_06.html as examples/patterns_and_components_06.html) 166 + (patterns_and_components_06.css as examples/patterns_and_components_06.css) 167 + (comprehensive_showcase_07.html as examples/comprehensive_showcase_07.html) 168 + (comprehensive_showcase_07.css as examples/comprehensive_showcase_07.css)))
+326
examples/effects_and_variants_05.ml
··· 1 + (* Example 05: Effects and Variants - Interactive Elements and Visual Effects *) 2 + 3 + open Htmlit 4 + open Tailwind 5 + 6 + let classes_attr tailwind_classes = 7 + At.class' (Tailwind.to_string tailwind_classes) 8 + 9 + let create_effects_demo () = 10 + 11 + (* Create comprehensive effects demonstration *) 12 + let html_doc = El.html [ 13 + El.head [ 14 + El.meta ~at:[At.charset "utf-8"] (); 15 + El.meta ~at:[At.name "viewport"; At.content "width=device-width, initial-scale=1"] (); 16 + El.title [El.txt "Effects and Variants"]; 17 + El.link ~at:[At.rel "stylesheet"; At.href "effects_and_variants_05.css"] (); 18 + ]; 19 + El.body ~at:[At.class' "min-h-screen bg-gray-50 p-8"] [ 20 + El.div ~at:[At.class' "max-w-6xl mx-auto"] [ 21 + El.h1 ~at:[classes_attr (tw [ 22 + Typography.(to_class (font_size `Xl2)); 23 + Typography.(to_class (font_weight `Bold)); 24 + Color.text (Color.make `Gray ~variant:`V800 ()); 25 + ]); At.class' "mb-8 text-center"] [El.txt "Effects and Variants Demo"]; 26 + 27 + (* Shadow Effects *) 28 + El.section ~at:[At.class' "mb-8"] [ 29 + El.h2 ~at:[classes_attr (tw [ 30 + Typography.(to_class (font_size `Xl)); 31 + Typography.(to_class (font_weight `Semibold)); 32 + Color.text (Color.make `Gray ~variant:`V700 ()); 33 + ]); At.class' "mb-6"] [El.txt "Shadow Effects"]; 34 + 35 + El.div ~at:[classes_attr (tw [ 36 + Display.grid; 37 + Grid.(to_class (template_cols (`Cols 1))); 38 + Responsive.(to_class (at_breakpoint `Md (Grid.(to_class (template_cols (`Cols 3)))))); 39 + Spacing.(to_class (gap `All (Size.rem 1.5))); 40 + ])] [ 41 + El.div ~at:[classes_attr (tw [ 42 + Color.bg Color.white; 43 + Spacing.(to_class (p (Size.rem 1.5))); 44 + Effects.shadow_sm; 45 + Effects.rounded_lg; 46 + ]); At.class' "text-center"] [ 47 + El.h3 ~at:[classes_attr (tw [ 48 + Typography.(to_class (font_weight `Semibold)); 49 + Color.text (Color.make `Gray ~variant:`V700 ()); 50 + ]); At.class' "mb-2"] [El.txt "Small Shadow"]; 51 + El.p ~at:[classes_attr (tw [ 52 + Typography.(to_class (font_size `Sm)); 53 + Color.text (Color.make `Gray ~variant:`V600 ()); 54 + ])] [El.txt "shadow-sm"]; 55 + ]; 56 + 57 + El.div ~at:[classes_attr (tw [ 58 + Color.bg Color.white; 59 + Spacing.(to_class (p (Size.rem 1.5))); 60 + Effects.shadow_md; 61 + Effects.rounded_lg; 62 + ]); At.class' "text-center"] [ 63 + El.h3 ~at:[classes_attr (tw [ 64 + Typography.(to_class (font_weight `Semibold)); 65 + Color.text (Color.make `Gray ~variant:`V700 ()); 66 + ]); At.class' "mb-2"] [El.txt "Medium Shadow"]; 67 + El.p ~at:[classes_attr (tw [ 68 + Typography.(to_class (font_size `Sm)); 69 + Color.text (Color.make `Gray ~variant:`V600 ()); 70 + ])] [El.txt "shadow-md"]; 71 + ]; 72 + 73 + El.div ~at:[classes_attr (tw [ 74 + Color.bg Color.white; 75 + Spacing.(to_class (p (Size.rem 1.5))); 76 + Effects.shadow_lg; 77 + Effects.rounded_lg; 78 + ]); At.class' "text-center"] [ 79 + El.h3 ~at:[classes_attr (tw [ 80 + Typography.(to_class (font_weight `Semibold)); 81 + Color.text (Color.make `Gray ~variant:`V700 ()); 82 + ]); At.class' "mb-2"] [El.txt "Large Shadow"]; 83 + El.p ~at:[classes_attr (tw [ 84 + Typography.(to_class (font_size `Sm)); 85 + Color.text (Color.make `Gray ~variant:`V600 ()); 86 + ])] [El.txt "shadow-lg"]; 87 + ]; 88 + ]; 89 + ]; 90 + 91 + (* Rounded Corners *) 92 + El.section ~at:[At.class' "mb-8"] [ 93 + El.h2 ~at:[classes_attr (tw [ 94 + Typography.(to_class (font_size `Xl)); 95 + Typography.(to_class (font_weight `Semibold)); 96 + Color.text (Color.make `Gray ~variant:`V700 ()); 97 + ]); At.class' "mb-6"] [El.txt "Border Radius"]; 98 + 99 + El.div ~at:[classes_attr (tw [ 100 + Display.flex; 101 + Flexbox.(to_class (wrap `Wrap)); 102 + Spacing.(to_class (gap `All (Size.rem 1.0))); 103 + Flexbox.(to_class (justify `Center)); 104 + ])] [ 105 + El.div ~at:[classes_attr (tw [ 106 + Color.bg (Color.make `Blue ~variant:`V100 ()); 107 + Spacing.(to_class (p (Size.rem 1.0))); 108 + (* No rounded corners *) 109 + ]); At.class' "text-center"] [El.txt "No Radius"]; 110 + 111 + El.div ~at:[classes_attr (tw [ 112 + Color.bg (Color.make `Green ~variant:`V100 ()); 113 + Spacing.(to_class (p (Size.rem 1.0))); 114 + Effects.rounded_sm; 115 + ]); At.class' "text-center"] [El.txt "Small"]; 116 + 117 + El.div ~at:[classes_attr (tw [ 118 + Color.bg (Color.make `Purple ~variant:`V100 ()); 119 + Spacing.(to_class (p (Size.rem 1.0))); 120 + Effects.rounded_md; 121 + ]); At.class' "text-center"] [El.txt "Medium"]; 122 + 123 + El.div ~at:[classes_attr (tw [ 124 + Color.bg (Color.make `Red ~variant:`V100 ()); 125 + Spacing.(to_class (p (Size.rem 1.0))); 126 + Effects.rounded_lg; 127 + ]); At.class' "text-center"] [El.txt "Large"]; 128 + 129 + El.div ~at:[classes_attr (tw [ 130 + Color.bg (Color.make `Yellow ~variant:`V100 ()); 131 + Spacing.(to_class (p (Size.rem 1.0))); 132 + Effects.rounded_full; 133 + ]); At.class' "text-center"] [El.txt "Full"]; 134 + ]; 135 + ]; 136 + 137 + (* Interactive Buttons *) 138 + El.section ~at:[At.class' "mb-8"] [ 139 + El.h2 ~at:[classes_attr (tw [ 140 + Typography.(to_class (font_size `Xl)); 141 + Typography.(to_class (font_weight `Semibold)); 142 + Color.text (Color.make `Gray ~variant:`V700 ()); 143 + ]); At.class' "mb-6"] [El.txt "Interactive Buttons"]; 144 + 145 + El.div ~at:[classes_attr (tw [ 146 + Display.flex; 147 + Flexbox.(to_class (wrap `Wrap)); 148 + Spacing.(to_class (gap `All (Size.rem 1.0))); 149 + Flexbox.(to_class (justify `Center)); 150 + ])] [ 151 + (* Hover color change *) 152 + El.button ~at:[classes_attr (tw [ 153 + Color.bg (Color.make `Blue ~variant:`V500 ()); 154 + Color.text Color.white; 155 + Spacing.(to_class (px (Size.rem 1.5))); 156 + Spacing.(to_class (py (Size.rem 0.75))); 157 + Effects.rounded_md; 158 + Typography.(to_class (font_weight `Medium)); 159 + Effects.transition `All; 160 + Variants.hover (Color.bg (Color.make `Blue ~variant:`V600 ())); 161 + ])] [El.txt "Hover Color"]; 162 + 163 + (* Hover shadow *) 164 + El.button ~at:[classes_attr (tw [ 165 + Color.bg (Color.make `Green ~variant:`V500 ()); 166 + Color.text Color.white; 167 + Spacing.(to_class (px (Size.rem 1.5))); 168 + Spacing.(to_class (py (Size.rem 0.75))); 169 + Effects.rounded_md; 170 + Typography.(to_class (font_weight `Medium)); 171 + Effects.shadow_md; 172 + Effects.transition `All; 173 + Variants.hover Effects.shadow_lg; 174 + ])] [El.txt "Hover Shadow"]; 175 + 176 + (* Scale effect *) 177 + El.button ~at:[classes_attr (tw [ 178 + Color.bg (Color.make `Purple ~variant:`V500 ()); 179 + Color.text Color.white; 180 + Spacing.(to_class (px (Size.rem 1.5))); 181 + Spacing.(to_class (py (Size.rem 0.75))); 182 + Effects.rounded_md; 183 + Typography.(to_class (font_weight `Medium)); 184 + Effects.transition `All; 185 + ]); At.class' "hover:scale-105 active:scale-95"] [El.txt "Scale Effect"]; 186 + 187 + (* Focus ring *) 188 + El.button ~at:[classes_attr (tw [ 189 + Color.bg (Color.make `Red ~variant:`V500 ()); 190 + Color.text Color.white; 191 + Spacing.(to_class (px (Size.rem 1.5))); 192 + Spacing.(to_class (py (Size.rem 0.75))); 193 + Effects.rounded_md; 194 + Typography.(to_class (font_weight `Medium)); 195 + Effects.transition `All; 196 + Variants.focus Effects.shadow_md; 197 + ])] [El.txt "Focus Ring"]; 198 + ]; 199 + ]; 200 + 201 + (* Card Hover Effects *) 202 + El.section ~at:[At.class' "mb-8"] [ 203 + El.h2 ~at:[classes_attr (tw [ 204 + Typography.(to_class (font_size `Xl)); 205 + Typography.(to_class (font_weight `Semibold)); 206 + Color.text (Color.make `Gray ~variant:`V700 ()); 207 + ]); At.class' "mb-6"] [El.txt "Card Hover Effects"]; 208 + 209 + El.div ~at:[classes_attr (tw [ 210 + Display.grid; 211 + Grid.(to_class (template_cols (`Cols 1))); 212 + Responsive.(to_class (at_breakpoint `Md (Grid.(to_class (template_cols (`Cols 2)))))); 213 + Spacing.(to_class (gap `All (Size.rem 1.5))); 214 + ])] [ 215 + (* Hover shadow card *) 216 + El.div ~at:[classes_attr (tw [ 217 + Color.bg Color.white; 218 + Spacing.(to_class (p (Size.rem 1.5))); 219 + Effects.rounded_lg; 220 + Effects.shadow_md; 221 + Effects.transition `All; 222 + Variants.hover Effects.shadow_lg; 223 + ]); At.class' "cursor-pointer"] [ 224 + El.h3 ~at:[classes_attr (tw [ 225 + Typography.(to_class (font_size `Lg)); 226 + Typography.(to_class (font_weight `Semibold)); 227 + Color.text (Color.make `Gray ~variant:`V800 ()); 228 + ]); At.class' "mb-2"] [El.txt "Shadow Lift"]; 229 + El.p ~at:[classes_attr (tw [ 230 + Color.text (Color.make `Gray ~variant:`V600 ()); 231 + ])] [El.txt "Hover to see the shadow increase. This creates a lifting effect."]; 232 + ]; 233 + 234 + (* Scale and shadow card *) 235 + El.div ~at:[classes_attr (tw [ 236 + Color.bg Color.white; 237 + Spacing.(to_class (p (Size.rem 1.5))); 238 + Effects.rounded_lg; 239 + Effects.shadow_md; 240 + Effects.transition `All; 241 + ]); At.class' "cursor-pointer hover:scale-105 hover:shadow-xl"] [ 242 + El.h3 ~at:[classes_attr (tw [ 243 + Typography.(to_class (font_size `Lg)); 244 + Typography.(to_class (font_weight `Semibold)); 245 + Color.text (Color.make `Gray ~variant:`V800 ()); 246 + ]); At.class' "mb-2"] [El.txt "Scale + Shadow"]; 247 + El.p ~at:[classes_attr (tw [ 248 + Color.text (Color.make `Gray ~variant:`V600 ()); 249 + ])] [El.txt "This card both scales up and increases shadow on hover."]; 250 + ]; 251 + ]; 252 + ]; 253 + 254 + (* Border Effects *) 255 + El.section [ 256 + El.h2 ~at:[classes_attr (tw [ 257 + Typography.(to_class (font_size `Xl)); 258 + Typography.(to_class (font_weight `Semibold)); 259 + Color.text (Color.make `Gray ~variant:`V700 ()); 260 + ]); At.class' "mb-6"] [El.txt "Border Effects"]; 261 + 262 + El.div ~at:[classes_attr (tw [ 263 + Display.grid; 264 + Grid.(to_class (template_cols (`Cols 1))); 265 + Responsive.(to_class (at_breakpoint `Md (Grid.(to_class (template_cols (`Cols 3)))))); 266 + Spacing.(to_class (gap `All (Size.rem 1.5))); 267 + ])] [ 268 + (* Regular border *) 269 + El.div ~at:[classes_attr (tw [ 270 + Color.bg Color.white; 271 + Spacing.(to_class (p (Size.rem 1.5))); 272 + Effects.border; 273 + Color.border (Color.make `Gray ~variant:`V200 ()); 274 + Effects.rounded_lg; 275 + ]); At.class' "text-center"] [ 276 + El.h3 ~at:[classes_attr (tw [ 277 + Typography.(to_class (font_weight `Semibold)); 278 + Color.text (Color.make `Gray ~variant:`V700 ()); 279 + ]); At.class' "mb-2"] [El.txt "Regular Border"]; 280 + El.p ~at:[classes_attr (tw [ 281 + Typography.(to_class (font_size `Sm)); 282 + Color.text (Color.make `Gray ~variant:`V600 ()); 283 + ])] [El.txt "border border-gray-200"]; 284 + ]; 285 + 286 + (* Colored border *) 287 + El.div ~at:[classes_attr (tw [ 288 + Color.bg Color.white; 289 + Spacing.(to_class (p (Size.rem 1.5))); 290 + Effects.border; 291 + Color.border (Color.make `Blue ~variant:`V300 ()); 292 + Effects.rounded_lg; 293 + ]); At.class' "text-center"] [ 294 + El.h3 ~at:[classes_attr (tw [ 295 + Typography.(to_class (font_weight `Semibold)); 296 + Color.text (Color.make `Blue ~variant:`V600 ()); 297 + ]); At.class' "mb-2"] [El.txt "Colored Border"]; 298 + El.p ~at:[classes_attr (tw [ 299 + Typography.(to_class (font_size `Sm)); 300 + Color.text (Color.make `Gray ~variant:`V600 ()); 301 + ])] [El.txt "border border-blue-300"]; 302 + ]; 303 + 304 + (* Thick border *) 305 + El.div ~at:[At.class' "bg-white p-6 border-2 border-purple-300 rounded-lg text-center"] [ 306 + El.h3 ~at:[classes_attr (tw [ 307 + Typography.(to_class (font_weight `Semibold)); 308 + Color.text (Color.make `Purple ~variant:`V600 ()); 309 + ]); At.class' "mb-2"] [El.txt "Thick Border"]; 310 + El.p ~at:[classes_attr (tw [ 311 + Typography.(to_class (font_size `Sm)); 312 + Color.text (Color.make `Gray ~variant:`V600 ()); 313 + ])] [El.txt "border-2 border-purple-300"]; 314 + ]; 315 + ]; 316 + ]; 317 + ]; 318 + ]; 319 + ] in 320 + html_doc 321 + 322 + let () = 323 + (* Output HTML to stdout *) 324 + let html_doc = create_effects_demo () in 325 + let html_string = El.to_string ~doctype:true html_doc in 326 + print_string html_string
+142
examples/hello_tailwind_01.ml
··· 1 + (* Example 01: Hello Tailwind - Your First Tailwind OCaml Program *) 2 + 3 + open Htmlit 4 + open Tailwind 5 + 6 + let classes_attr tailwind_classes = 7 + At.class' (Tailwind.to_string tailwind_classes) 8 + 9 + let create_page () = 10 + let hello_classes = 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 = 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 = tw [ 27 + Layout.(to_class (max_width (Size.rem 42.0))); (* max-w-2xl is ~42rem *) 28 + Spacing.(to_class (mx `Auto)); 29 + Typography.(to_class (text_align `Center)); 30 + ] in 31 + 32 + let paragraph_classes = 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 = 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 = 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 = 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 = tw [ 61 + Color.text (Color.make `Blue ~variant:`V600 ()); 62 + ] in 63 + 64 + let section_classes = 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 = 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 = tw [ 76 + Display.flex; 77 + Flexbox.(to_class (align_items `Start)); 78 + ] in 79 + 80 + let checkmark_classes = tw [ 81 + Color.text (Color.make `Green ~variant:`V500 ()); 82 + Spacing.(to_class (mr (Size.rem 0.5))); 83 + ] in 84 + 85 + let html = El.html [ 86 + El.head [ 87 + El.meta ~at:[At.charset "utf-8"] (); 88 + El.meta ~at:[At.name "viewport"; At.content "width=device-width, initial-scale=1"] (); 89 + El.title [El.txt "Hello Tailwind"]; 90 + El.link ~at:[At.rel "stylesheet"; At.href "hello_tailwind_01.css"] (); 91 + ]; 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!" 96 + ]; 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." 100 + ]; 101 + El.div ~at:[classes_attr card_classes] [ 102 + El.h2 ~at:[classes_attr subheading_classes] [ 103 + El.txt "Generated Classes:" 104 + ]; 105 + El.pre ~at:[classes_attr code_block_classes] [ 106 + El.code ~at:[classes_attr code_classes] [ 107 + El.txt (to_string hello_classes) 108 + ]; 109 + ]; 110 + ]; 111 + El.div ~at:[classes_attr section_classes] [ 112 + El.h3 ~at:[classes_attr subheading_classes] [ 113 + El.txt "What you're learning:" 114 + ]; 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" 119 + ]; 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" 123 + ]; 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" 127 + ]; 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" 131 + ]; 132 + ]; 133 + ]; 134 + ]; 135 + ]; 136 + ] in 137 + html 138 + 139 + let () = 140 + (* Output HTML to stdout *) 141 + let html = create_page () in 142 + print_string (El.to_string ~doctype:true html)
+4
examples/input.css
··· 1 + /* Tailwind CSS configuration for examples */ 2 + @tailwind base; 3 + @tailwind components; 4 + @tailwind utilities;
+176
examples/patterns_and_components_06.ml
··· 1 + (* Example 06: Patterns and Components - Reusable Layout Patterns *) 2 + 3 + open Htmlit 4 + open Tailwind 5 + 6 + let classes_attr tailwind_classes = 7 + At.class' (Tailwind.to_string tailwind_classes) 8 + 9 + let create_patterns_demo () = 10 + (* Create comprehensive patterns demonstration *) 11 + let html_doc = El.html [ 12 + El.head [ 13 + El.meta ~at:[At.charset "utf-8"] (); 14 + El.meta ~at:[At.name "viewport"; At.content "width=device-width, initial-scale=1"] (); 15 + El.title [El.txt "Patterns and Components"]; 16 + El.link ~at:[At.rel "stylesheet"; At.href "patterns_and_components_06.css"] (); 17 + ]; 18 + El.body ~at:[At.class' "min-h-screen bg-gray-50 p-8"] [ 19 + El.div ~at:[At.class' "max-w-6xl mx-auto"] [ 20 + El.h1 ~at:[classes_attr (tw [ 21 + Typography.(to_class (font_size `Xl2)); 22 + Typography.(to_class (font_weight `Bold)); 23 + Color.text (Color.make `Gray ~variant:`V800 ()); 24 + ]); At.class' "mb-8 text-center"] [El.txt "Patterns and Components Demo"]; 25 + 26 + (* Container Pattern *) 27 + El.section ~at:[At.class' "mb-12"] [ 28 + El.h2 ~at:[classes_attr (tw [ 29 + Typography.(to_class (font_size `Xl)); 30 + Typography.(to_class (font_weight `Semibold)); 31 + Color.text (Color.make `Gray ~variant:`V700 ()); 32 + ]); At.class' "mb-6"] [El.txt "Container Pattern"]; 33 + 34 + El.div ~at:[classes_attr (tw [Patterns.container ()]); At.class' "bg-white rounded-lg shadow-sm p-6"] [ 35 + El.p ~at:[classes_attr (tw [ 36 + Color.text (Color.make `Gray ~variant:`V600 ()); 37 + ])] [El.txt "This content is inside a container pattern that centers content and provides responsive padding."]; 38 + ]; 39 + ]; 40 + 41 + (* Card Pattern *) 42 + El.section ~at:[At.class' "mb-12"] [ 43 + El.h2 ~at:[classes_attr (tw [ 44 + Typography.(to_class (font_size `Xl)); 45 + Typography.(to_class (font_weight `Semibold)); 46 + Color.text (Color.make `Gray ~variant:`V700 ()); 47 + ]); At.class' "mb-6"] [El.txt "Card Pattern"]; 48 + 49 + El.div ~at:[At.class' "grid grid-cols-1 md:grid-cols-3 gap-6"] [ 50 + El.div ~at:[classes_attr (tw [Patterns.card]); At.class' "p-6"] [ 51 + El.h3 ~at:[classes_attr (tw [ 52 + Typography.(to_class (font_size `Lg)); 53 + Typography.(to_class (font_weight `Semibold)); 54 + Color.text (Color.make `Gray ~variant:`V800 ()); 55 + ]); At.class' "mb-2"] [El.txt "Card One"]; 56 + El.p ~at:[classes_attr (tw [ 57 + Color.text (Color.make `Gray ~variant:`V600 ()); 58 + ])] [El.txt "This is a card using the built-in card pattern."]; 59 + ]; 60 + 61 + El.div ~at:[classes_attr (tw [Patterns.card]); At.class' "p-6"] [ 62 + El.h3 ~at:[classes_attr (tw [ 63 + Typography.(to_class (font_size `Lg)); 64 + Typography.(to_class (font_weight `Semibold)); 65 + Color.text (Color.make `Gray ~variant:`V800 ()); 66 + ]); At.class' "mb-2"] [El.txt "Card Two"]; 67 + El.p ~at:[classes_attr (tw [ 68 + Color.text (Color.make `Gray ~variant:`V600 ()); 69 + ])] [El.txt "Another card with the same styling pattern applied."]; 70 + ]; 71 + 72 + El.div ~at:[classes_attr (tw [Patterns.card]); At.class' "p-6"] [ 73 + El.h3 ~at:[classes_attr (tw [ 74 + Typography.(to_class (font_size `Lg)); 75 + Typography.(to_class (font_weight `Semibold)); 76 + Color.text (Color.make `Gray ~variant:`V800 ()); 77 + ]); At.class' "mb-2"] [El.txt "Card Three"]; 78 + El.p ~at:[classes_attr (tw [ 79 + Color.text (Color.make `Gray ~variant:`V600 ()); 80 + ])] [El.txt "A third card demonstrating consistent styling."]; 81 + ]; 82 + ]; 83 + ]; 84 + 85 + (* Flex Center Pattern *) 86 + El.section ~at:[At.class' "mb-12"] [ 87 + El.h2 ~at:[classes_attr (tw [ 88 + Typography.(to_class (font_size `Xl)); 89 + Typography.(to_class (font_weight `Semibold)); 90 + Color.text (Color.make `Gray ~variant:`V700 ()); 91 + ]); At.class' "mb-6"] [El.txt "Flex Center Pattern"]; 92 + 93 + El.div ~at:[classes_attr (tw [Patterns.flex_center]); At.class' "bg-blue-50 rounded-lg h-32"] [ 94 + El.p ~at:[classes_attr (tw [ 95 + Color.text (Color.make `Blue ~variant:`V600 ()); 96 + Typography.(to_class (font_weight `Medium)); 97 + ])] [El.txt "This content is perfectly centered using flex_center pattern"]; 98 + ]; 99 + ]; 100 + 101 + (* Stack Pattern *) 102 + El.section ~at:[At.class' "mb-12"] [ 103 + El.h2 ~at:[classes_attr (tw [ 104 + Typography.(to_class (font_size `Xl)); 105 + Typography.(to_class (font_weight `Semibold)); 106 + Color.text (Color.make `Gray ~variant:`V700 ()); 107 + ]); At.class' "mb-6"] [El.txt "Stack Pattern"]; 108 + 109 + El.div ~at:[classes_attr (tw [Patterns.stack ~gap:(Size.rem 1.0) ()]); At.class' "bg-white rounded-lg shadow-sm p-6"] [ 110 + El.div ~at:[classes_attr (tw [ 111 + Color.bg (Color.make `Green ~variant:`V50 ()); 112 + Spacing.(to_class (p (Size.rem 1.0))); 113 + Effects.rounded_md; 114 + ])] [El.txt "Stack Item 1"]; 115 + 116 + El.div ~at:[classes_attr (tw [ 117 + Color.bg (Color.make `Blue ~variant:`V50 ()); 118 + Spacing.(to_class (p (Size.rem 1.0))); 119 + Effects.rounded_md; 120 + ])] [El.txt "Stack Item 2"]; 121 + 122 + El.div ~at:[classes_attr (tw [ 123 + Color.bg (Color.make `Purple ~variant:`V50 ()); 124 + Spacing.(to_class (p (Size.rem 1.0))); 125 + Effects.rounded_md; 126 + ])] [El.txt "Stack Item 3"]; 127 + ]; 128 + ]; 129 + 130 + (* Inline Stack Pattern *) 131 + El.section [ 132 + El.h2 ~at:[classes_attr (tw [ 133 + Typography.(to_class (font_size `Xl)); 134 + Typography.(to_class (font_weight `Semibold)); 135 + Color.text (Color.make `Gray ~variant:`V700 ()); 136 + ]); At.class' "mb-6"] [El.txt "Inline Stack Pattern"]; 137 + 138 + El.div ~at:[classes_attr (tw [Patterns.inline_stack ~gap:(Size.rem 1.0) ()]); At.class' "bg-white rounded-lg shadow-sm p-6"] [ 139 + El.span ~at:[classes_attr (tw [ 140 + Color.bg (Color.make `Red ~variant:`V50 ()); 141 + Color.text (Color.make `Red ~variant:`V600 ()); 142 + Spacing.(to_class (px (Size.rem 0.75))); 143 + Spacing.(to_class (py (Size.rem 0.5))); 144 + Effects.rounded_full; 145 + Typography.(to_class (font_size `Sm)); 146 + ])] [El.txt "Tag 1"]; 147 + 148 + El.span ~at:[classes_attr (tw [ 149 + Color.bg (Color.make `Yellow ~variant:`V50 ()); 150 + Color.text (Color.make `Yellow ~variant:`V600 ()); 151 + Spacing.(to_class (px (Size.rem 0.75))); 152 + Spacing.(to_class (py (Size.rem 0.5))); 153 + Effects.rounded_full; 154 + Typography.(to_class (font_size `Sm)); 155 + ])] [El.txt "Tag 2"]; 156 + 157 + El.span ~at:[classes_attr (tw [ 158 + Color.bg (Color.make `Indigo ~variant:`V50 ()); 159 + Color.text (Color.make `Indigo ~variant:`V600 ()); 160 + Spacing.(to_class (px (Size.rem 0.75))); 161 + Spacing.(to_class (py (Size.rem 0.5))); 162 + Effects.rounded_full; 163 + Typography.(to_class (font_size `Sm)); 164 + ])] [El.txt "Tag 3"]; 165 + ]; 166 + ]; 167 + ]; 168 + ]; 169 + ] in 170 + html_doc 171 + 172 + let () = 173 + (* Output HTML to stdout *) 174 + let html_doc = create_patterns_demo () in 175 + let html_string = El.to_string ~doctype:true html_doc in 176 + print_string html_string
+80
lib/tailwind-html/cli.ml
··· 1 + (** Tailwind CLI integration for CSS processing *) 2 + 3 + open Unix 4 + 5 + type config = { 6 + input_css: string; (** Path to input CSS file with Tailwind directives *) 7 + output_css: string; (** Path to output CSS file *) 8 + content: string list; (** List of content paths to scan for classes *) 9 + minify: bool; (** Whether to minify the output *) 10 + } 11 + 12 + let default_config = { 13 + input_css = "input.css"; 14 + output_css = "output.css"; 15 + content = ["*.html"; "*.ml"]; 16 + minify = true; 17 + } 18 + 19 + (** Check if Tailwind CLI is available *) 20 + let check_tailwind_cli () = 21 + try 22 + let ic = open_process_in "npx @tailwindcss/cli --help 2>/dev/null" in 23 + let _ = input_line ic in 24 + let status = close_process_in ic in 25 + status = WEXITED 0 26 + with _ -> false 27 + 28 + (** Generate Tailwind CSS from input file *) 29 + let process_css config = 30 + if not (check_tailwind_cli ()) then 31 + failwith "Tailwind CLI not found. Install with: npm install -D @tailwindcss/cli" 32 + else 33 + let content_args = String.concat " " (List.map (fun p -> Printf.sprintf "--content '%s'" p) config.content) in 34 + let minify_flag = if config.minify then "--minify" else "" in 35 + let cmd = Printf.sprintf "npx @tailwindcss/cli -i %s -o %s %s %s 2>&1" 36 + config.input_css 37 + config.output_css 38 + content_args 39 + minify_flag 40 + in 41 + let ic = open_process_in cmd in 42 + let rec read_output acc = 43 + try 44 + let line = input_line ic in 45 + read_output (line :: acc) 46 + with End_of_file -> 47 + List.rev acc 48 + in 49 + let output = read_output [] in 50 + let status = close_process_in ic in 51 + match status with 52 + | WEXITED 0 -> Ok output 53 + | WEXITED n -> Error (Printf.sprintf "Tailwind CLI exited with code %d:\n%s" n (String.concat "\n" output)) 54 + | _ -> Error "Tailwind CLI terminated abnormally" 55 + 56 + (** Process CSS for a specific HTML file *) 57 + let process_for_html ~input_css ~html_file ~output_css ?(minify=true) () = 58 + let config = { 59 + input_css; 60 + output_css; 61 + content = [html_file]; 62 + minify; 63 + } in 64 + process_css config 65 + 66 + (** Write HTML to file and process CSS *) 67 + let write_and_process ~html_content ~html_file ~input_css ~output_css () = 68 + (* Write HTML to file *) 69 + let oc = open_out html_file in 70 + output_string oc html_content; 71 + close_out oc; 72 + 73 + (* Process CSS *) 74 + match process_for_html ~input_css ~html_file ~output_css () with 75 + | Ok _ -> 76 + Printf.eprintf "✅ Generated %s and %s\n" html_file output_css; 77 + Ok () 78 + | Error msg -> 79 + Printf.eprintf "❌ CSS processing failed: %s\n" msg; 80 + Error msg
+34
lib/tailwind-html/cli.mli
··· 1 + (** Tailwind CLI integration for CSS processing *) 2 + 3 + type config = { 4 + input_css: string; (** Path to input CSS file with Tailwind directives *) 5 + output_css: string; (** Path to output CSS file *) 6 + content: string list; (** List of content paths to scan for classes *) 7 + minify: bool; (** Whether to minify the output *) 8 + } 9 + 10 + val default_config : config 11 + 12 + (** Check if Tailwind CLI is available *) 13 + val check_tailwind_cli : unit -> bool 14 + 15 + (** Generate Tailwind CSS from input file *) 16 + val process_css : config -> (string list, string) result 17 + 18 + (** Process CSS for a specific HTML file *) 19 + val process_for_html : 20 + input_css:string -> 21 + html_file:string -> 22 + output_css:string -> 23 + ?minify:bool -> 24 + unit -> 25 + (string list, string) result 26 + 27 + (** Write HTML to file and process CSS *) 28 + val write_and_process : 29 + html_content:string -> 30 + html_file:string -> 31 + input_css:string -> 32 + output_css:string -> 33 + unit -> 34 + (unit, string) result