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>

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