123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448(** Transform utilities for 2D and 3D transformations. *)moduleCss=Cascade.Css(** Error helpers *)leterr_not_utility=Error(`Msg"Not a transform utility")moduleHandler=structopenStyle(* Capture the project Pp before [open Css] shadows it with Css.Pp. *)letpp_float=Pp.floatopenCsstypet=|(* 2D Transforms *)Rotateofint|Rotate_arbitraryofstring*Css.angle|Rotate_rawofstring*string|Rotate_none|Rotate_3d_arbitraryofstring*float*float*float*Css.angle|Rotate_bare_varofstring|Neg_rotate_bare_varofstring|Neg_rotate_arbitraryofstring|Translate_xofint|Translate_x_full|Translate_x_px|Translate_x_stepoffloat|Translate_x_arbitraryofstring*Css.length|Translate_x_rawofstring*string|Translate_yofint|Translate_y_full|Translate_y_px|Translate_y_stepoffloat|Translate_y_arbitraryofstring*Css.length|Translate_y_rawofstring*string|Scaleofint|Scale_xofint|Scale_x_arbitraryofstring*[`Numoffloat|`Rawofstring]|Scale_yofint|Scale_y_arbitraryofstring*[`Numoffloat|`Rawofstring]|Scale_raw_1ofstring*float|Scale_raw_3ofstring*float*float*float|Scale_arbitrary_rawofstring*string|Skew_xofint|Skew_x_arbitraryofstring*[`AngleofCss.angle|`Rawofstring]|Skew_yofint|Skew_y_arbitraryofstring*[`AngleofCss.angle|`Rawofstring]|Skewofint|Skew_arbitraryofstring*[`AngleofCss.angle|`Rawofstring]|Neg_skew_arbitraryofstring|Neg_skew_x_arbitraryofstring|Neg_skew_y_arbitraryofstring|Translate_x_fractionofint*int|Translate_y_fractionofint*int|(* Combined translate utilities *)Translateofint|Translate_full|Translate_none|Translate_px|Translate_1_2|Translate_fractionofint*int|Translate_arbitraryofstring*Css.length|Translate_rawofstring*string|(* Negative translate utilities *)Neg_translateofint|Neg_translate_arbitraryofstring|Neg_translate_full|Neg_translate_px|Neg_translate_fractionofint*int|Neg_translate_x_arbitraryofstring|Neg_translate_x_full|Neg_translate_x_px|Neg_translate_x_1_2|Neg_translate_x_fractionofint*int|Neg_translate_y_arbitraryofstring|Neg_translate_y_full|Neg_translate_y_px|Neg_translate_y_1_2|Neg_translate_y_fractionofint*int|(* 3D Transforms *)Translate_zofint|Translate_z_stepoffloat(* translate-z-0.5, a half-step scale value *)|Translate_z_px|Translate_z_arbitraryofstring*[`LengthofCss.length|`Rawofstring]|Neg_translate_z_arbitraryofstring|Neg_translate_z_px|Translate_3d|Rotate_xofint|Rotate_x_arbitraryofstring*Css.angle|Rotate_x_rawofstring*string|Rotate_x_bare_varofstring|Neg_rotate_x_bare_varofstring|Neg_rotate_x_arbitraryofstring|Rotate_yofint|Rotate_y_arbitraryofstring*Css.angle|Rotate_y_rawofstring*string|Rotate_y_bare_varofstring|Neg_rotate_y_bare_varofstring|Neg_rotate_y_arbitraryofstring|Rotate_zofint|Rotate_z_arbitraryofstring*Css.angle|Rotate_z_rawofstring*string|Rotate_z_bare_varofstring|Neg_rotate_z_bare_varofstring|Neg_rotate_z_arbitraryofstring|Scale_zofint|Scale_z_arbitraryofstring|(* The negative bracket forms hold the bracket as written; the style reads
it, as the negative translate forms do. *)Neg_scale_arbitraryofstring|Neg_scale_x_arbitraryofstring|Neg_scale_y_arbitraryofstring|Neg_scale_z_arbitraryofstring|Scale_3d|Scale_none|Perspective_none|(* depth named by a project [--perspective-*] token *)Perspective_themeofstring|Perspective_dramatic|Perspective_near|Perspective_normal|Perspective_midrange|Perspective_distant|Perspective_arbitraryofstring*Css.length|Perspective_rawofstring*string|(* Perspective origin *)Perspective_origin_center|Perspective_origin_top|Perspective_origin_bottom|Perspective_origin_left|Perspective_origin_right|Perspective_origin_top_left|Perspective_origin_top_right|Perspective_origin_bottom_left|Perspective_origin_bottom_right(* The author's bracket text travels with the value it denotes, so the class
name is spelled exactly as it was written. *)|Perspective_origin_arbitraryofstring*Css.perspective_origin|Perspective_origin_rawofstring*string|(* Transform style *)Transform_style_3d|Transform_style_flat|(* Transform box *)Transform_box_border|Transform_box_content|Transform_box_fill|Transform_box_stroke|Transform_box_view|(* Backface visibility *)Backface_visible|Backface_hidden|(* Transform control *)Transform|Transform_cpu|Transform_none|Transform_gpu|Transform_arbitraryofstring*Css.transformlist|Transform_rawofstring*string(* A bracket no reader took, on any of these families: the class names one
longhand or one channel, so the value goes there verbatim - Tailwind's
token-stream contract. *)|(* Transform origin *)Origin_center|Origin_top|Origin_bottom|Origin_left|Origin_right|Origin_top_left|Origin_top_right|Origin_bottom_left|Origin_bottom_right|Origin_arbitraryofstring*Css.transform_origin|Origin_rawofstring*string(** Priority for transform utilities *)letname="transforms"(* The transform chain sits before animations. Controls that Tailwind sorts
through a later property band must not travel with it: origin precedes the
chain, backface follows selection, perspective follows backface, and
transform box/style follow text-shadow. *)letpriority=function|Origin_center|Origin_top|Origin_bottom|Origin_left|Origin_right|Origin_top_left|Origin_top_right|Origin_bottom_left|Origin_bottom_right|Origin_arbitrary_|Origin_raw_->8|Backface_visible|Backface_hidden->38|Perspective_none|Perspective_theme_|Perspective_dramatic|Perspective_near|Perspective_normal|Perspective_midrange|Perspective_distant|Perspective_arbitrary_|Perspective_raw_|Perspective_origin_center|Perspective_origin_top|Perspective_origin_bottom|Perspective_origin_left|Perspective_origin_right|Perspective_origin_top_left|Perspective_origin_top_right|Perspective_origin_bottom_left|Perspective_origin_bottom_right|Perspective_origin_arbitrary_|Perspective_origin_raw_->40|Transform_style_3d|Transform_style_flat|Transform_box_border|Transform_box_content|Transform_box_fill|Transform_box_stroke|Transform_box_view->42|_->9(* Tailwind v4 uses rotate-x/y/z and skew-x/y variables for the transform
utility. These variables contain the full transform function values, e.g.:
--tw-rotate-x: rotateX(45deg) --tw-skew-x: skewX(10deg) *)(* Property ordering to match Tailwind v4:
translate (0-2), scale (3-5), then other properties follow *)(* Translate variables - use property_default pattern with initial 0 *)(* Translate properties don't appear in @layer properties in standard Tailwind - only used locally *)lettw_translate_x_var=Var.property_defaultCss.Length~initial:Css.Zero~universal:true~property_order:0~family:`Translate"tw-translate-x"lettw_translate_y_var=Var.property_defaultCss.Length~initial:Css.Zero~universal:true~property_order:1~family:`Translate"tw-translate-y"lettw_translate_z_var=Var.property_defaultCss.Length~initial:Css.Zero~universal:true~property_order:2~family:`Translate"tw-translate-z"(* Scale variables - property_order (6-8) comes after duration (5) *)lettw_scale_x_var=Var.property_defaultCss.Number_percentage~initial:(Num1.0)~universal:true~property_order:6~family:`Scale"tw-scale-x"lettw_scale_y_var=Var.property_defaultCss.Number_percentage~initial:(Num1.0)~universal:true~property_order:7~family:`Scale"tw-scale-y"lettw_scale_z_var=Var.property_defaultCss.Number_percentage~initial:(Num1.0)~universal:true~property_order:8~family:`Scale"tw-scale-z"(* Rotate and skew properties appear FIRST in @layer properties (0-4) *)lettw_rotate_x_var=Var.channel~needs_property:true~property_order:0~family:`RotateCss.Transform"tw-rotate-x"lettw_rotate_y_var=Var.channel~needs_property:true~property_order:1~family:`RotateCss.Transform"tw-rotate-y"lettw_rotate_z_var=Var.channel~needs_property:true~property_order:2~family:`RotateCss.Transform"tw-rotate-z"lettw_skew_x_var=Var.channel~needs_property:true~property_order:3~family:`SkewCss.Transform"tw-skew-x"lettw_skew_y_var=Var.channel~needs_property:true~property_order:4~family:`SkewCss.Transform"tw-skew-y"(* Perspective theme variables, ordered by value to match Tailwind's theme.
The family owns slots (8, 10-16), after blur (8, 0-8). *)letperspective_dramatic_var=Var.themeCss.Length"perspective-dramatic"~order:(8,10)letperspective_near_var=Var.themeCss.Length"perspective-near"~order:(8,11)letperspective_normal_var=Var.themeCss.Length"perspective-normal"~order:(8,12)letperspective_midrange_var=Var.themeCss.Length"perspective-midrange"~order:(8,13)letperspective_distant_var=Var.themeCss.Length"perspective-distant"~order:(8,14)(* A [--perspective-*] token the project declared names a depth the built-in
scale has no slot for; they share the slot after the scale. *)letperspective_named_cache=Domain_cache.v8letperspective_named_varname=Domain_cache.or_addperspective_named_cachename(fun()->Var.themeCss.Length("perspective-"^name)~order:(8,16))letperspective_none_var=Var.themeCss.Length"perspective-none"~order:(8,15)(* Publish the scale through the theme-token registry, the way rule.ml
publishes the breakpoints, so [perspective-[theme(--perspective-normal)]]
resolves and [theme(static)] emits it. [--perspective-none] is not in
Tailwind's default theme - the utility writes [perspective: none] - so it
stays out. *)let()=List.iter(fun(var,px)->Theme.register_defaultvar(Css.Pxpx:Css.length))[(perspective_dramatic_var,100.);(perspective_near_var,300.);(perspective_normal_var,500.);(perspective_midrange_var,800.);(perspective_distant_var,1200.);](** {1 Helpers} *)letcollect_property_rulesvars=List.filter_mapVar.property_rulevars|>concat(* Property rules for rotate/skew transform chain *)letrotate_skew_props=collect_property_rules[tw_rotate_x_var;tw_rotate_y_var;tw_rotate_z_var;tw_skew_x_var;tw_skew_y_var;](* Helper to create transform with variable chain: Sets one variable and
outputs transform with all rotate/skew vars *)lettransform_with_varvartransform_val=letd=Var.setvartransform_valinletrotate_x_ref=Var.reference_with_empty_fallbacktw_rotate_x_varinletrotate_y_ref=Var.reference_with_empty_fallbacktw_rotate_y_varinletrotate_z_ref=Var.reference_with_empty_fallbacktw_rotate_z_varinletskew_x_ref=Var.reference_with_empty_fallbacktw_skew_x_varinletskew_y_ref=Var.reference_with_empty_fallbacktw_skew_y_varinstyle~property_rules:rotate_skew_props[d;transforms[Varrotate_x_ref;Varrotate_y_ref;Varrotate_z_ref;Varskew_x_ref;Varskew_y_ref;];](* Helper to create an angle with calc for negative values *)letmake_angledeg=ifdeg>=0thenDeg(float_of_intdeg)elseCalc(Expr(Val(Deg(float_of_int(absdeg))),Mul,Num(-1.)))(* Helper to create a percentage with calc for negative values *)letmake_pctn:Css.number_percentage=ifn>=0thenPct(float_of_intn)elseCalc(Expr(Val(Pct(float_of_int(absn))),Mul,Num(-1.)))lettransform_with_both_skew_angleangle=letskew_x_decl=Var.settw_skew_x_var(Skew_xangle)inletskew_y_decl=Var.settw_skew_y_var(Skew_yangle)inletrotate_x_ref=Var.reference_with_empty_fallbacktw_rotate_x_varinletrotate_y_ref=Var.reference_with_empty_fallbacktw_rotate_y_varinletrotate_z_ref=Var.reference_with_empty_fallbacktw_rotate_z_varinletskew_x_ref=Var.reference_with_empty_fallbacktw_skew_x_varinletskew_y_ref=Var.reference_with_empty_fallbacktw_skew_y_varinstyle~property_rules:rotate_skew_props[skew_x_decl;skew_y_decl;transforms[Varrotate_x_ref;Varrotate_y_ref;Varrotate_z_ref;Varskew_x_ref;Varskew_y_ref;];]lettransform_with_both_skewdeg=letangle=make_angledeginletskew_x_decl=Var.settw_skew_x_var(Skew_xangle)inletskew_y_decl=Var.settw_skew_y_var(Skew_yangle)inletrotate_x_ref=Var.reference_with_empty_fallbacktw_rotate_x_varinletrotate_y_ref=Var.reference_with_empty_fallbacktw_rotate_y_varinletrotate_z_ref=Var.reference_with_empty_fallbacktw_rotate_z_varinletskew_x_ref=Var.reference_with_empty_fallbacktw_skew_x_varinletskew_y_ref=Var.reference_with_empty_fallbacktw_skew_y_varinstyle~property_rules:rotate_skew_props[skew_x_decl;skew_y_decl;transforms[Varrotate_x_ref;Varrotate_y_ref;Varrotate_z_ref;Varskew_x_ref;Varskew_y_ref;];]letneg_classnamen=letprefix=ifn<0then"-"else""inprefix^name^string_of_int(absn)letparse_bracket_lengths:(Css.length,_)result=ifParse.is_bracket_valuesthen(letinner=Parse.bracket_innersinletslen=String.lengthinnerin(* Allow a leading minus so negative arbitrary values
(translate-x-[-0.5px], translate-y-[-110%]) parse. *)leti=ref(ifslen>0&&inner.[0]='-'then1else0)inwhile!i<slen&&((inner.[!i]>='0'&&inner.[!i]<='9')||inner.[!i]='.')doincridone;if!i=0||!i=slenthenError(`Msg("Invalid length value: "^inner))elseletnum_s=String.subinner0!iinletunit_s=String.subinner!i(slen-!i)inmatchFloat.of_string_optnum_swith|Option.None->Error(`Msg("Invalid length value: "^inner))|Option.Somen->(matchunit_swith|"px"->Ok(Pxn:Css.length)|"rem"->Ok(Remn)|"em"->Ok(Emn)|"vw"->Ok(Vwn)|"vh"->Ok(Vhn)|"cm"->Ok(Cmn)|"mm"->Ok(Mmn)|"pt"->Ok(Ptn)|"%"->Ok(Pctn)|_->Error(`Msg("Invalid length unit: "^unit_s))))elseError(`Msg("Not a bracket value: "^s))(* The angle a bracket spells: a signed decimal under one of the four angle
units. *)letangle_of_innerinner:Css.angleoption=letslen=String.lengthinnerinleti=ref0inwhile!i<slen&&((inner.[!i]>='0'&&inner.[!i]<='9')||inner.[!i]='.'||inner.[!i]='-')doincridone;if!i=0||!i=slenthenNoneelseletnum_s=String.subinner0!iinletunit_s=String.subinner!i(slen-!i)inmatch(Float.of_string_optnum_s,unit_s)with|Somen,"deg"->Some(Degn)|Somen,"rad"->Some(Radn)|Somen,"turn"->Some(Turnn)|Somen,"grad"->Some(Gradn)|_->None(* The bracket names the class, so hand its text back beside the angle. *)letparse_bracket_angles:(string*Css.angle,_)result=ifParse.is_bracket_valuesthenletinner=Parse.bracket_innersinmatchangle_of_innerinnerwith|Somea->Ok(inner,a)|None->Error(`Msg("Invalid angle value: "^inner))elseError(`Msg("Not a bracket value: "^s))(* A bracket a negative translate takes: one holding a value, which every
spelling of one becomes [calc(<value> * -1)]. *)letneg_bracketvalue=Parse.is_bracket_valuevalue&&Option.is_some(Parse.arbitrary_declaration_value(Parse.bracket_innervalue))(* The number a bracket spells, when it is a plain decimal. A spelling only
OCaml's number reader takes ([0x4]) is not a number under another name. *)letnumber_of_innerv=Parse.decimal_float(Parse.decode_arbitrary_valuev)(* The bracket beside its reading: a plain decimal is the number it spells,
and anything else goes to the custom property as written. *)letparse_bracket_numbers=ifParse.is_bracket_valuesthenletinner=Parse.bracket_innersinmatchnumber_of_innerinnerwith|Somef->Ok(inner,`Numf)|None->(matchParse.arbitrary_declaration_valueinnerwith|Somev->Ok(inner,`Rawv)|None->Error(`Msg("Invalid number: "^inner)))elseError(`Msg("Not a bracket value: "^s))(** {1 2D Transform Utilities} *)letrotaten=style[Css.rotate(Angle(Deg(float_of_intn)))]letrotate_arbitraryangle=style[Css.rotate(Angleangle)]letopaque_stylepropertyvalue=matchParse.opaque_declarationpropertyvaluewith|Somedeclaration->style[declaration]|None->style[]letrotate_3d_arbitraryxyzangle=style[Css.rotate(Axis(x,y,z,angle))]letrotate_bare_varname=(* name is "--var", strip the -- prefix *)letbare=String.subname2(String.lengthname-2)inletref:Css.angleCss.var=Var.bracketbareinstyle[Css.rotate(Angle(Varref))]letneg_rotate_bare_varname=(* name is "--var", strip the -- prefix for Calc.var *)letbare=String.subname2(String.lengthname-2)inletneg_angle:Css.angle=Calc(Expr(Var(Var.bracketbare),Mul,Num(-1.)))instyle[Css.rotate(Angleneg_angle)]lettranslate_props=collect_property_rules[tw_translate_x_var;tw_translate_y_var;tw_translate_z_var]lettranslate_xy_refs=lettx_ref=Var.referencetw_translate_x_varinletty_ref=Var.referencetw_translate_y_varinCss.translate(XY(Vartx_ref,Varty_ref))lettranslate_xyz_refs=lettx_ref=Var.referencetw_translate_x_varinletty_ref=Var.referencetw_translate_y_varinlettz_ref=Var.referencetw_translate_z_varinCss.translate(XYZ(Vartx_ref,Varty_ref,Vartz_ref))(* The spacing step comes from [Theme], as every spacing family's does, so a
project's [--spacing-N] token and an inline [--spacing] reach a translate
the way they reach a padding. [calc(var(--spacing) * 0)] is zero whatever
the spacing is, and Tailwind writes that zero with a unit: the target is a
[--tw-*] custom property, an opaque token stream where [0] and [0px] are
not the same token, which is the [0px] [Theme] writes. *)lettranslate_axis?themeaxis_varn=letspacing_decl,spacing_value=Theme.spacing_calc?themeninletaxis_decl=Var.setaxis_varspacing_valueinstyle~property_rules:translate_props(spacing_decl::axis_decl::[translate_xy_refs])lettranslate_x?themen=translate_axis?themetw_translate_x_varnlettranslate_y?themen=translate_axis?themetw_translate_y_varn(* A fractional spacing step ([translate-x-0.5]), signed so the negative form
goes through the same path. *)lettranslate_axis_step?themeaxis_varf=letspacing_decl,spacing_value=Theme.spacing_calc_float?themefinletaxis_decl=Var.setaxis_varspacing_valueinstyle~property_rules:translate_props(spacing_decl::axis_decl::[translate_xy_refs])lettranslate_x_step?themef=translate_axis_step?themetw_translate_x_varflettranslate_y_step?themef=translate_axis_step?themetw_translate_y_varf(** Helper to create a fraction percentage value: calc(n/d * 100%) *)letmake_fraction_pctnumdenom:Css.length=Parse.fraction_lengthnumdenom(** Helper to create a negated fraction percentage value: calc(calc(n/d *
100%) * -1) *)letmake_neg_fraction_pctnumdenom:Css.length=Parse.neg_fraction_lengthnumdenomlettranslate_x_fractionnumdenom=letaxis_decl=Var.settw_translate_x_var(make_fraction_pctnumdenom)instyle~property_rules:translate_props(axis_decl::[translate_xy_refs])lettranslate_y_fractionnumdenom=letaxis_decl=Var.settw_translate_y_var(make_fraction_pctnumdenom)instyle~property_rules:translate_props(axis_decl::[translate_xy_refs])lettranslate_x_full=letaxis_decl=Var.settw_translate_x_var(Pct100.0)instyle~property_rules:translate_props(axis_decl::[translate_xy_refs])lettranslate_x_px=letaxis_decl=Var.settw_translate_x_var(Px1.0)instyle~property_rules:translate_props(axis_decl::[translate_xy_refs])lettranslate_x_arbitrarylen=letaxis_decl=Var.settw_translate_x_varleninstyle~property_rules:translate_props(axis_decl::[translate_xy_refs])lettranslate_y_full=letaxis_decl=Var.settw_translate_y_var(Pct100.0)instyle~property_rules:translate_props(axis_decl::[translate_xy_refs])lettranslate_y_px=letaxis_decl=Var.settw_translate_y_var(Px1.0)instyle~property_rules:translate_props(axis_decl::[translate_xy_refs])lettranslate_y_arbitrarylen=letaxis_decl=Var.settw_translate_y_varleninstyle~property_rules:translate_props(axis_decl::[translate_xy_refs])(* The bracket [s] negated the way Tailwind writes a negative candidate: the
value [read] takes, with any data-type hint taken off, comes back [`Typed]
for the family to negate as its type, and every other value comes back
[`Raw], the token stream [calc(<value> * -1)] between [before] and [after],
which is the function call a channel holds. [None] is a bracket that names
no value, which the reader refused. *)letneg_bracket_value?(before="")?(after="")reads=matchOption.bind(Parse.value_after_hints)readwith|Somev->Some(`Typedv)|None->Option.bind(Parse.arbitrary_declaration_values)(funv->Option.map(funraw->`Rawraw)(Parse.wrap_declaration_value~before:(before^"calc(")~after:(" * -1)"^after)v))(* [var] set to the bracket [s] negated: a value [read] takes goes through
[neg], the family's own negation, and a token stream is written as it
is. *)letneg_channel_decl?before?afterreadnegvars=matchneg_bracket_value?before?afterreadswith|Some(`Typedv)->Some(Var.setvar(negv))|Some(`Rawv)->Some(Css.custom_property~layer:"utilities"(Var.css_namevar)v)|None->Noneletneg_length(l:Css.length):Css.length=Calc(Calc.mul(Calc.lengthl)(Calc.float(-1.)))letneg_numberf:Css.number_percentage=Calc(Calc.mul(Calc.floatf)(Calc.float(-1.)))letneg_angle(a:Css.angle):Css.angle=Calc(Calc.mul(Vala)(Calc.float(-1.)))(* A translate channel: a length is negated as a length. *)letneg_arbitrary_declvars=neg_channel_declParse.arbitrary_lengthneg_lengthvars(* A negative bracket translate on [vars], the axes it sets. *)letneg_translate_arbitrary_onvarss=matchList.map(funvar->neg_arbitrary_declvars)varswith|declswhenList.for_allOption.is_somedecls->letdecls=List.filter_mapFun.iddeclsinletrefs=ifList.memqtw_translate_z_varvarsthentranslate_xyz_refselsetranslate_xy_refsinstyle~property_rules:translate_props~metadata:(List.mapVar.metadatavars)(decls@[refs])|_->style[]letneg_translate_x_arbitrary_styles=neg_translate_arbitrary_on[tw_translate_x_var]sletneg_translate_y_arbitrary_styles=neg_translate_arbitrary_on[tw_translate_y_var]sletneg_translate_x_full=letaxis_decl=Var.settw_translate_x_var(Pct(-100.0))instyle~property_rules:translate_props(axis_decl::[translate_xy_refs])letneg_translate_y_full=letaxis_decl=Var.settw_translate_y_var(Pct(-100.0))instyle~property_rules:translate_props(axis_decl::[translate_xy_refs])letscalen=letvalue=make_pctninletdx=Var.settw_scale_x_varvalueinletdy=Var.settw_scale_y_varvalueinletdz=Var.settw_scale_z_varvalueinletprops=collect_property_rules[tw_scale_x_var;tw_scale_y_var;tw_scale_z_var]in(* Also set the scale property with variable references *)letscale_x_ref=Var.referencetw_scale_x_varinletscale_y_ref=Var.referencetw_scale_y_varinstyle~property_rules:props(dx::dy::dz::[Css.scale(XY(Varscale_x_ref,Varscale_y_ref))])letscale_xn=letvalue=make_pctninletd=Var.settw_scale_x_varvalueinletprops=collect_property_rules[tw_scale_x_var;tw_scale_y_var;tw_scale_z_var]inletscale_x_ref=Var.referencetw_scale_x_varinletscale_y_ref=Var.referencetw_scale_y_varinstyle~property_rules:props(d::[Css.scale(XY(Varscale_x_ref,Varscale_y_ref))])letscale_yn=letvalue=make_pctninletd=Var.settw_scale_y_varvalueinletprops=collect_property_rules[tw_scale_x_var;tw_scale_y_var;tw_scale_z_var]inletscale_x_ref=Var.referencetw_scale_x_varinletscale_y_ref=Var.referencetw_scale_y_varinstyle~property_rules:props(d::[Css.scale(XY(Varscale_x_ref,Varscale_y_ref))])(* One axis of the scale, from either the number the bracket denotes or, when
it denotes none, the author's text: Tailwind hands a bracket to the custom
property unvalidated, so [scale-x-[0x4]] sets [0x4] and not the [4] an
OCaml number reader would fold it to. *)letscale_axis_arbitraryvarv=letbinding=matchvwith|`Numf->Var.setvar(Css.Numf:Css.number_percentage)|`Rawvalue->Css.custom_property~layer:"utilities"(Var.css_namevar)valueinletprops=collect_property_rules[tw_scale_x_var;tw_scale_y_var;tw_scale_z_var]inletscale_x_ref=Var.referencetw_scale_x_varinletscale_y_ref=Var.referencetw_scale_y_varinstyle~property_rules:props[binding;Css.scale(XY(Varscale_x_ref,Varscale_y_ref))]letscale_x_arbitraryv=scale_axis_arbitrarytw_scale_x_varvletscale_y_arbitraryv=scale_axis_arbitrarytw_scale_y_varvletscale_props=collect_property_rules[tw_scale_x_var;tw_scale_y_var;tw_scale_z_var]letscale_xy_refs=letx=Var.referencetw_scale_x_varinlety=Var.referencetw_scale_y_varinCss.scale(XY(Varx,Vary))letscale_xyz_refs=letx=Var.referencetw_scale_x_varinlety=Var.referencetw_scale_y_varinletz=Var.referencetw_scale_z_varinCss.scale(XYZ(Varx,Vary,Varz))(* A negative bracket scale on [var], the axis it sets, beside [refs], the
composition the positive form writes: a number is negated as a number. *)letneg_scale_axis_arbitraryvarrefss=matchneg_channel_declnumber_of_innerneg_numbervarswith|Somedecl->style~property_rules:scale_props~metadata:[Var.metadatavar][decl;refs]|None->style[]letneg_scale_x_arbitrary=neg_scale_axis_arbitrarytw_scale_x_varscale_xy_refsletneg_scale_y_arbitrary=neg_scale_axis_arbitrarytw_scale_y_varscale_xy_refsletneg_scale_z_arbitrary=neg_scale_axis_arbitrarytw_scale_z_varscale_xyz_refs(* The bare form writes [scale] itself, as the positive one does. *)letneg_scale_arbitrarys=matchneg_bracket_valuenumber_of_innerswith|Some(`Typedf)->style[Css.scale(X(neg_numberf))]|Some(`Rawv)->opaque_style"scale"v|None->style[]letskew_xdeg=transform_with_vartw_skew_x_var(Skew_x(make_angledeg))letskew_ydeg=transform_with_vartw_skew_y_var(Skew_y(make_angledeg))letskew_x_arbitraryangle=transform_with_vartw_skew_x_var(Skew_xangle)letskew_y_arbitraryangle=transform_with_vartw_skew_y_var(Skew_yangle)(* The transform every rotate and skew channel utility writes, reading the
five channels with an empty fallback each. *)letrotate_skew_chain=letrotate_x_ref=Var.reference_with_empty_fallbacktw_rotate_x_varinletrotate_y_ref=Var.reference_with_empty_fallbacktw_rotate_y_varinletrotate_z_ref=Var.reference_with_empty_fallbacktw_rotate_z_varinletskew_x_ref=Var.reference_with_empty_fallbacktw_skew_x_varinletskew_y_ref=Var.reference_with_empty_fallbacktw_skew_y_varintransforms[Varrotate_x_ref;Varrotate_y_ref;Varrotate_z_ref;Varskew_x_ref;Varskew_y_ref;](* A negative bracket on [axes], each a rotate or skew channel with the
function it holds and the transform that function is: the channel takes the
function around the bracket negated, an angle as an angle and a token
stream as [calc(<value> * -1)], beside the chain. *)letneg_chain_arbitrary_onaxess=letdecl(var,fn,transform)=neg_channel_decl~before:(fn^"(")~after:")"angle_of_inner(funa->transform(neg_anglea))varsinmatchList.mapdeclaxeswith|declswhenList.for_allOption.is_somedecls->style~property_rules:rotate_skew_props~metadata:(List.map(fun(var,_,_)->Var.metadatavar)axes)(List.filter_mapFun.iddecls@[rotate_skew_chain])|_->style[]letskew_x_axis=(tw_skew_x_var,"skewX",funa:Css.transform->Skew_xa)letskew_y_axis=(tw_skew_y_var,"skewY",funa:Css.transform->Skew_ya)letneg_skew_x_arbitrary=neg_chain_arbitrary_on[skew_x_axis]letneg_skew_y_arbitrary=neg_chain_arbitrary_on[skew_y_axis]letneg_skew_arbitrary=neg_chain_arbitrary_on[skew_x_axis;skew_y_axis]letrotate_x_axis=(tw_rotate_x_var,"rotateX",funa:Css.transform->Rotate_xa)letrotate_y_axis=(tw_rotate_y_var,"rotateY",funa:Css.transform->Rotate_ya)letrotate_z_axis=(tw_rotate_z_var,"rotateZ",funa:Css.transform->Rotate_za)letneg_rotate_x_arbitrary=neg_chain_arbitrary_on[rotate_x_axis]letneg_rotate_y_arbitrary=neg_chain_arbitrary_on[rotate_y_axis]letneg_rotate_z_arbitrary=neg_chain_arbitrary_on[rotate_z_axis](* The bare form writes [rotate] itself, as the positive one does. *)letneg_rotate_arbitrarys=matchneg_bracket_valueangle_of_innerswith|Some(`Typeda)->style[Css.rotate(Angle(neg_anglea))]|Some(`Rawv)->opaque_style"rotate"v|None->style[]letraw_skew_styleaxesvalue=letrawvarfn=Parse.wrap_declaration_value~before:(fn^"(")~after:")"value|>Option.map(Css.custom_property~layer:"utilities"(Var.css_namevar))inletbindings,metadata=matchaxeswith|`X->(Option.to_list(rawtw_skew_x_var"skewX"),[Var.metadatatw_skew_x_var])|`Y->(Option.to_list(rawtw_skew_y_var"skewY"),[Var.metadatatw_skew_y_var])|`Both->(List.filter_mapFun.id[rawtw_skew_x_var"skewX";rawtw_skew_y_var"skewY"],[Var.metadatatw_skew_x_var;Var.metadatatw_skew_y_var])inletrotate_x_ref=Var.reference_with_empty_fallbacktw_rotate_x_varinletrotate_y_ref=Var.reference_with_empty_fallbacktw_rotate_y_varinletrotate_z_ref=Var.reference_with_empty_fallbacktw_rotate_z_varinletskew_x_ref=Var.reference_with_empty_fallbacktw_skew_x_varinletskew_y_ref=Var.reference_with_empty_fallbacktw_skew_y_varinstyle~property_rules:rotate_skew_props~metadata(bindings@[transforms[Varrotate_x_ref;Varrotate_y_ref;Varrotate_z_ref;Varskew_x_ref;Varskew_y_ref;];])(* A bracket no reader took, written into the channel the class names and into
the transform chain that reads it. [raw_skew_style] above is the same shape
for the skew channels; this is the rotate one. *)letraw_rotate_stylevarfnvalue=letbinding=Parse.wrap_declaration_value~before:(fn^"(")~after:")"value|>Option.map(Css.custom_property~layer:"utilities"(Var.css_namevar))inletrotate_x_ref=Var.reference_with_empty_fallbacktw_rotate_x_varinletrotate_y_ref=Var.reference_with_empty_fallbacktw_rotate_y_varinletrotate_z_ref=Var.reference_with_empty_fallbacktw_rotate_z_varinletskew_x_ref=Var.reference_with_empty_fallbacktw_skew_x_varinletskew_y_ref=Var.reference_with_empty_fallbacktw_skew_y_varinstyle~property_rules:rotate_skew_props~metadata:[Var.metadatavar](Option.to_listbinding@[transforms[Varrotate_x_ref;Varrotate_y_ref;Varrotate_z_ref;Varskew_x_ref;Varskew_y_ref;];])(* The translate channels take the value itself rather than a function call,
so the raw form writes the text straight into the channel. *)letraw_translate_stylevarsvalue=letbindingvar=Css.custom_property~layer:"utilities"(Var.css_namevar)valueinletprops=collect_property_rules[tw_translate_x_var;tw_translate_y_var;tw_translate_z_var]instyle~property_rules:props~metadata:(List.mapVar.metadatavars)(List.mapbindingvars@[translate_xy_refs])(* Combined translate utilities *)lettranslate_full=letdx=Var.settw_translate_x_var(Pct100.0)inletdy=Var.settw_translate_y_var(Pct100.0)inletprops=collect_property_rules[tw_translate_x_var;tw_translate_y_var;tw_translate_z_var]instyle~property_rules:props(dx::dy::[translate_xy_refs])(* Combined spacing translate (translate-N / -translate-N): both axes bound to
calc(var(--spacing) * n); a negative n renders the "* -n" multiplier. *)lettranslate_spacing?themen=letspacing_decl,spacing_value=Theme.spacing_product?theme(float_of_intn)inletdx=Var.settw_translate_x_varspacing_valueinletdy=Var.settw_translate_y_varspacing_valueinletprops=collect_property_rules[tw_translate_x_var;tw_translate_y_var;tw_translate_z_var]instyle~property_rules:props(spacing_decl::dx::dy::[translate_xy_refs])lettranslate_1_2=(* Tailwind outputs calc(1 / 2 * 100%) rather than 50% *)lethalf_pct:Css.length=Css.Calc(Css.Calc.mul(Css.Calc.div(Css.Calc.float1.)(Css.Calc.float2.))(Css.Calc.length(Css.Pct100.)))inletdx=Var.settw_translate_x_varhalf_pctinletdy=Var.settw_translate_y_varhalf_pctinletprops=collect_property_rules[tw_translate_x_var;tw_translate_y_var;tw_translate_z_var]instyle~property_rules:props(dx::dy::[translate_xy_refs])lettranslate_arbitrarylen=letdx=Var.settw_translate_x_varleninletdy=Var.settw_translate_y_varleninletprops=collect_property_rules[tw_translate_x_var;tw_translate_y_var;tw_translate_z_var]instyle~property_rules:props(dx::dy::[translate_xy_refs])lettranslate_px=letdx=Var.settw_translate_x_var(Px1.0)inletdy=Var.settw_translate_y_var(Px1.0)inletprops=collect_property_rules[tw_translate_x_var;tw_translate_y_var;tw_translate_z_var]instyle~property_rules:props(dx::dy::[translate_xy_refs])letneg_translate_px=letdx=Var.settw_translate_x_var(Px(-1.0))inletdy=Var.settw_translate_y_var(Px(-1.0))inletprops=collect_property_rules[tw_translate_x_var;tw_translate_y_var;tw_translate_z_var]instyle~property_rules:props(dx::dy::[translate_xy_refs])letneg_translate_x_px=letaxis_decl=Var.settw_translate_x_var(Px(-1.0))instyle~property_rules:translate_props(axis_decl::[translate_xy_refs])letneg_translate_y_px=letaxis_decl=Var.settw_translate_y_var(Px(-1.0))instyle~property_rules:translate_props(axis_decl::[translate_xy_refs])letneg_translate_arbitrary_styles=neg_translate_arbitrary_on[tw_translate_x_var;tw_translate_y_var]sletneg_translate_full=letdx=Var.settw_translate_x_var(Pct(-100.0))inletdy=Var.settw_translate_y_var(Pct(-100.0))inletprops=collect_property_rules[tw_translate_x_var;tw_translate_y_var;tw_translate_z_var]instyle~property_rules:props(dx::dy::[translate_xy_refs])lettranslate_fractionnumdenom=letfrac_pct=make_fraction_pctnumdenominletdx=Var.settw_translate_x_varfrac_pctinletdy=Var.settw_translate_y_varfrac_pctinletprops=collect_property_rules[tw_translate_x_var;tw_translate_y_var;tw_translate_z_var]instyle~property_rules:props(dx::dy::[translate_xy_refs])letneg_translate_fractionnumdenom=letneg_frac_pct=make_neg_fraction_pctnumdenominletdx=Var.settw_translate_x_varneg_frac_pctinletdy=Var.settw_translate_y_varneg_frac_pctinletprops=collect_property_rules[tw_translate_x_var;tw_translate_y_var;tw_translate_z_var]instyle~property_rules:props(dx::dy::[translate_xy_refs])letneg_translate_x_fractionnumdenom=letaxis_decl=Var.settw_translate_x_var(make_neg_fraction_pctnumdenom)instyle~property_rules:translate_props(axis_decl::[translate_xy_refs])letneg_translate_y_fractionnumdenom=letaxis_decl=Var.settw_translate_y_var(make_neg_fraction_pctnumdenom)instyle~property_rules:translate_props(axis_decl::[translate_xy_refs])(* Legacy negative translate utilities for centering - kept for backward
compat *)letneg_translate_x_1_2=neg_translate_x_fraction12letneg_translate_y_1_2=neg_translate_y_fraction12(** {1 3D Transform Utilities} *)letrotate_xn=transform_with_vartw_rotate_x_var(Rotate_x(make_anglen))letrotate_x_arbitraryangle=transform_with_vartw_rotate_x_var(Rotate_xangle)letrotate_x_bare_varname=letbare=String.subname2(String.lengthname-2)inletref:Css.angleCss.var=Var.bracketbareintransform_with_vartw_rotate_x_var(Rotate_x(Varref))letneg_rotate_x_bare_varname=letbare=String.subname2(String.lengthname-2)inletneg:Css.angle=Calc(Expr(Var(Var.bracketbare),Mul,Num(-1.)))intransform_with_vartw_rotate_x_var(Rotate_xneg)letrotate_yn=transform_with_vartw_rotate_y_var(Rotate_y(make_anglen))letrotate_y_arbitraryangle=transform_with_vartw_rotate_y_var(Rotate_yangle)letrotate_y_bare_varname=letbare=String.subname2(String.lengthname-2)inletref:Css.angleCss.var=Var.bracketbareintransform_with_vartw_rotate_y_var(Rotate_y(Varref))letneg_rotate_y_bare_varname=letbare=String.subname2(String.lengthname-2)inletneg:Css.angle=Calc(Expr(Var(Var.bracketbare),Mul,Num(-1.)))intransform_with_vartw_rotate_y_var(Rotate_yneg)letrotate_zn=transform_with_vartw_rotate_z_var(Rotate_z(make_anglen))letrotate_z_arbitraryangle=transform_with_vartw_rotate_z_var(Rotate_zangle)letrotate_z_bare_varname=letbare=String.subname2(String.lengthname-2)inletref:Css.angleCss.var=Var.bracketbareintransform_with_vartw_rotate_z_var(Rotate_z(Varref))letneg_rotate_z_bare_varname=letbare=String.subname2(String.lengthname-2)inletneg:Css.angle=Calc(Expr(Var(Var.bracketbare),Mul,Num(-1.)))intransform_with_vartw_rotate_z_var(Rotate_zneg)lettranslate_z?themen=letspacing_decl,spacing_value=Theme.spacing_product?theme(float_of_intn)inletaxis_decl=Var.settw_translate_z_varspacing_valueinstyle~property_rules:translate_props(spacing_decl::axis_decl::[translate_xyz_refs])(* A fractional spacing step ([translate-z-0.5]), mirroring
[translate_axis_step] for the X/Y axes. *)lettranslate_z_step?themef=letspacing_decl,spacing_value=Theme.spacing_product?themefinletaxis_decl=Var.settw_translate_z_varspacing_valueinstyle~property_rules:translate_props(spacing_decl::axis_decl::[translate_xyz_refs])lettranslate_z_px=letaxis_decl=Var.settw_translate_z_var(Px1.0)instyle~property_rules:translate_props(axis_decl::[translate_xyz_refs])lettranslate_z_arbitrary=function|`Lengthlength->letaxis_decl=Var.settw_translate_z_varlengthinstyle~property_rules:translate_props(axis_decl::[translate_xyz_refs])|`Rawvalue->style~property_rules:translate_props~metadata:[Var.metadatatw_translate_z_var][Css.custom_property~layer:"utilities"(Var.css_nametw_translate_z_var)value;translate_xyz_refs;]letneg_translate_z_arbitrary_styles=neg_translate_arbitrary_on[tw_translate_z_var]sletneg_translate_z_px=letaxis_decl=Var.settw_translate_z_var(Px(-1.0))instyle~property_rules:translate_props(axis_decl::[translate_xyz_refs])lettranslate_3d=style~property_rules:translate_props[translate_xyz_refs]letscale_zn=letvalue=make_pctninletd=Var.settw_scale_z_varvalueinletprops=collect_property_rules[tw_scale_x_var;tw_scale_y_var;tw_scale_z_var]inletscale_x_ref=Var.referencetw_scale_x_varinletscale_y_ref=Var.referencetw_scale_y_varinletscale_z_ref=Var.referencetw_scale_z_varinstyle~property_rules:props(d::[Css.scale(XYZ(Varscale_x_ref,Varscale_y_ref,Varscale_z_ref))])letscale_z_arbitrarys=(* Arbitrary values pass through verbatim into [--tw-scale-z], whose
[@property] uses [syntax: "*"]. *)letd=Css.custom_property~layer:"utilities""--tw-scale-z"(Parse.decode_arbitrary_values)inletprops=collect_property_rules[tw_scale_x_var;tw_scale_y_var;tw_scale_z_var]inletscale_x_ref=Var.referencetw_scale_x_varinletscale_y_ref=Var.referencetw_scale_y_varinletscale_z_ref=Var.referencetw_scale_z_varinstyle~property_rules:props[d;Css.scale(XYZ(Varscale_x_ref,Varscale_y_ref,Varscale_z_ref))]letscale_3d=letprops=collect_property_rules[tw_scale_x_var;tw_scale_y_var;tw_scale_z_var]inletscale_x_ref=Var.referencetw_scale_x_varinletscale_y_ref=Var.referencetw_scale_y_varinletscale_z_ref=Var.referencetw_scale_z_varinstyle~property_rules:props[Css.scale(XYZ(Varscale_x_ref,Varscale_y_ref,Varscale_z_ref))](* A project that declares [--perspective-none] chooses the length the utility
resolves to, so read its value with the CSS length grammar rather than a
px-only test whose failure arm was a fabricated zero. A value the grammar
cannot read falls back to the utility's own meaning and still reaches the
sheet unaltered, since the theme layer restores the project's own text over
the bound value. *)letperspective_none?theme()=matchScheme.theme_valuetheme"perspective-none"with|Somevalue->letlen=Option.value(Css.parse_length(String.trimvalue))~default:(None:Css.length)inletdecl,r=Var.bindingperspective_none_varleninstyle(decl::[Css.perspective(Varr)])|None->style[Css.perspectiveNone]letis_theme_perspectivethemen=Scheme.theme_value(Sometheme)("perspective-"^n)<>Noneletperspective_themethemename=matchScheme.theme_value(Sometheme)("perspective-"^name)with|None->style[]|Someraw->(matchCss.parse_lengthrawwith|None->style[]|Somelen->letdecl,r=Var.binding(perspective_named_varname)leninstyle(decl::[Css.perspective(Varr)]))letperspective_dramatic=letdecl,r=Var.bindingperspective_dramatic_var(Px100.0)instyle(decl::[Css.perspective(Varr)])letperspective_near=letdecl,r=Var.bindingperspective_near_var(Px300.0)instyle(decl::[Css.perspective(Varr)])letperspective_normal=letdecl,r=Var.bindingperspective_normal_var(Px500.0)instyle(decl::[Css.perspective(Varr)])letperspective_midrange=letdecl,r=Var.bindingperspective_midrange_var(Px800.0)instyle(decl::[Css.perspective(Varr)])letperspective_distant=letdecl,r=Var.bindingperspective_distant_var(Px1200.0)instyle(decl::[Css.perspective(Varr)])letperspective_arbitrarylen=style[Css.perspectivelen]letpo_with_ref?themename(default:Css.perspective_origin)default_css()=matchScheme.theme_valuethemenamewith|Somevalue_str->letdecl=Css.custom_property~layer:"theme"("--"^name)value_strinletref:Css.perspective_origin=Var(Var.theme_refname~default~default_css)inletperspective_ref:Css.length=Css.Var(Var.theme_refname~default:(Css.Zero:Css.length)~default_css)instyle[decl;perspective_originref;Css.perspectiveperspective_ref](* Without a theme override Tailwind writes the keyword, not a reference to
a token nothing declares. *)|None->style[perspective_origindefault]letperspective_origin_center?theme()=po_with_ref?theme"perspective-origin-center"Center"center"()letperspective_origin_top?theme()=po_with_ref?theme"perspective-origin-top"Top"top"()letperspective_origin_bottom?theme()=po_with_ref?theme"perspective-origin-bottom"Bottom"bottom"()letperspective_origin_left?theme()=po_with_ref?theme"perspective-origin-left"(SingleZero)"0"()letperspective_origin_right?theme()=po_with_ref?theme"perspective-origin-right"(Single(Pct100.))"100%"()letperspective_origin_top_left?theme()=po_with_ref?theme"perspective-origin-top-left"(XY(Zero,Zero))"0 0"()letperspective_origin_top_right?theme()=po_with_ref?theme"perspective-origin-top-right"(XY(Pct100.,Zero))"100% 0"()letperspective_origin_bottom_left?theme()=po_with_ref?theme"perspective-origin-bottom-left"(XY(Zero,Pct100.))"0 100%"()letperspective_origin_bottom_right?theme()=po_with_ref?theme"perspective-origin-bottom-right"(XY(Pct100.,Pct100.))"100% 100%"()letperspective_origin_arbitrarypo=style[perspective_originpo]lettransform_style_3d=style[transform_stylePreserve_3d]lettransform_style_flat=style[transform_styleFlat]lettransform_box_border=style[Css.transform_boxBorder_box]lettransform_box_content=style[Css.transform_boxContent_box]lettransform_box_fill=style[Css.transform_boxFill_box]lettransform_box_stroke=style[Css.transform_boxStroke_box]lettransform_box_view=style[Css.transform_boxView_box]letbackface_visible=style[backface_visibilityVisible]letbackface_hidden=style[backface_visibilityHidden](** {1 Transform Origin Utilities}
Tailwind v4 uses theme variable references for origin utilities when theme
variables are defined. *)letorigin_with_ref?themename(default:Css.transform_origin)default_css()=matchScheme.theme_valuethemenamewith|Somevalue_str->letdecl=Css.custom_property~layer:"theme"("--"^name)value_strinletref:Css.transform_originCss.var=Var.theme_refname~default~default_cssinstyle[decl;transform_origin(Varref)]|None->style[transform_origindefault]letorigin_center?theme()=origin_with_ref?theme"transform-origin-center"Center"center"()letorigin_top?theme()=origin_with_ref?theme"transform-origin-top"Top"top"()letorigin_bottom?theme()=origin_with_ref?theme"transform-origin-bottom"Bottom"bottom"()letorigin_left?theme()=origin_with_ref?theme"transform-origin-left"(XZero)"0"()letorigin_right?theme()=origin_with_ref?theme"transform-origin-right"(X(Pct100.))"100%"()letorigin_top_left?theme()=origin_with_ref?theme"transform-origin-top-left"(XY(Zero,Zero))"0 0"()letorigin_top_right?theme()=origin_with_ref?theme"transform-origin-top-right"(XY(Pct100.,Zero))"100% 0"()letorigin_bottom_left?theme()=origin_with_ref?theme"transform-origin-bottom-left"(XY(Zero,Pct100.))"0 100%"()letorigin_bottom_right?theme()=origin_with_ref?theme"transform-origin-bottom-right"(XY(Pct100.,Pct100.))"100% 100%"()letorigin_arbitraryt=style[transform_origint](** {1 Transform Control Utilities} *)(* Tailwind v4 transform utility uses individual rotate-x/y/z and skew-x/y
variables with empty fallbacks. The output is: transform:
var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,)
var(--tw-skew-x,) var(--tw-skew-y,); When a variable is set (e.g.,
--tw-rotate-x: rotateX(45deg)), it becomes part of the transform. *)lettransform=(* Create var references with Empty fallbacks for optional transform
components *)letrotate_x_ref=Var.reference_with_empty_fallbacktw_rotate_x_varinletrotate_y_ref=Var.reference_with_empty_fallbacktw_rotate_y_varinletrotate_z_ref=Var.reference_with_empty_fallbacktw_rotate_z_varinletskew_x_ref=Var.reference_with_empty_fallbacktw_skew_x_varinletskew_y_ref=Var.reference_with_empty_fallbacktw_skew_y_varin(* Collect @property rules for these variables *)letproperty_rules=collect_property_rules[tw_rotate_x_var;tw_rotate_y_var;tw_rotate_z_var;tw_skew_x_var;tw_skew_y_var;]instyle~property_rules[transforms[Varrotate_x_ref;Varrotate_y_ref;Varrotate_z_ref;Varskew_x_ref;Varskew_y_ref;];]lettransform_none=style[Css.transformNone](* [transform-cpu] writes what [transform] writes. The [@property] block for
the five channels belongs to [transform] alone, though: Tailwind declares
them there and neither of the two below carries them, so asking for the
rules here put a whole [@layer properties] in a sheet whose only transform
utility was this one. *)lettransform_cpu=letrotate_x_ref=Var.reference_with_empty_fallbacktw_rotate_x_varinletrotate_y_ref=Var.reference_with_empty_fallbacktw_rotate_y_varinletrotate_z_ref=Var.reference_with_empty_fallbacktw_rotate_z_varinletskew_x_ref=Var.reference_with_empty_fallbacktw_skew_x_varinletskew_y_ref=Var.reference_with_empty_fallbacktw_skew_y_varinstyle[transforms[Varrotate_x_ref;Varrotate_y_ref;Varrotate_z_ref;Varskew_x_ref;Varskew_y_ref;];](* transform-gpu adds translateZ(0) for GPU acceleration plus all var refs. No
[@property] block here either; see [transform_cpu]. *)lettransform_gpu=letrotate_x_ref=Var.reference_with_empty_fallbacktw_rotate_x_varinletrotate_y_ref=Var.reference_with_empty_fallbacktw_rotate_y_varinletrotate_z_ref=Var.reference_with_empty_fallbacktw_rotate_z_varinletskew_x_ref=Var.reference_with_empty_fallbacktw_skew_x_varinletskew_y_ref=Var.reference_with_empty_fallbacktw_skew_y_varinstyle[transforms[Translate_zZero;Varrotate_x_ref;Varrotate_y_ref;Varrotate_z_ref;Varskew_x_ref;Varskew_y_ref;];](** {1 Parsing Functions} *)let(>|=)=Parse.(>|=)(** {1 Utility Conversion Functions} *)letto_styletheme=letperspective_none()=perspective_none~theme()inletperspective_origin_center()=perspective_origin_center~theme()inletperspective_origin_top()=perspective_origin_top~theme()inletperspective_origin_bottom()=perspective_origin_bottom~theme()inletperspective_origin_left()=perspective_origin_left~theme()inletperspective_origin_right()=perspective_origin_right~theme()inletperspective_origin_top_left()=perspective_origin_top_left~theme()inletperspective_origin_top_right()=perspective_origin_top_right~theme()inletperspective_origin_bottom_left()=perspective_origin_bottom_left~theme()inletperspective_origin_bottom_right()=perspective_origin_bottom_right~theme()inletorigin_center()=origin_center~theme()inletorigin_top()=origin_top~theme()inletorigin_bottom()=origin_bottom~theme()inletorigin_left()=origin_left~theme()inletorigin_right()=origin_right~theme()inletorigin_top_left()=origin_top_left~theme()inletorigin_top_right()=origin_top_right~theme()inletorigin_bottom_left()=origin_bottom_left~theme()inletorigin_bottom_right()=origin_bottom_right~theme()infunction|Rotaten->rotaten|Rotate_arbitrary(_,a)->rotate_arbitrarya|Rotate_raw(_,value)->opaque_style"rotate"value|Rotate_none->style[Css.rotateNone]|Rotate_3d_arbitrary(_,x,y,z,a)->rotate_3d_arbitraryxyza|Rotate_bare_varname->rotate_bare_varname|Neg_rotate_bare_varname->neg_rotate_bare_varname|Neg_rotate_arbitrarys->neg_rotate_arbitrarys|Translate_xn->translate_x~themen|Translate_x_full->translate_x_full|Translate_x_px->translate_x_px|Translate_x_stepf->translate_x_step~themef|Translate_x_arbitrary(_,len)->translate_x_arbitrarylen|Translate_x_raw(_,v)->raw_translate_style[tw_translate_x_var]v|Translate_y_raw(_,v)->raw_translate_style[tw_translate_y_var]v|Translate_raw(_,v)->raw_translate_style[tw_translate_x_var;tw_translate_y_var]v|Rotate_x_raw(_,v)->raw_rotate_styletw_rotate_x_var"rotateX"v|Rotate_y_raw(_,v)->raw_rotate_styletw_rotate_y_var"rotateY"v|Rotate_z_raw(_,v)->raw_rotate_styletw_rotate_z_var"rotateZ"v|Perspective_raw(_,v)->style(Option.to_list(Parse.opaque_declaration"perspective"v))|Perspective_origin_raw(_,v)->style(Option.to_list(Parse.opaque_declaration"perspective-origin"v))|Origin_raw(_,v)->style(Option.to_list(Parse.opaque_declaration"transform-origin"v))|Transform_raw(_,v)->style(Option.to_list(Parse.opaque_declaration"transform"v))|Translate_x_fraction(num,denom)->translate_x_fractionnumdenom|Translate_yn->translate_y~themen|Translate_y_full->translate_y_full|Translate_y_px->translate_y_px|Translate_y_stepf->translate_y_step~themef|Translate_y_arbitrary(_,len)->translate_y_arbitrarylen|Translate_y_fraction(num,denom)->translate_y_fractionnumdenom|Translaten->translate_spacing~themen|Neg_translaten->translate_spacing~theme(-n)|Translate_full->translate_full|Translate_none->style[Css.translateNone]|Translate_px->translate_px|Translate_1_2->translate_1_2|Translate_fraction(num,denom)->translate_fractionnumdenom|Translate_arbitrary(_,len)->translate_arbitrarylen|Neg_translate_arbitrarys->neg_translate_arbitrary_styles|Neg_translate_full->neg_translate_full|Neg_translate_px->neg_translate_px|Neg_translate_x_px->neg_translate_x_px|Neg_translate_y_px->neg_translate_y_px|Neg_translate_fraction(num,denom)->neg_translate_fractionnumdenom|Neg_translate_x_arbitrarys->neg_translate_x_arbitrary_styles|Neg_translate_x_full->neg_translate_x_full|Neg_translate_x_1_2->neg_translate_x_1_2|Neg_translate_x_fraction(num,denom)->neg_translate_x_fractionnumdenom|Neg_translate_y_arbitrarys->neg_translate_y_arbitrary_styles|Neg_translate_y_full->neg_translate_y_full|Neg_translate_y_1_2->neg_translate_y_1_2|Neg_translate_y_fraction(num,denom)->neg_translate_y_fractionnumdenom|Translate_zn->translate_z~themen|Translate_z_stepf->translate_z_step~themef|Translate_z_px->translate_z_px|Translate_z_arbitrary(_,value)->translate_z_arbitraryvalue|Neg_translate_z_arbitrarys->neg_translate_z_arbitrary_styles|Neg_translate_z_px->neg_translate_z_px|Translate_3d->translate_3d|Scalen->scalen|Scale_xn->scale_xn|Scale_x_arbitrary(_,v)->scale_x_arbitraryv|Scale_yn->scale_yn|Scale_y_arbitrary(_,v)->scale_y_arbitraryv|Scale_raw_1(_,f)->style[Css.scale(X(Numf))]|Scale_raw_3(_,x,y,z)->style[Css.scale(XYZ(Numx,Numy,Numz))]|Scale_arbitrary_raw(_,value)->opaque_style"scale"value|Scale_zn->scale_zn|Scale_z_arbitrarys->scale_z_arbitrarys|Neg_scale_arbitrarys->neg_scale_arbitrarys|Neg_scale_x_arbitrarys->neg_scale_x_arbitrarys|Neg_scale_y_arbitrarys->neg_scale_y_arbitrarys|Neg_scale_z_arbitrarys->neg_scale_z_arbitrarys|Scale_3d->scale_3d|Scale_none->style[Css.scaleNone]|Skew_xn->skew_xn|Skew_x_arbitrary(_,`Anglea)->skew_x_arbitrarya|Skew_x_arbitrary(_,`Rawvalue)->raw_skew_style`Xvalue|Skew_yn->skew_yn|Skew_y_arbitrary(_,`Anglea)->skew_y_arbitrarya|Skew_y_arbitrary(_,`Rawvalue)->raw_skew_style`Yvalue|Skewn->transform_with_both_skewn|Skew_arbitrary(_,`Anglea)->transform_with_both_skew_anglea|Skew_arbitrary(_,`Rawvalue)->raw_skew_style`Bothvalue|Neg_skew_arbitrarys->neg_skew_arbitrarys|Neg_skew_x_arbitrarys->neg_skew_x_arbitrarys|Neg_skew_y_arbitrarys->neg_skew_y_arbitrarys|Rotate_xn->rotate_xn|Rotate_x_arbitrary(_,a)->rotate_x_arbitrarya|Rotate_x_bare_varname->rotate_x_bare_varname|Neg_rotate_x_bare_varname->neg_rotate_x_bare_varname|Neg_rotate_x_arbitrarys->neg_rotate_x_arbitrarys|Rotate_yn->rotate_yn|Rotate_y_arbitrary(_,a)->rotate_y_arbitrarya|Rotate_y_bare_varname->rotate_y_bare_varname|Neg_rotate_y_bare_varname->neg_rotate_y_bare_varname|Neg_rotate_y_arbitrarys->neg_rotate_y_arbitrarys|Rotate_zn->rotate_zn|Rotate_z_arbitrary(_,a)->rotate_z_arbitrarya|Rotate_z_bare_varname->rotate_z_bare_varname|Neg_rotate_z_bare_varname->neg_rotate_z_bare_varname|Neg_rotate_z_arbitrarys->neg_rotate_z_arbitrarys|Perspective_none->perspective_none()|Perspective_dramatic->perspective_dramatic|Perspective_themename->perspective_themethemename|Perspective_near->perspective_near|Perspective_normal->perspective_normal|Perspective_midrange->perspective_midrange|Perspective_distant->perspective_distant|Perspective_arbitrary(_,len)->perspective_arbitrarylen|Perspective_origin_center->perspective_origin_center()|Perspective_origin_top->perspective_origin_top()|Perspective_origin_bottom->perspective_origin_bottom()|Perspective_origin_left->perspective_origin_left()|Perspective_origin_right->perspective_origin_right()|Perspective_origin_top_left->perspective_origin_top_left()|Perspective_origin_top_right->perspective_origin_top_right()|Perspective_origin_bottom_left->perspective_origin_bottom_left()|Perspective_origin_bottom_right->perspective_origin_bottom_right()|Perspective_origin_arbitrary(_,po)->perspective_origin_arbitrarypo|Transform_style_3d->transform_style_3d|Transform_style_flat->transform_style_flat|Transform_box_border->transform_box_border|Transform_box_content->transform_box_content|Transform_box_fill->transform_box_fill|Transform_box_stroke->transform_box_stroke|Transform_box_view->transform_box_view|Backface_visible->backface_visible|Backface_hidden->backface_hidden|Transform->transform|Transform_cpu->transform_cpu|Transform_none->transform_none|Transform_gpu->transform_gpu|Transform_arbitrary(_,ts)->style[transformsts]|Origin_center->origin_center()|Origin_top->origin_top()|Origin_bottom->origin_bottom()|Origin_left->origin_left()|Origin_right->origin_right()|Origin_top_left->origin_top_left()|Origin_top_right->origin_top_right()|Origin_bottom_left->origin_bottom_left()|Origin_bottom_right->origin_bottom_right()|Origin_arbitrary(_,t)->origin_arbitrarytletsuborder=function|Transform->2000|Transform_arbitrary_|Transform_raw_->2001|Transform_cpu->2002|Transform_gpu->2003|Transform_none->2004(* Candidate values share a slot within each sign/axis band. Tailwind uses
the natural candidate spelling inside that slot; baking the magnitude
into the suborder reverses negatives and lets a large value spill into
the next axis. *)|Neg_translate_|Neg_translate_arbitrary_|Neg_translate_full|Neg_translate_px|Neg_translate_fraction_->20|Translate_|Translate_arbitrary_|Translate_raw_|Translate_full|Translate_px|Translate_1_2|Translate_fraction_->21|Translate_xnwhenn<0->100|Translate_x_stepfwhenf<0.->100|Neg_translate_x_arbitrary_|Neg_translate_x_full|Neg_translate_x_px|Neg_translate_x_1_2|Neg_translate_x_fraction_->100|Translate_x_|Translate_x_fraction_|Translate_x_full|Translate_x_px|Translate_x_step_|Translate_x_arbitrary_|Translate_x_raw_->101|Translate_ynwhenn<0->200|Translate_y_stepfwhenf<0.->200|Neg_translate_y_arbitrary_|Neg_translate_y_full|Neg_translate_y_px|Neg_translate_y_1_2|Neg_translate_y_fraction_->200|Translate_y_|Translate_y_fraction_|Translate_y_full|Translate_y_px|Translate_y_step_|Translate_y_arbitrary_|Translate_y_raw_->201|Translate_znwhenn<0->300|Translate_z_stepfwhenf<0.->300|Neg_translate_z_arbitrary_|Neg_translate_z_px->300|Translate_z_|Translate_z_step_|Translate_z_px|Translate_z_arbitrary_->301|Translate_3d|Translate_none->302(* Scale utilities use the same sign/axis bands. *)|Scalenwhenn<0->400|Neg_scale_arbitrary_->400|Scale_|Scale_raw_1_|Scale_raw_3_|Scale_arbitrary_raw_->401|Scale_xnwhenn<0->500|Neg_scale_x_arbitrary_->500|Scale_x_|Scale_x_arbitrary_->501|Scale_ynwhenn<0->600|Neg_scale_y_arbitrary_->600|Scale_y_|Scale_y_arbitrary_->601|Scale_znwhenn<0->700|Neg_scale_z_arbitrary_->700|Scale_z_|Scale_z_arbitrary_->701|Scale_3d|Scale_none->702(* Rotate utilities: sign first, then a bare variable, named values and the
arbitrary/none tail. Axes each get their own sign pair. *)|Rotatenwhenn<0->799|Neg_rotate_bare_var_|Neg_rotate_arbitrary_->799|Rotate_bare_var_->800|Rotate_->801|Rotate_3d_arbitrary_|Rotate_arbitrary_|Rotate_raw_|Rotate_none->802|Rotate_xnwhenn<0->900|Neg_rotate_x_bare_var_|Neg_rotate_x_arbitrary_->900|Rotate_x_|Rotate_x_bare_var_|Rotate_x_arbitrary_|Rotate_x_raw_->901|Rotate_ynwhenn<0->1000|Neg_rotate_y_bare_var_|Neg_rotate_y_arbitrary_->1000|Rotate_y_|Rotate_y_bare_var_|Rotate_y_arbitrary_|Rotate_y_raw_->1001|Rotate_znwhenn<0->1100|Neg_rotate_z_bare_var_|Neg_rotate_z_arbitrary_->1100|Rotate_z_|Rotate_z_bare_var_|Rotate_z_arbitrary_|Rotate_z_raw_->1101(* Combined skew precedes skew-x, then skew-y. *)|Skewnwhenn<0->1200|Neg_skew_arbitrary_->1200|Skew_|Skew_arbitrary_->1201|Skew_xnwhenn<0->1210|Neg_skew_x_arbitrary_->1210|Skew_x_|Skew_x_arbitrary_->1211|Skew_ynwhenn<0->1300|Neg_skew_y_arbitrary_->1300|Skew_y_|Skew_y_arbitrary_->1301(* Perspective depths are one candidate band; Tailwind orders their class
spellings rather than their semantic distance. *)|Perspective_arbitrary_|Perspective_raw_|Perspective_dramatic|Perspective_none|Perspective_theme_|Perspective_normal|Perspective_near|Perspective_midrange|Perspective_distant->1400|Perspective_origin_arbitrary_|Perspective_origin_raw_->1499|Perspective_origin_bottom->1500|Perspective_origin_bottom_left->1501|Perspective_origin_bottom_right->1502|Perspective_origin_center->1503|Perspective_origin_left->1504|Perspective_origin_right->1505|Perspective_origin_top->1506|Perspective_origin_top_left->1507|Perspective_origin_top_right->1508(* Alphabetical by class name: backface-hidden, backface-visible,
transform-3d, transform-border, ..., transform-flat, ...,
transform-view *)|Backface_hidden->1600|Backface_visible->1601|Transform_style_3d->1602|Transform_box_border->1603|Transform_box_content->1604|Transform_box_fill->1605|Transform_style_flat->1606|Transform_box_stroke->1607|Transform_box_view->1608(* Transform origin - alphabetical: bottom, bottom-left, bottom-right,
center, left, right, top, top-left, top-right *)|Origin_bottom->1700|Origin_bottom_left->1701|Origin_bottom_right->1702|Origin_center->1703|Origin_left->1704|Origin_right->1705|Origin_top->1706|Origin_top_left->1707|Origin_top_right->1708|Origin_arbitrary_|Origin_raw_->1699(* A fractional spacing step: [0.5], [2.5]. Integers keep the existing
path. *)letparse_spacing_steps=matchParse.decimal_floatswith|Somefwhenf>0.&¬(Float.is_integerf)->Somef|_->None(* The translate family writes [calc(n / m * 100%)] out rather than folding
it, so it takes every fraction Tailwind reads, a zero on either side
included. *)letparse_fraction=Parse.fraction(* The value a bracket denotes, read with the grammar cascade already has for
the property, off the arbitrary-value pipeline every other family reads:
[_] stands for a space, [--spacing(n)] expands, and a binary [+] gets the
spaces CSS math wants, so [calc(1px+1px)] is a value rather than a parse
error. [None] is a bracket that grammar refuses, and [of_class] declines
the utility rather than leaving [to_style] to raise.
A data-type hint chooses which longhand a bracket lands in and says nothing
about the value. Every family reading through here writes one longhand, so
the hint lands there and [read] is handed what follows it. A bracket whose
hint is empty names no utility, which is the other [None]. The caller keeps
the bracket whole for the class name, which is what the markup carries. *)letarbitrary_valuereadinner=Option.bind(Parse.value_after_hintinner)(funvalue->letcursor=Cascade.Cursor.of_string(Parse.decode_arbitrary_valuevalue)inmatchCascade.Cursor.try_parse_full_errreadcursorwith|Okv->Somev|Error_->None)letof_classthemeclass_name=letparts=Parse.split_classclass_nameinmatchpartswith|["rotate";n]whenParse.is_bare_varn->Ok(Rotate_bare_var(Parse.bare_var_innern))|["rotate";n]whenParse.is_bracket_valuen->(letinner=Parse.bracket_innernin(* The 3D form is four components, [x y z <angle>]. [_] separates them
and [\_] is a literal underscore inside one, so the value is decoded
before it is split. What the axes take is a CSS number, which OCaml's
own float reader is wider than: it reads [1_2] as 12, where the value
is meant to reach the sheet as written. *)letparts_3d=String.split_on_char' '(Parse.decode_underscoresinner)inmatchparts_3dwith|[x;y;z;a]->(match(Parse.decimal_floatx,Parse.decimal_floaty,Parse.decimal_floatz,parse_bracket_angle("["^a^"]"))with|Somefx,Somefy,Somefz,Ok(_,angle)->Ok(Rotate_3d_arbitrary(inner,fx,fy,fz,angle))|_->(matchparse_bracket_anglenwith|Ok(raw,a)->Ok(Rotate_arbitrary(raw,a))|Error_->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Rotate_raw(inner,value))|None->err_not_utility)))|_->(matchparse_bracket_anglenwith|Ok(raw,a)->Ok(Rotate_arbitrary(raw,a))|Error_->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Rotate_raw(inner,value))|None->err_not_utility)))|["rotate";"none"]->OkRotate_none|["rotate";n]->Parse.int_anyn>|=funn->Rotaten|["translate";"x";n]whenParse.is_bracket_valuen->(matchparse_bracket_lengthnwith|Oklen->Ok(Translate_x_arbitrary(Parse.bracket_innern,len))|Error_->(letraw=Parse.bracket_innerninmatchParse.arbitrary_declaration_valuerawwith|Somev->Ok(Translate_x_raw(raw,v))|None->err_not_utility))|["translate";"x";"full"]->OkTranslate_x_full|["translate";"x";"px"]->OkTranslate_x_px|["translate";"x";n]whenString.containsn'/'->(matchparse_fractionnwith|Some(num,denom)->Ok(Translate_x_fraction(num,denom))|None->err_not_utility)|["translate";"x";n]whenparse_spacing_stepn<>None->Ok(Translate_x_step(Option.get(parse_spacing_stepn)))|["translate";"x";n]->Parse.int_anyn>|=funn->Translate_xn|["translate";"y";n]whenParse.is_bracket_valuen->(matchparse_bracket_lengthnwith|Oklen->Ok(Translate_y_arbitrary(Parse.bracket_innern,len))|Error_->(letraw=Parse.bracket_innerninmatchParse.arbitrary_declaration_valuerawwith|Somev->Ok(Translate_y_raw(raw,v))|None->err_not_utility))|["translate";"y";"full"]->OkTranslate_y_full|["translate";"y";"px"]->OkTranslate_y_px|["translate";"y";n]whenString.containsn'/'->(matchparse_fractionnwith|Some(num,denom)->Ok(Translate_y_fraction(num,denom))|None->err_not_utility)|["translate";"y";n]whenparse_spacing_stepn<>None->Ok(Translate_y_step(Option.get(parse_spacing_stepn)))|["translate";"y";n]->Parse.int_anyn>|=funn->Translate_yn|["translate";"z";n]whenParse.is_bracket_valuen->(letinner=Parse.bracket_innerninmatchParse.arbitrary_lengthinnerwith|Somelength->Ok(Translate_z_arbitrary(inner,`Lengthlength))|None->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Translate_z_arbitrary(inner,`Rawvalue))|None->err_not_utility))|["translate";"z";"px"]->OkTranslate_z_px|["translate";"z";n]whenparse_spacing_stepn<>None->Ok(Translate_z_step(Option.get(parse_spacing_stepn)))|["translate";"z";n]->Parse.int_anyn>|=funn->Translate_zn|["translate";"full"]->OkTranslate_full|["translate";"none"]->OkTranslate_none|["translate";"px"]->OkTranslate_px|["translate";"1/2"]->OkTranslate_1_2|["translate";n]whenString.containsn'/'->(matchparse_fractionnwith|Some(num,denom)->Ok(Translate_fraction(num,denom))|None->err_not_utility)|["translate";"3d"]->OkTranslate_3d|["translate";n]whennot(Parse.is_bracket_valuen)->Parse.int_anyn>|=funn->Translaten|"translate"::restwhenmatchrestwith|[]|["x";_]|["y";_]|["z";_]|["full"]|["1/2"]->false|[n]whenString.containsn'/'->false|_->true->(letvalue=String.concat"-"restinmatchparse_bracket_lengthvaluewith|Oklen->Ok(Translate_arbitrary(Parse.bracket_innervalue,len))|Error_whennot(Parse.is_bracket_valuevalue)->err_not_utility|Error_->(letraw=Parse.bracket_innervalueinmatchParse.arbitrary_declaration_valuerawwith|Somev->Ok(Translate_raw(raw,v))|None->err_not_utility))(* Negative translate utilities: -translate-x-N, -translate-y-N,
-translate-z-N Split by '-' gives [""; "translate"; axis; n] *)|["";"translate";value]whenneg_bracketvalue->letinner=Parse.bracket_innervalueinOk(Neg_translate_arbitraryinner)|["";"translate";"full"]->OkNeg_translate_full|["";"translate";"px"]->OkNeg_translate_px|["";"translate";n]whenString.containsn'/'->(matchparse_fractionnwith|Some(num,denom)->Ok(Neg_translate_fraction(num,denom))|None->err_not_utility)|["";"translate";n]->Parse.int_anyn>|=funn->Neg_translaten|["";"translate";"x";value]whenneg_bracketvalue->letinner=Parse.bracket_innervalueinOk(Neg_translate_x_arbitraryinner)|["";"translate";"x";"full"]->OkNeg_translate_x_full|["";"translate";"x";"px"]->OkNeg_translate_x_px|["";"translate";"x";n]whenString.containsn'/'->(matchparse_fractionnwith|Some(num,denom)->Ok(Neg_translate_x_fraction(num,denom))|None->err_not_utility)|["";"translate";"x";n]whenparse_spacing_stepn<>None->Ok(Translate_x_step(-.Option.get(parse_spacing_stepn)))|["";"translate";"x";n]->Parse.int_pos~name:"translate-x"n>|=funn->Translate_x(-n)|["";"translate";"y";value]whenneg_bracketvalue->letinner=Parse.bracket_innervalueinOk(Neg_translate_y_arbitraryinner)|["";"translate";"y";"full"]->OkNeg_translate_y_full|["";"translate";"y";"px"]->OkNeg_translate_y_px|["";"translate";"y";n]whenString.containsn'/'->(matchparse_fractionnwith|Some(num,denom)->Ok(Neg_translate_y_fraction(num,denom))|None->err_not_utility)|["";"translate";"y";n]whenparse_spacing_stepn<>None->Ok(Translate_y_step(-.Option.get(parse_spacing_stepn)))|["";"translate";"y";n]->Parse.int_pos~name:"translate-y"n>|=funn->Translate_y(-n)|["";"translate";"z";value]whenneg_bracketvalue->letinner=Parse.bracket_innervalueinOk(Neg_translate_z_arbitraryinner)|["";"translate";"z";"px"]->OkNeg_translate_z_px|["";"translate";"z";n]whenparse_spacing_stepn<>None->Ok(Translate_z_step(-.Option.get(parse_spacing_stepn)))|["";"translate";"z";n]->Parse.int_pos~name:"translate-z"n>|=funn->Translate_z(-n)|["scale";n]whenParse.is_bracket_valuen->(letinner=Parse.bracket_innernin(* Check for multi-value: x_y_z *)letparts=String.split_on_char'_'inner|>List.filter(funs->s<>"")inmatchpartswith|[x;y;z]->(match(Float.of_string_optx,Float.of_string_opty,Float.of_string_optz)with|Somefx,Somefy,Somefz->Ok(Scale_raw_3(inner,fx,fy,fz))|_->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Scale_arbitrary_raw(inner,value))|None->err_not_utility))|[_]->(matchFloat.of_string_optinnerwith|Somef->Ok(Scale_raw_1(inner,f))|None->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Scale_arbitrary_raw(inner,value))|None->err_not_utility))|_->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Scale_arbitrary_raw(inner,value))|None->err_not_utility))|["scale";"3d"]->OkScale_3d|["scale";"none"]->OkScale_none|["scale";n]->Parse.int_pos~name:"scale"n>|=funn->Scalen|["scale";"x";n]whenParse.is_bracket_valuen->(matchparse_bracket_numbernwith|Ok(raw,f)->Ok(Scale_x_arbitrary(raw,f))|Error_->err_not_utility)|["scale";"x";n]->Parse.int_pos~name:"scale-x"n>|=funn->Scale_xn|["scale";"y";n]whenParse.is_bracket_valuen->(matchparse_bracket_numbernwith|Ok(raw,f)->Ok(Scale_y_arbitrary(raw,f))|Error_->err_not_utility)|["scale";"y";n]->Parse.int_pos~name:"scale-y"n>|=funn->Scale_yn|["scale";"z";n]whenParse.is_bracket_valuen&&Parse.is_declaration_value(Parse.decode_arbitrary_value(Parse.bracket_innern))->Ok(Scale_z_arbitrary(Parse.bracket_innern))|["scale";"z";n]->Parse.int_pos~name:"scale-z"n>|=funn->Scale_zn|["skew";"x";n]whenParse.is_bracket_valuen->(matchparse_bracket_anglenwith|Ok(raw,a)->Ok(Skew_x_arbitrary(raw,`Anglea))|Error_->(letraw=Parse.bracket_innerninmatchParse.arbitrary_declaration_valuerawwith|Somevalue->Ok(Skew_x_arbitrary(raw,`Rawvalue))|None->err_not_utility))|["skew";"x";n]->Parse.int_anyn>|=funn->Skew_xn|["skew";"y";n]whenParse.is_bracket_valuen->(matchparse_bracket_anglenwith|Ok(raw,a)->Ok(Skew_y_arbitrary(raw,`Anglea))|Error_->(letraw=Parse.bracket_innerninmatchParse.arbitrary_declaration_valuerawwith|Somevalue->Ok(Skew_y_arbitrary(raw,`Rawvalue))|None->err_not_utility))|["skew";"y";n]->Parse.int_anyn>|=funn->Skew_yn|["skew";n]whenParse.is_bracket_valuen->(matchparse_bracket_anglenwith|Ok(raw,a)->Ok(Skew_arbitrary(raw,`Anglea))|Error_->(letraw=Parse.bracket_innerninmatchParse.arbitrary_declaration_valuerawwith|Somevalue->Ok(Skew_arbitrary(raw,`Rawvalue))|None->err_not_utility))|["skew";n]->Parse.int_anyn>|=funn->Skewn|["rotate";"x";n]whenParse.is_bare_varn->Ok(Rotate_x_bare_var(Parse.bare_var_innern))|["rotate";"x";n]whenParse.is_bracket_valuen->(matchparse_bracket_anglenwith|Ok(raw,a)->Ok(Rotate_x_arbitrary(raw,a))|Error_->((* The channel is this family's longhand, so a bracket no angle
reader took still reaches it. *)letraw=Parse.bracket_innerninmatchParse.arbitrary_declaration_valuerawwith|Somevalue->Ok(Rotate_x_raw(raw,value))|None->err_not_utility))|["rotate";"x";n]->Parse.int_anyn>|=funn->Rotate_xn|["rotate";"y";n]whenParse.is_bare_varn->Ok(Rotate_y_bare_var(Parse.bare_var_innern))|["rotate";"y";n]whenParse.is_bracket_valuen->(matchparse_bracket_anglenwith|Ok(raw,a)->Ok(Rotate_y_arbitrary(raw,a))|Error_->((* The channel is this family's longhand, so a bracket no angle
reader took still reaches it. *)letraw=Parse.bracket_innerninmatchParse.arbitrary_declaration_valuerawwith|Somevalue->Ok(Rotate_y_raw(raw,value))|None->err_not_utility))|["rotate";"y";n]->Parse.int_anyn>|=funn->Rotate_yn|["rotate";"z";n]whenParse.is_bare_varn->Ok(Rotate_z_bare_var(Parse.bare_var_innern))|["rotate";"z";n]whenParse.is_bracket_valuen->(matchparse_bracket_anglenwith|Ok(raw,a)->Ok(Rotate_z_arbitrary(raw,a))|Error_->((* The channel is this family's longhand, so a bracket no angle
reader took still reaches it. *)letraw=Parse.bracket_innerninmatchParse.arbitrary_declaration_valuerawwith|Somevalue->Ok(Rotate_z_raw(raw,value))|None->err_not_utility))|["rotate";"z";n]->Parse.int_anyn>|=funn->Rotate_zn(* Negative rotate: -rotate-N, -rotate-(--var), -rotate-[123deg] *)|["";"rotate";n]whenParse.is_bare_varn->Ok(Neg_rotate_bare_var(Parse.bare_var_innern))|["";"rotate";value]whenneg_bracketvalue->Ok(Neg_rotate_arbitrary(Parse.bracket_innervalue))|["";"rotate";n]->Parse.int_pos~name:"rotate"n>|=funn->Rotate(-n)|["";"rotate";"x";n]whenParse.is_bare_varn->Ok(Neg_rotate_x_bare_var(Parse.bare_var_innern))|["";"rotate";"x";value]whenneg_bracketvalue->Ok(Neg_rotate_x_arbitrary(Parse.bracket_innervalue))|["";"rotate";"x";n]->Parse.int_pos~name:"rotate-x"n>|=funn->Rotate_x(-n)|["";"rotate";"y";n]whenParse.is_bare_varn->Ok(Neg_rotate_y_bare_var(Parse.bare_var_innern))|["";"rotate";"y";value]whenneg_bracketvalue->Ok(Neg_rotate_y_arbitrary(Parse.bracket_innervalue))|["";"rotate";"y";n]->Parse.int_pos~name:"rotate-y"n>|=funn->Rotate_y(-n)|["";"rotate";"z";n]whenParse.is_bare_varn->Ok(Neg_rotate_z_bare_var(Parse.bare_var_innern))|["";"rotate";"z";value]whenneg_bracketvalue->Ok(Neg_rotate_z_arbitrary(Parse.bracket_innervalue))|["";"rotate";"z";n]->Parse.int_pos~name:"rotate-z"n>|=funn->Rotate_z(-n)(* Negative scale: -scale-N, -scale-[1.5] *)|["";"scale";value]whenneg_bracketvalue->Ok(Neg_scale_arbitrary(Parse.bracket_innervalue))|["";"scale";n]->Parse.int_pos~name:"scale"n>|=funn->Scale(-n)|["";"scale";"x";value]whenneg_bracketvalue->Ok(Neg_scale_x_arbitrary(Parse.bracket_innervalue))|["";"scale";"x";n]->Parse.int_pos~name:"scale-x"n>|=funn->Scale_x(-n)|["";"scale";"y";value]whenneg_bracketvalue->Ok(Neg_scale_y_arbitrary(Parse.bracket_innervalue))|["";"scale";"y";n]->Parse.int_pos~name:"scale-y"n>|=funn->Scale_y(-n)|["";"scale";"z";value]whenneg_bracketvalue->Ok(Neg_scale_z_arbitrary(Parse.bracket_innervalue))|["";"scale";"z";n]->Parse.int_pos~name:"scale-z"n>|=funn->Scale_z(-n)(* Negative skew: -skew-N, -skew-x-N, -skew-y-N, -skew-x-[10deg] *)|["";"skew";"x";value]whenneg_bracketvalue->Ok(Neg_skew_x_arbitrary(Parse.bracket_innervalue))|["";"skew";"x";n]->Parse.int_pos~name:"skew-x"n>|=funn->Skew_x(-n)|["";"skew";"y";value]whenneg_bracketvalue->Ok(Neg_skew_y_arbitrary(Parse.bracket_innervalue))|["";"skew";"y";n]->Parse.int_pos~name:"skew-y"n>|=funn->Skew_y(-n)|["";"skew";value]whenneg_bracketvalue->Ok(Neg_skew_arbitrary(Parse.bracket_innervalue))|["";"skew";n]->Parse.int_pos~name:"skew"n>|=funn->Skew(-n)|["perspective";"none"]->OkPerspective_none|["perspective";"dramatic"]->OkPerspective_dramatic|["perspective";"near"]->OkPerspective_near|["perspective";"normal"]->OkPerspective_normal|["perspective";"midrange"]->OkPerspective_midrange|["perspective";"distant"]->OkPerspective_distant|"perspective"::restwhen(matchrestwith"origin"::_|[]->false|_->true)&&is_theme_perspectivetheme(String.concat"-"rest)->Ok(Perspective_theme(String.concat"-"rest))|"perspective"::restwhenmatchrestwith"origin"::_|[]->false|_->true->(letvalue=String.concat"-"restinmatchparse_bracket_lengthvaluewith|Oklen->Ok(Perspective_arbitrary(Parse.bracket_innervalue,len))|Error_whennot(Parse.is_bracket_valuevalue)->err_not_utility|Error_->(letinner=Parse.bracket_innervalueinmatchParse.arbitrary_declaration_valueinnerwith|Somev->Ok(Perspective_raw(inner,v))|None->err_not_utility))|["perspective";"origin";"center"]->OkPerspective_origin_center|["perspective";"origin";"top"]->OkPerspective_origin_top|["perspective";"origin";"bottom"]->OkPerspective_origin_bottom|["perspective";"origin";"left"]->OkPerspective_origin_left|["perspective";"origin";"right"]->OkPerspective_origin_right|["perspective";"origin";"top";"left"]->OkPerspective_origin_top_left|["perspective";"origin";"top";"right"]->OkPerspective_origin_top_right|["perspective";"origin";"bottom";"left"]->OkPerspective_origin_bottom_left|["perspective";"origin";"bottom";"right"]->OkPerspective_origin_bottom_right|"perspective"::"origin"::restwhenrest<>[]->letvalue=String.concat"-"restinifParse.is_bracket_valuevaluethenletinner=Parse.bracket_innervalueinmatcharbitrary_valueCss.Properties.read_perspective_origininnerwith|Somepo->Ok(Perspective_origin_arbitrary(inner,po))|None->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Perspective_origin_raw(inner,value))|None->Error(`Msg("perspective-origin-["^inner^"]: not a valid perspective-origin")))elseerr_not_utility|["transform";"style";"3d"]->OkTransform_style_3d|["transform";"style";"flat"]->OkTransform_style_flat|["transform";"3d"]->OkTransform_style_3d|["transform";"flat"]->OkTransform_style_flat|["backface";"hidden"]->OkBackface_hidden|["backface";"visible"]->OkBackface_visible|["transform";"border"]->OkTransform_box_border|["transform";"content"]->OkTransform_box_content|["transform";"fill"]->OkTransform_box_fill|["transform";"stroke"]->OkTransform_box_stroke|["transform";"view"]->OkTransform_box_view|["transform";value]whenParse.is_bracket_valuevalue->(letinner=Parse.bracket_innervalueinmatcharbitrary_valueCss.Properties.read_transformsinnerwith|Somets->Ok(Transform_arbitrary(inner,ts))|None->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Transform_raw(inner,value))|None->Error(`Msg("transform-["^inner^"]: not a valid transform list"))))|["transform"]->OkTransform|["transform";"cpu"]->OkTransform_cpu|["transform";"none"]->OkTransform_none|["transform";"gpu"]->OkTransform_gpu|["origin";"center"]->OkOrigin_center|["origin";"top"]->OkOrigin_top|["origin";"bottom"]->OkOrigin_bottom|["origin";"left"]->OkOrigin_left|["origin";"right"]->OkOrigin_right|["origin";"top";"left"]->OkOrigin_top_left|["origin";"top";"right"]->OkOrigin_top_right|["origin";"bottom";"left"]->OkOrigin_bottom_left|["origin";"bottom";"right"]->OkOrigin_bottom_right|"origin"::restwhenrest<>[]->letvalue=String.concat"-"restinifParse.is_bracket_valuevaluethenletinner=Parse.bracket_innervalueinmatcharbitrary_valueCss.Properties.read_transform_origininnerwith|Somet->Ok(Origin_arbitrary(inner,t))|None->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Origin_raw(inner,value))|None->Error(`Msg("origin-["^inner^"]: not a valid transform-origin")))elseerr_not_utility|_->err_not_utility(* [translate-x-0.5], and [-translate-x-0.5] for the negative step. Tailwind
writes the fraction as the author did, so keep the trailing digits. *)letstep_classprefixf=(iff<0.then"-"else"")^prefix^"-"^pp_float(Float.absf)letto_class=function|Rotaten->neg_class"rotate-"n|Rotate_arbitrary(raw,_)->"rotate-"^"["^raw^"]"|Rotate_raw(raw,_)->"rotate-["^raw^"]"|Rotate_none->"rotate-none"|Rotate_3d_arbitrary(raw,_,_,_,_)->"rotate-["^raw^"]"|Rotate_bare_varname->"rotate-("^name^")"|Neg_rotate_bare_varname->"-rotate-("^name^")"|Neg_rotate_arbitrarys->"-rotate-["^s^"]"|Translate_xn->neg_class"translate-x-"n|Translate_x_full->"translate-x-full"|Translate_x_px->"translate-x-px"|Translate_x_stepf->step_class"translate-x"f|Translate_x_arbitrary(raw,_)|Translate_x_raw(raw,_)->"translate-x-["^raw^"]"|Translate_y_raw(raw,_)->"translate-y-["^raw^"]"|Translate_raw(raw,_)->"translate-["^raw^"]"|Translate_x_fraction(num,denom)->"translate-x-"^string_of_intnum^"/"^string_of_intdenom|Translate_yn->neg_class"translate-y-"n|Translate_y_full->"translate-y-full"|Translate_y_px->"translate-y-px"|Translate_y_stepf->step_class"translate-y"f|Translate_y_arbitrary(raw,_)->"translate-y-["^raw^"]"|Translate_y_fraction(num,denom)->"translate-y-"^string_of_intnum^"/"^string_of_intdenom|Translate_zn->neg_class"translate-z-"n|Translate_z_stepf->step_class"translate-z"f|Translate_z_px->"translate-z-px"|Translate_z_arbitrary(raw,_)->"translate-z-["^raw^"]"|Neg_translate_z_arbitrarys->"-translate-z-["^s^"]"|Neg_translate_z_px->"-translate-z-px"|Translate_3d->"translate-3d"|Translaten->"translate-"^string_of_intn|Neg_translaten->"-translate-"^string_of_intn|Translate_full->"translate-full"|Translate_none->"translate-none"|Translate_px->"translate-px"|Translate_1_2->"translate-1/2"|Translate_fraction(num,denom)->"translate-"^string_of_intnum^"/"^string_of_intdenom|Translate_arbitrary(raw,_)->"translate-"^"["^raw^"]"|Neg_translate_arbitrarys->"-translate-["^s^"]"|Neg_translate_full->"-translate-full"|Neg_translate_px->"-translate-px"|Neg_translate_x_px->"-translate-x-px"|Neg_translate_y_px->"-translate-y-px"|Neg_translate_fraction(num,denom)->"-translate-"^string_of_intnum^"/"^string_of_intdenom|Neg_translate_x_arbitrarys->"-translate-x-["^s^"]"|Neg_translate_x_full->"-translate-x-full"|Neg_translate_x_1_2->"-translate-x-1/2"|Neg_translate_x_fraction(num,denom)->"-translate-x-"^string_of_intnum^"/"^string_of_intdenom|Neg_translate_y_arbitrarys->"-translate-y-["^s^"]"|Neg_translate_y_full->"-translate-y-full"|Neg_translate_y_1_2->"-translate-y-1/2"|Neg_translate_y_fraction(num,denom)->"-translate-y-"^string_of_intnum^"/"^string_of_intdenom|Scalen->neg_class"scale-"n|Scale_xn->neg_class"scale-x-"n|Scale_x_arbitrary(raw,_)->"scale-x-["^raw^"]"|Scale_yn->neg_class"scale-y-"n|Scale_y_arbitrary(raw,_)->"scale-y-["^raw^"]"|Scale_raw_1(raw,_)->"scale-["^raw^"]"|Scale_raw_3(raw,_,_,_)->"scale-["^raw^"]"|Scale_arbitrary_raw(raw,_)->"scale-["^raw^"]"|Scale_zn->neg_class"scale-z-"n|Scale_z_arbitrarys->"scale-z-["^s^"]"|Neg_scale_arbitrarys->"-scale-["^s^"]"|Neg_scale_x_arbitrarys->"-scale-x-["^s^"]"|Neg_scale_y_arbitrarys->"-scale-y-["^s^"]"|Neg_scale_z_arbitrarys->"-scale-z-["^s^"]"|Scale_3d->"scale-3d"|Scale_none->"scale-none"|Skew_xn->neg_class"skew-x-"n|Skew_x_arbitrary(raw,_)->"skew-x-"^"["^raw^"]"|Skew_yn->neg_class"skew-y-"n|Skew_y_arbitrary(raw,_)->"skew-y-"^"["^raw^"]"|Skewn->neg_class"skew-"n|Skew_arbitrary(raw,_)->"skew-"^"["^raw^"]"|Neg_skew_arbitrarys->"-skew-["^s^"]"|Neg_skew_x_arbitrarys->"-skew-x-["^s^"]"|Neg_skew_y_arbitrarys->"-skew-y-["^s^"]"|Rotate_xn->neg_class"rotate-x-"n|Rotate_x_arbitrary(raw,_)|Rotate_x_raw(raw,_)->"rotate-x-["^raw^"]"|Rotate_y_raw(raw,_)->"rotate-y-["^raw^"]"|Rotate_z_raw(raw,_)->"rotate-z-["^raw^"]"|Rotate_x_bare_varname->"rotate-x-("^name^")"|Neg_rotate_x_bare_varname->"-rotate-x-("^name^")"|Neg_rotate_x_arbitrarys->"-rotate-x-["^s^"]"|Rotate_yn->neg_class"rotate-y-"n|Rotate_y_arbitrary(raw,_)->"rotate-y-"^"["^raw^"]"|Rotate_y_bare_varname->"rotate-y-("^name^")"|Neg_rotate_y_bare_varname->"-rotate-y-("^name^")"|Neg_rotate_y_arbitrarys->"-rotate-y-["^s^"]"|Rotate_zn->neg_class"rotate-z-"n|Rotate_z_arbitrary(raw,_)->"rotate-z-"^"["^raw^"]"|Rotate_z_bare_varname->"rotate-z-("^name^")"|Neg_rotate_z_bare_varname->"-rotate-z-("^name^")"|Neg_rotate_z_arbitrarys->"-rotate-z-["^s^"]"|Perspective_none->"perspective-none"|Perspective_dramatic->"perspective-dramatic"|Perspective_themename->"perspective-"^name|Perspective_near->"perspective-near"|Perspective_normal->"perspective-normal"|Perspective_midrange->"perspective-midrange"|Perspective_distant->"perspective-distant"|Perspective_arbitrary(raw,_)|Perspective_raw(raw,_)->"perspective-["^raw^"]"|Perspective_origin_center->"perspective-origin-center"|Perspective_origin_top->"perspective-origin-top"|Perspective_origin_bottom->"perspective-origin-bottom"|Perspective_origin_left->"perspective-origin-left"|Perspective_origin_right->"perspective-origin-right"|Perspective_origin_top_left->"perspective-origin-top-left"|Perspective_origin_top_right->"perspective-origin-top-right"|Perspective_origin_bottom_left->"perspective-origin-bottom-left"|Perspective_origin_bottom_right->"perspective-origin-bottom-right"|Perspective_origin_arbitrary(s,_)|Perspective_origin_raw(s,_)->"perspective-origin-["^s^"]"|Transform_style_3d->"transform-3d"|Transform_style_flat->"transform-flat"|Transform_box_border->"transform-border"|Transform_box_content->"transform-content"|Transform_box_fill->"transform-fill"|Transform_box_stroke->"transform-stroke"|Transform_box_view->"transform-view"|Backface_visible->"backface-visible"|Backface_hidden->"backface-hidden"|Transform->"transform"|Transform_cpu->"transform-cpu"|Transform_none->"transform-none"|Transform_gpu->"transform-gpu"|Transform_arbitrary(s,_)|Transform_raw(s,_)->"transform-["^s^"]"|Origin_center->"origin-center"|Origin_top->"origin-top"|Origin_bottom->"origin-bottom"|Origin_left->"origin-left"|Origin_right->"origin-right"|Origin_top_left->"origin-top-left"|Origin_top_right->"origin-top-right"|Origin_bottom_left->"origin-bottom-left"|Origin_bottom_right->"origin-bottom-right"|Origin_arbitrary(s,_)|Origin_raw(s,_)->"origin-["^s^"]"letexamples=[Origin_top;Perspective_none;Perspective_origin_top;Backface_hidden;Transform_none;Transform_style_flat;Rotate45;Scale50;Translate_x2;]endopenHandlermoduleUtility_factory=Utility.Make(Handler)(** Register the transform utility handlers *)(** Public API returning Utility.t *)letutility=Utility_factory.vletrotaten=utility(Rotaten)lettranslate_xn=utility(Translate_xn)(* A whole-number float keeps the int constructor's own 0/1 shortcuts
(calc(var(--spacing)) rather than calc(var(--spacing) * 1)); only a genuine
fractional value needs the step form. *)lettranslate_x'f=ifFloat.is_integerfthentranslate_x(int_of_floatf)elseutility(Translate_x_stepf)lettranslate_x_fractionnumdenom=utility(Translate_x_fraction(num,denom))lettranslate_yn=utility(Translate_yn)lettranslate_y'f=ifFloat.is_integerfthentranslate_y(int_of_floatf)elseutility(Translate_y_stepf)lettranslate_y_fractionnumdenom=utility(Translate_y_fraction(num,denom))letscalen=utility(Scalen)letscale_xn=utility(Scale_xn)letscale_yn=utility(Scale_yn)letskew_xn=utility(Skew_xn)letskew_yn=utility(Skew_yn)lettranslate_zn=utility(Translate_zn)lettranslate_z'f=ifFloat.is_integerfthentranslate_z(int_of_floatf)elseutility(Translate_z_stepf)letrotate_xn=utility(Rotate_xn)letrotate_yn=utility(Rotate_yn)letrotate_zn=utility(Rotate_zn)letscale_zn=utility(Scale_zn)letperspective_none=utilityPerspective_noneletperspective_dramatic=utilityPerspective_dramaticletperspective_near=utilityPerspective_nearletperspective_normal=utilityPerspective_normalletperspective_midrange=utilityPerspective_midrangeletperspective_distant=utilityPerspective_distantletneg_translate_x_1_2=utilityNeg_translate_x_1_2letneg_translate_y_1_2=utilityNeg_translate_y_1_2letperspective_origin_center=utilityPerspective_origin_centerletperspective_origin_top=utilityPerspective_origin_topletperspective_origin_bottom=utilityPerspective_origin_bottomletperspective_origin_left=utilityPerspective_origin_leftletperspective_origin_right=utilityPerspective_origin_rightlettransform_style_3d=utilityTransform_style_3dlettransform_style_flat=utilityTransform_style_flatlettransform_box_border=utilityTransform_box_borderlettransform_box_content=utilityTransform_box_contentlettransform_box_fill=utilityTransform_box_filllettransform_box_stroke=utilityTransform_box_strokelettransform_box_view=utilityTransform_box_viewletbackface_visible=utilityBackface_visibleletbackface_hidden=utilityBackface_hiddenlettransform=utilityTransformlettransform_none=utilityTransform_nonelettransform_gpu=utilityTransform_gpuletorigin_center=utilityOrigin_centerletorigin_top=utilityOrigin_topletorigin_bottom=utilityOrigin_bottomletorigin_left=utilityOrigin_leftletorigin_right=utilityOrigin_rightletorigin_top_left=utilityOrigin_top_leftletorigin_top_right=utilityOrigin_top_rightletorigin_bottom_left=utilityOrigin_bottom_leftletorigin_bottom_right=utilityOrigin_bottom_right