Tailwind classes in OCaml

Add index.html generator for examples showcase

- Create index_html_generator.ml that generates a beautiful index page
- Add comprehensive example descriptions and feature tags
- Link to all numbered tutorial examples (01-07) with descriptions
- Include responsive grid layout using type-safe Tailwind classes
- Add hover effects and proper styling for better user experience
- Integrate with dune build system to generate index.html automatically
- Add index.html to examples-html alias and install rules
- Reuse CSS from hello_tailwind_01.css to minimize dependencies

The index page provides:
- Progressive tutorial overview with clear descriptions
- Visual feature tags for each example
- Direct links to individual example pages
- Professional styling consistent with library design
- Mobile-responsive layout

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

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

Changed files
+201
examples
+15
examples/dune
···
(package tailwind)
(libraries tailwind tailwind-html htmlit unix))
+
;; HTML index generator
+
(executable
+
(public_name index_html_generator)
+
(name index_html_generator)
+
(package tailwind)
+
(libraries tailwind htmlit))
+
;; Generate HTML files from examples
(rule
···
(action
(run npx @tailwindcss/cli --input input.css --output %{targets} --content comprehensive_showcase_07.html --minify)))
+
;; Generate index.html page
+
(rule
+
(target index.html)
+
(deps (:exe index_html_generator.exe))
+
(action (with-stdout-to %{target} (run %{exe}))))
+
;; Alias to build all HTML files
(alias
(name examples-html)
(deps
+
index.html
hello_tailwind_01.html
colors_and_typography_02.html
layout_and_spacing_03.html
···
(section doc)
(package tailwind)
(files
+
(index.html as examples/index.html)
(hello_tailwind_01.html as examples/hello_tailwind_01.html)
(hello_tailwind_01.css as examples/hello_tailwind_01.css)
(colors_and_typography_02.html as examples/colors_and_typography_02.html)
+186
examples/index_html_generator.ml
···
+
(* Generate index.html page linking to all examples *)
+
+
open Htmlit
+
open Tailwind
+
+
let classes_attr tailwind_classes =
+
At.class' (Tailwind.to_string tailwind_classes)
+
+
let examples = [
+
("hello_tailwind_01.html", "01. Hello Tailwind",
+
"Your first Tailwind OCaml program. Learn the basics of creating and using type-safe Tailwind classes.",
+
["Basic concepts"; "Type safety"; "Class composition"]);
+
+
("colors_and_typography_02.html", "02. Colors and Typography",
+
"Explore the comprehensive color system and typography utilities with compile-time validation.",
+
["Color variants"; "Typography scale"; "Font weights"]);
+
+
("layout_and_spacing_03.html", "03. Layout and Spacing",
+
"Master the box model, flexbox layouts, and spacing utilities for building structured interfaces.",
+
["Box model"; "Flexbox"; "Spacing system"]);
+
+
("responsive_design_04.html", "04. Responsive Design",
+
"Learn responsive design patterns with breakpoints and mobile-first utilities.",
+
["Breakpoints"; "Mobile-first"; "Responsive utilities"]);
+
+
("effects_and_variants_05.html", "05. Effects and Variants",
+
"Add visual effects and interactive states with shadows, borders, and hover variants.",
+
["Visual effects"; "Interactive states"; "Hover variants"]);
+
+
("patterns_and_components_06.html", "06. Patterns and Components",
+
"Build reusable layout patterns and component compositions for consistent design.",
+
["Layout patterns"; "Component composition"; "Reusable utilities"]);
+
+
("comprehensive_showcase_07.html", "07. Comprehensive Showcase",
+
"Complete application demo showcasing all library features in a real-world context.",
+
["Complete application"; "All features"; "Best practices"]);
+
]
+
+
let create_index_page () =
+
let page_classes = tw [
+
Layout.(to_class (min_height Size.screen));
+
Color.bg (Color.make `Gray ~variant:`V50 ());
+
] in
+
+
let container_classes = tw [
+
Layout.(to_class (max_width (Size.rem 72.0))); (* max-w-6xl *)
+
Spacing.(to_class (mx `Auto));
+
Spacing.(to_class (p (Size.rem 2.0)));
+
] in
+
+
let header_classes = tw [
+
Typography.(to_class (font_size `Xl4));
+
Typography.(to_class (font_weight `Bold));
+
Color.text (Color.make `Gray ~variant:`V900 ());
+
Spacing.(to_class (mb (Size.rem 2.0)));
+
Typography.(to_class (text_align `Center));
+
] in
+
+
let subtitle_classes = tw [
+
Typography.(to_class (font_size `Xl));
+
Color.text (Color.make `Gray ~variant:`V600 ());
+
Typography.(to_class (text_align `Center));
+
Spacing.(to_class (mb (Size.rem 3.0)));
+
] in
+
+
let grid_classes = tw [
+
Display.grid;
+
Grid.(to_class (template_cols (`Cols 1)));
+
Responsive.(to_class (at_breakpoint `Md (Grid.(to_class (template_cols (`Cols 2))))));
+
Spacing.(to_class (gap `All (Size.rem 1.5)));
+
Spacing.(to_class (mb (Size.rem 3.0)));
+
] in
+
+
let card_classes = tw [
+
Color.bg Color.white;
+
Effects.rounded_lg;
+
Effects.shadow_sm;
+
Spacing.(to_class (p (Size.rem 1.5)));
+
Effects.border;
+
Color.border (Color.make `Gray ~variant:`V200 ());
+
Effects.transition `All;
+
Variants.hover Effects.shadow_md;
+
Variants.hover (Color.border (Color.make `Blue ~variant:`V300 ()));
+
] in
+
+
let card_title_classes = tw [
+
Typography.(to_class (font_size `Lg));
+
Typography.(to_class (font_weight `Semibold));
+
Color.text (Color.make `Gray ~variant:`V900 ());
+
Spacing.(to_class (mb (Size.rem 0.75)));
+
] in
+
+
let card_description_classes = tw [
+
Color.text (Color.make `Gray ~variant:`V600 ());
+
Spacing.(to_class (mb (Size.rem 1.0)));
+
Typography.(to_class (line_height `Relaxed));
+
] in
+
+
let features_classes = tw [
+
Display.flex;
+
Flexbox.(to_class (wrap `Wrap));
+
Spacing.(to_class (gap `All (Size.rem 0.5)));
+
Spacing.(to_class (mb (Size.rem 1.0)));
+
] in
+
+
let feature_tag_classes = tw [
+
Color.bg (Color.make `Blue ~variant:`V50 ());
+
Color.text (Color.make `Blue ~variant:`V700 ());
+
Typography.(to_class (font_size `Xs));
+
Spacing.(to_class (px (Size.rem 0.5)));
+
Spacing.(to_class (py (Size.rem 0.25)));
+
Effects.rounded_full;
+
] in
+
+
let link_classes = tw [
+
Color.text (Color.make `Blue ~variant:`V600 ());
+
Typography.(to_class (font_weight `Medium));
+
Variants.hover (Color.text (Color.make `Blue ~variant:`V800 ()));
+
Effects.transition `Colors;
+
] in
+
+
let footer_classes = tw [
+
Typography.(to_class (text_align `Center));
+
Spacing.(to_class (mt (Size.rem 3.0)));
+
Color.text (Color.make `Gray ~variant:`V500 ());
+
Typography.(to_class (font_size `Sm));
+
] in
+
+
let html_doc = El.html [
+
El.head [
+
El.meta ~at:[At.charset "utf-8"] ();
+
El.meta ~at:[At.name "viewport"; At.content "width=device-width, initial-scale=1"] ();
+
El.title [El.txt "Tailwind OCaml Examples"];
+
El.link ~at:[At.rel "stylesheet"; At.href "hello_tailwind_01.css"] (); (* Reuse CSS from first example *)
+
El.style [El.txt {|
+
body { font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; }
+
a { text-decoration: none; }
+
.example-card { display: block; }
+
|}];
+
];
+
El.body ~at:[classes_attr page_classes] [
+
El.div ~at:[classes_attr container_classes] [
+
El.h1 ~at:[classes_attr header_classes] [
+
El.txt "Tailwind OCaml Examples"
+
];
+
+
El.p ~at:[classes_attr subtitle_classes] [
+
El.txt "A progressive tutorial series demonstrating type-safe Tailwind CSS generation in OCaml"
+
];
+
+
El.div ~at:[classes_attr grid_classes] (
+
List.map (fun (href, title, description, features) ->
+
El.a ~at:[At.href href; At.class' "example-card"] [
+
El.div ~at:[classes_attr card_classes] [
+
El.h2 ~at:[classes_attr card_title_classes] [El.txt title];
+
El.p ~at:[classes_attr card_description_classes] [El.txt description];
+
El.div ~at:[classes_attr features_classes] (
+
List.map (fun feature ->
+
El.span ~at:[classes_attr feature_tag_classes] [El.txt feature]
+
) features
+
);
+
El.div ~at:[classes_attr link_classes] [
+
El.txt "View Example →"
+
];
+
];
+
]
+
) examples
+
);
+
+
El.div ~at:[classes_attr footer_classes] [
+
El.p [
+
El.txt "Built with ";
+
El.a ~at:[At.href "https://github.com/dbuenzli/htmlit"; classes_attr link_classes] [El.txt "Htmlit"];
+
El.txt " and type-safe Tailwind CSS generation"
+
];
+
];
+
];
+
];
+
] in
+
html_doc
+
+
let () =
+
(* Output HTML to stdout *)
+
let html_doc = create_index_page () in
+
let html_string = El.to_string ~doctype:true html_doc in
+
print_string html_string