Tailwind classes in OCaml

Fix form module build issues by implementing helper and error text rendering

- Add proper rendering for helper_text and error_text fields in form inputs
- Implement styled helper text (gray) and error text (red) with appropriate spacing
- Wrap form elements with help text in container div when needed
- Resolve unused field warnings that were preventing compilation

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

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

+50 -2
+7
.agent/CLAUDE.md
··· 1 + My goal is to build a high quality professions OCaml library to generate 2 + Tailwind CSS components. Build a core `tailwind` library that serialises the 3 + CSS into strings. Then build a `tailwind-html` library that uses the Htmlit 4 + OCaml HTML generation library https://github.com/dbuenzli/htmlit for common 5 + components. 6 + 7 + You can find full Tailwind docs in tailwind-llms.txt in this directory.
+18
LICENSE.md
··· 1 + (* 2 + * ISC License 3 + * 4 + * Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org> 5 + * 6 + * Permission to use, copy, modify, and distribute this software for any 7 + * purpose with or without fee is hereby granted, provided that the above 8 + * copyright notice and this permission notice appear in all copies. 9 + * 10 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 + * 18 + *)
+25 -2
lib/tailwind-html/form.ml
··· 245 245 El.input ~at:(At.type' "checkbox" :: all_attrs) () 246 246 in 247 247 248 - (* Wrap in label if provided *) 249 - match field.label with 248 + let label_element = match field.label with 250 249 | Some label_text -> 251 250 El.label [ 252 251 El.txt label_text; 253 252 input_element; 254 253 ] 255 254 | None -> input_element 255 + in 256 + 257 + (* Add helper and error text if provided *) 258 + let help_elements = List.filter_map (fun x -> x) [ 259 + Option.map (fun text -> 260 + El.p ~at:[classes_attr (Tailwind.Css.tw [ 261 + Tailwind.Typography.(to_class (font_size `Sm)); 262 + Tailwind.Color.text (Tailwind.Color.make `Gray ~variant:`V600 ()); 263 + Tailwind.Spacing.(to_class (mt (Tailwind.Size.rem 0.25))); 264 + ])] [El.txt text] 265 + ) field.helper_text; 266 + Option.map (fun text -> 267 + El.p ~at:[classes_attr (Tailwind.Css.tw [ 268 + Tailwind.Typography.(to_class (font_size `Sm)); 269 + Tailwind.Color.text (Tailwind.Color.make `Red ~variant:`V600 ()); 270 + Tailwind.Spacing.(to_class (mt (Tailwind.Size.rem 0.25))); 271 + ])] [El.txt text] 272 + ) field.error_text; 273 + ] in 274 + 275 + (* Wrap everything in a container *) 276 + match help_elements with 277 + | [] -> label_element 278 + | _ -> El.div ([label_element] @ help_elements) 256 279 257 280 let group ?classes ~fields () = 258 281 let group_classes = Tailwind.Css.tw [
tailwind-llms.txt .agent/tailwind-llms.txt