From 4f775b32aeb0f3c7a2341d7c9a2e7dad850a5a17 Mon Sep 17 00:00:00 2001 From: Thomas Gazagnaire Date: Sun, 26 Jul 2026 00:26:46 -0700 Subject: [PATCH] sizing: accept a bare-number arbitrary aspect ratio aspect-[1.333] produced no rule: the arbitrary bracket only parsed an X/Y ratio through the quarter-multiple scale. Parse a bare positive number as a single-value aspect-ratio, which minifies to just the number. --- lib/sizing.ml | 18 +++++++++++++++--- test/test_sizing.ml | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/sizing.ml b/lib/sizing.ml index affe7997..05d40c0b 100644 --- a/lib/sizing.ml +++ b/lib/sizing.ml @@ -246,6 +246,7 @@ module Handler = struct | Aspect_video | Aspect_ratio of float * float (* aspect-4/3, aspect-8.5/11 *) | Aspect_bracket of float * float (* aspect-[10/9] *) + | Aspect_bracket_num of string (* aspect-[1.333] single number *) type Utility.base += Self of t @@ -896,6 +897,7 @@ module Handler = struct | Aspect_video -> aspect_video' | Aspect_ratio (w, h) -> aspect_ratio' w h | Aspect_bracket (w, h) -> aspect_ratio' w h + | Aspect_bracket_num s -> aspect_ratio' (float_of_string s) 1. let err_not_utility = Error (`Msg "Not a sizing utility") @@ -1289,9 +1291,16 @@ module Handler = struct | [ "aspect"; "auto" ] -> Ok Aspect_auto | [ "aspect"; "square" ] -> Ok Aspect_square | [ "aspect"; "video" ] -> Ok Aspect_video - | [ "aspect"; value ] when Parse.is_bracket_value value -> - parse_aspect_ratio (Parse.bracket_inner value) (fun w h -> - Aspect_bracket (w, h)) + | [ "aspect"; value ] when Parse.is_bracket_value value -> ( + let inner = Parse.bracket_inner value in + match parse_aspect_ratio inner (fun w h -> Aspect_bracket (w, h)) with + | Ok _ as ok -> ok + | Error _ -> ( + (* A bare number arbitrary ratio (aspect-[1.333]) is a single-value + aspect-ratio, which minifies to just the number. *) + match float_of_string_opt inner with + | Some f when f > 0. -> Ok (Aspect_bracket_num inner) + | _ -> err_not_utility)) | [ "aspect"; value ] -> parse_aspect_ratio value (fun w h -> Aspect_ratio (w, h)) | _ -> err_not_utility @@ -1598,6 +1607,8 @@ module Handler = struct aspect + int_of_float (rw *. 10.) + int_of_float rh | Aspect_bracket (rw, rh) -> aspect + 1000 + int_of_float (rw *. 10.) + int_of_float rh + | Aspect_bracket_num s -> + aspect + 1000 + int_of_float (float_of_string s *. 10.) + 1 | Aspect_auto -> aspect + 2000 | Aspect_square -> aspect + 2001 | Aspect_video -> aspect + 2002 @@ -1847,6 +1858,7 @@ module Handler = struct else string_of_float f in "aspect-[" ^ num w ^ "/" ^ num h ^ "]" + | Aspect_bracket_num s -> "aspect-[" ^ s ^ "]" end open Handler diff --git a/test/test_sizing.ml b/test/test_sizing.ml index 5375abbc..d569cbff 100644 --- a/test/test_sizing.ml +++ b/test/test_sizing.ml @@ -168,6 +168,21 @@ let test_aspect_css () = Alcotest.check bool "has 16/9" true (Astring.String.is_infix ~affix:"16/9" css) +(* A bare-number arbitrary ratio (aspect-[1.333]) is a single-value + aspect-ratio; it minifies to just the number, and round-trips. *) +let test_aspect_bracket_number () = + let css cls = + match Tw.of_string cls with + | Ok u -> Tw.to_css ~base:false [ u ] |> Tw.Css.to_string ~minify:true + | Error (`Msg m) -> Alcotest.failf "%s: %s" cls m + in + Alcotest.(check bool) + "aspect-[1.333] is aspect-ratio:1.333" true + (Astring.String.is_infix ~affix:"aspect-ratio:1.333" (css "aspect-[1.333]")); + Alcotest.(check string) + "aspect-[1.333] round-trips" "aspect-[1.333]" + (Tw.pp (Result.get_ok (Tw.of_string "aspect-[1.333]"))) + (* aspect-square inlines the 1/1 ratio in v4; it used to emit aspect-ratio: var(--aspect-square) with a stray --aspect-square theme token that bare Tailwind does not define. *) @@ -324,6 +339,7 @@ let tests = test_case "sizing of_string - invalid values" `Quick of_string_invalid; test_case "aspect classes" `Quick test_aspect_classes; test_case "aspect css" `Quick test_aspect_css; + test_case "aspect bracket number" `Quick test_aspect_bracket_number; test_case "aspect-square inlined 1/1" `Quick test_aspect_square_inlined; test_case "class generation" `Quick test_class_generation; test_case "sizing suborder matches Tailwind" `Quick