1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907(** Typography utilities for text and font styling. *)moduleCss=Cascade.Css(* Text size variables with line heights *)lettext_xs_var=Var.themeCss.Length"text-xs"~order:(6,0)lettext_xs_lh_var=Var.themeCss.Line_height"text-xs--line-height"~order:(6,1)lettext_sm_var=Var.themeCss.Length"text-sm"~order:(6,2)lettext_sm_lh_var=Var.themeCss.Line_height"text-sm--line-height"~order:(6,3)lettext_base_var=Var.themeCss.Length"text-base"~order:(6,4)lettext_base_lh_var=Var.themeCss.Line_height"text-base--line-height"~order:(6,5)lettext_lg_var=Var.themeCss.Length"text-lg"~order:(6,6)lettext_lg_lh_var=Var.themeCss.Line_height"text-lg--line-height"~order:(6,7)lettext_xl_var=Var.themeCss.Length"text-xl"~order:(6,8)lettext_xl_lh_var=Var.themeCss.Line_height"text-xl--line-height"~order:(6,9)lettext_2xl_var=Var.themeCss.Length"text-2xl"~order:(6,10)lettext_2xl_lh_var=Var.themeCss.Line_height"text-2xl--line-height"~order:(6,11)lettext_3xl_var=Var.themeCss.Length"text-3xl"~order:(6,12)lettext_3xl_lh_var=Var.themeCss.Line_height"text-3xl--line-height"~order:(6,13)lettext_4xl_var=Var.themeCss.Length"text-4xl"~order:(6,14)lettext_4xl_lh_var=Var.themeCss.Line_height"text-4xl--line-height"~order:(6,15)lettext_5xl_var=Var.themeCss.Length"text-5xl"~order:(6,16)lettext_5xl_lh_var=Var.themeCss.Line_height"text-5xl--line-height"~order:(6,17)lettext_6xl_var=Var.themeCss.Length"text-6xl"~order:(6,18)lettext_6xl_lh_var=Var.themeCss.Line_height"text-6xl--line-height"~order:(6,19)lettext_7xl_var=Var.themeCss.Length"text-7xl"~order:(6,20)lettext_7xl_lh_var=Var.themeCss.Line_height"text-7xl--line-height"~order:(6,21)lettext_8xl_var=Var.themeCss.Length"text-8xl"~order:(6,22)lettext_8xl_lh_var=Var.themeCss.Line_height"text-8xl--line-height"~order:(6,23)lettext_9xl_var=Var.themeCss.Length"text-9xl"~order:(6,24)lettext_9xl_lh_var=Var.themeCss.Line_height"text-9xl--line-height"~order:(6,25)letfont_weight_thin_var=Var.themeCss.Font_weight"font-weight-thin"~order:(6,30)letfont_weight_extralight_var=Var.themeCss.Font_weight"font-weight-extralight"~order:(6,31)letfont_weight_light_var=Var.themeCss.Font_weight"font-weight-light"~order:(6,32)letfont_weight_normal_var=Var.themeCss.Font_weight"font-weight-normal"~order:(6,33)letfont_weight_medium_var=Var.themeCss.Font_weight"font-weight-medium"~order:(6,34)letfont_weight_semibold_var=Var.themeCss.Font_weight"font-weight-semibold"~order:(6,35)letfont_weight_bold_var=Var.themeCss.Font_weight"font-weight-bold"~order:(6,36)letfont_weight_extrabold_var=Var.themeCss.Font_weight"font-weight-extrabold"~order:(6,37)letfont_weight_black_var=Var.themeCss.Font_weight"font-weight-black"~order:(6,38)(* A [--font-weight-*] token the project declared names a weight the built-in
scale has no slot for; they share the slot after the scale. *)letfont_weight_named_cache=Domain_cache.v8letfont_weight_named_varname=Domain_cache.or_addfont_weight_named_cachename(fun()->Var.themeCss.Font_weight("font-weight-"^name)~order:(6,38))(* Theme variables for named tracking values *)lettracking_tighter_var=Var.themeCss.Length"tracking-tighter"~order:(6,39)lettracking_tight_var=Var.themeCss.Length"tracking-tight"~order:(6,40)lettracking_normal_var=Var.themeCss.Length"tracking-normal"~order:(6,41)lettracking_wide_var=Var.themeCss.Length"tracking-wide"~order:(6,42)lettracking_wider_var=Var.themeCss.Length"tracking-wider"~order:(6,43)lettracking_widest_var=Var.themeCss.Length"tracking-widest"~order:(6,44)(* A [--tracking-*] token the project declared names a letter spacing the
built-in scale has no slot for; they share the slot after the scale, the way
the project's font families and text sizes share theirs. *)lettracking_named_cache=Domain_cache.v8lettracking_named_varname=Domain_cache.or_addtracking_named_cachename(fun()->Var.themeCss.Length("tracking-"^name)~order:(6,45))(* Publish the weight and tracking scales through the theme-token registry, the
way rule.ml publishes the breakpoints, so [tracking-[theme(--tracking-wide)]]
resolves and [theme(static)] emits them. *)let()=List.iter(fun(var,weight)->Theme.register_defaultvarweight)[(font_weight_thin_var,(Weight100.:Css.font_weight));(font_weight_extralight_var,Weight200.);(font_weight_light_var,Weight300.);(font_weight_normal_var,Weight400.);(font_weight_medium_var,Weight500.);(font_weight_semibold_var,Weight600.);(font_weight_bold_var,Weight700.);(font_weight_extrabold_var,Weight800.);(font_weight_black_var,Weight900.);];List.iter(fun(var,spacing)->Theme.register_defaultvarspacing)[(tracking_tighter_var,(Em(-0.05):Css.length));(tracking_tight_var,Em(-0.025));(* Tailwind's default theme spells the normal tracking [0em], not a
unitless zero. *)(tracking_normal_var,Em0.0);(tracking_wide_var,Em0.025);(tracking_wider_var,Em0.05);(tracking_widest_var,Em0.1);](* Theme variables for named leading values *)letleading_none_var=Var.themeCss.Line_height"leading-none"~order:(6,47)letleading_tight_var=Var.themeCss.Line_height"leading-tight"~order:(6,48)letleading_snug_var=Var.themeCss.Line_height"leading-snug"~order:(6,49)letleading_normal_var=Var.themeCss.Line_height"leading-normal"~order:(6,50)letleading_relaxed_var=Var.themeCss.Line_height"leading-relaxed"~order:(6,51)letleading_loose_var=Var.themeCss.Line_height"leading-loose"~order:(6,52)letunderline_offset_auto_var=Var.themeCss.Length"text-underline-offset-auto"~order:(6,55)letleading_var=(* Leading appears in @layer properties after transforms (position 11) *)Var.channel~needs_property:true~property_order:11~family:`LeadingCss.Line_height"tw-leading"letfont_weight_var=(* Font-weight appears in @layer properties after leading (position 12) *)Var.channel~needs_property:true~property_order:12~family:`Font_weightCss.Font_weight"tw-font-weight"lettracking_var=(* Tracking doesn't appear in @layer properties in standard Tailwind - only
referenced *)Var.channel~needs_property:true~property_order:101~family:`TrackingCss.Length"tw-tracking"letordinal_var=Var.channel~needs_property:true~property_order:102Css.Font_variant_numeric_token"tw-ordinal"letslashed_var=Var.channel~needs_property:true~property_order:103Css.Font_variant_numeric_token"tw-slashed-zero"letfigure_var=Var.channel~needs_property:true~property_order:104Css.Font_variant_numeric_token"tw-numeric-figure"letspacing_var=Var.channel~needs_property:true~property_order:105Css.Font_variant_numeric_token"tw-numeric-spacing"letfraction_var=Var.channel~needs_property:true~property_order:106Css.Font_variant_numeric_token"tw-numeric-fraction"(* Theme record for line height variables *)typeline_height_theme={leading:Css.declaration*Css.line_heightCss.var}(* Default line height theme with empty fallback for Tailwind's var(--name,)
pattern *)letdefault_line_height_theme:line_height_theme=letleading_decl,leading_ref=Var.bindingleading_var~fallback:Css.Empty(Num1.5)in{leading=(leading_decl,leading_ref)}(* Content variable *)letcontent_var=Var.property_defaultContent~initial:(String"")~universal:true~property_order:100~family:`Content"tw-content"(* Default theme for font variant numeric variables *)typefont_variant_theme={ordinal:Css.declaration*Css.font_variant_numeric_tokenCss.var;slashed:Css.declaration*Css.font_variant_numeric_tokenCss.var;figure:Css.declaration*Css.font_variant_numeric_tokenCss.var;spacing:Css.declaration*Css.font_variant_numeric_tokenCss.var;fraction:Css.declaration*Css.font_variant_numeric_tokenCss.var;}letdefault_font_variant_theme:font_variant_theme=letordinal_decl,ordinal_ref=Var.bindingordinal_var~fallback:Css.EmptyNormalinletslashed_decl,slashed_ref=Var.bindingslashed_var~fallback:Css.EmptyNormalinletfigure_decl,figure_ref=Var.bindingfigure_var~fallback:Css.EmptyNormalinletspacing_decl,spacing_ref=Var.bindingspacing_var~fallback:Css.EmptyNormalinletfraction_decl,fraction_ref=Var.bindingfraction_var~fallback:Css.EmptyNormalin{ordinal=(ordinal_decl,ordinal_ref);slashed=(slashed_decl,slashed_ref);figure=(figure_decl,figure_ref);spacing=(spacing_decl,spacing_ref);fraction=(fraction_decl,fraction_ref);}(* Font family theme variables. A project [@theme] can name families of its own
([--font-source]); order (1, 100) puts those after the three built-in
stacks. *)letfont_named_cache=Domain_cache.v8letfont_named_varname=Domain_cache.or_addfont_named_cachename(fun()->Var.themeCss.Font_family("font-"^name)~order:(1,100))(* The [--font-<name>--font-feature-settings] and [--font-variation-settings]
tokens a project declares beside a family, each after the family. *)letfont_named_features_cache=Domain_cache.v8letfont_named_variations_cache=Domain_cache.v8letfont_named_features_varname=Domain_cache.or_addfont_named_features_cachename(fun()->Var.themeCss.Font_feature_settings("font-"^name^"--font-feature-settings")~order:(1,101))letfont_named_variations_varname=Domain_cache.or_addfont_named_variations_cachename(fun()->Var.themeCss.Font_variation_settings("font-"^name^"--font-variation-settings")~order:(1,101))(* A [--text-*] token the project declared names a font size the built-in scale
has no slot for; they share the slot after the scale, the way the project's
font families share theirs. *)lettext_named_cache=Domain_cache.v8lettext_named_varname=Domain_cache.or_addtext_named_cachename(fun()->Var.themeCss.Length("text-"^name)~order:(6,26))(* The [--text-<name>--line-height], [--letter-spacing] and [--font-weight]
tokens a project declares beside a size of its own, each after the size. *)lettext_named_lh_cache=Domain_cache.v8lettext_named_tracking_cache=Domain_cache.v8lettext_named_weight_cache=Domain_cache.v8lettext_named_lh_varname=Domain_cache.or_addtext_named_lh_cachename(fun()->Var.themeCss.Line_height("text-"^name^"--line-height")~order:(6,27))lettext_named_tracking_varname=Domain_cache.or_addtext_named_tracking_cachename(fun()->Var.themeCss.Length("text-"^name^"--letter-spacing")~order:(6,27))lettext_named_weight_varname=Domain_cache.or_addtext_named_weight_cachename(fun()->Var.themeCss.Font_weight("text-"^name^"--font-weight")~order:(6,27))(* The same for a [--leading-*] token the project declared. *)letleading_named_cache=Domain_cache.v8letleading_named_varname=Domain_cache.or_addleading_named_cachename(fun()->Var.themeCss.Line_height("leading-"^name)~order:(6,53))letfont_sans_var=Var.themeCss.Font_family"font-sans"~order:(1,0)letfont_serif_var=Var.themeCss.Font_family"font-serif"~order:(1,1)letfont_mono_var=Var.themeCss.Font_family"font-mono"~order:(1,2)(* Default font family variables that reference the base font variables *)letdefault_font_family_var=Var.themeCss.Font_family"default-font-family"~order:(8,39)letdefault_mono_font_family_var=Var.themeCss.Font_family"default-mono-font-family"~order:(8,42)(* Tailwind v4.3.2 replaced the [ui-sans-serif, system-ui, ...] default with the
platform system-font stack. *)letdefault_sans_stack:Css.font_family=List[Name"-apple-system";Name"BlinkMacSystemFont";Segoe_ui;Roboto;Helvetica_neue;Noto_sans;Arial;Sans_serif;Apple_color_emoji;Segoe_ui_emoji;Segoe_ui_symbol;Noto_color_emoji;]letdefault_mono_stack:Css.font_family=List[Ui_monospace;SFMono_regular;Menlo;Monaco;Consolas;Liberation_mono;Courier_new;Monospace;]letdefault_serif_stack:Css.font_family=List[Ui_serif;Georgia;Cambria;Times_new_roman;Times;Serif](* Published the same way, so [font-[theme(--font-mono)]] resolves. *)let()=List.iter(fun(var,stack)->Theme.register_defaultvarstack)[(font_sans_var,default_sans_stack);(font_serif_var,default_serif_stack);(font_mono_var,default_mono_stack);](* Base font family variables for theme layer *)letdefault_font_declarations=letsans_decl=Var.setfont_sans_vardefault_sans_stackinletmono_decl=Var.setfont_mono_vardefault_mono_stackin[sans_decl;mono_decl](* Default font family variables that reference the base font variables. *)letdefault_font_family_declarations=letsans_decl,sans_ref=Var.bindingfont_sans_vardefault_sans_stackinletmono_decl,mono_ref=Var.bindingfont_mono_vardefault_mono_stackinletdefault_font_decl=Var.setdefault_font_family_var(Css.Varsans_ref)inletdefault_mono_decl=Var.setdefault_mono_font_family_var(Css.Varmono_ref)in[sans_decl;mono_decl;default_font_decl;default_mono_decl](* CSS Fonts 4 sec. 6.4: [font-feature-settings] is [normal] or a comma list of
[<opentype-tag> [<integer> | on | off]?], the tag being a quoted
four-character string. The docs' [<value>] placeholder is neither. *)(** Early typography handler - comes before color utilities (priority 22) *)letis_feature_tag_lists=lets=Parse.decode_underscoressinletentrye=matchString.split_on_char' '(String.trime)|>List.filter((<>)"")with|[]->false|tag::rest->(letn=String.lengthtaginn=6&&(tag.[0]='"'||tag.[0]='\'')&&tag.[n-1]=tag.[0]&&matchrestwith|[]->true|[x]->x="on"||x="off"||int_of_string_optx<>None|_->false)inString.trims="normal"||(s<>""&&List.for_allentry(String.split_on_char','s))(* CSS Fonts 4 sec. 2.2: a family name is an identifier sequence or a quoted
string, and the list is comma-separated. *)letis_font_family_values=letentrye=lete=String.trimeinletn=String.lengtheinifn=0thenfalseelseif(e.[0]='"'||e.[0]='\'')&&n>1thene.[n-1]=e.[0]elseString.split_on_char' 'e|>List.filter(funw->w<>"")|>funws->ws<>[]&&List.for_allParse.is_identwsins<>""&&List.for_allentry(String.split_on_char','s)moduleTypography_early=structopenStyleopenCsstypet=|(* Text sizes *)Text_xs|Text_sm|Text_base|Text_lg|Text_xl|Text_2xl|Text_3xl|Text_4xl|Text_5xl|Text_6xl|Text_7xl|Text_8xl|Text_9xl|(* Font weights *)Font_thin|Font_extralight|Font_light|Font_normal|Font_medium|Font_semibold|Font_bold|Font_extrabold|Font_black|(* Arbitrary font values - store (bracket_content, parsed_value) *)Font_bracket_weightofstring*int(* font-[100] *)|Font_bracket_weight_varofstring*string(* font-[number:var(--x)] or font-[var(--x)] *)|Font_bracket_family_quotedofstring*string(* font-["arial_rounded"] *)|Font_bracket_family_nameofstring*string(* font-[ui-sans-serif] *)|Font_bracket_family_varofstring*string(* font-[family-name:var(--x)] or font-[generic-name:var(--x)] *)|(* Font feature settings *)Font_features_quotedofstring(* font-features-["smcp"] *)|Font_features_rawofstring*string(* Each of these families writes one longhand, so a bracket no reader took
still names it and the value is forwarded verbatim - Tailwind's
token-stream contract. *)|Font_features_varofstring(* font-features-[var(--x)] *)|Font_features_bare_varofstring(* font-features-(--my-var) *)|(* Font families. The flag records whether the theme adds feature
settings, which gives the candidate a wider property set. *)Font_sansofbool|Font_serifofbool|Font_monoofbool|Font_namedofstring*bool(* font-source, from a --font-* theme token *)|Font_weight_themeofstring(* weight from a --font-weight-* token *)|Leading_themeofstring(* line height from a --leading-* token *)|(* Font styles *)Italic|Not_italic|(* Text alignment *)Text_left|Text_center|Text_right|Text_justify|Text_start|Text_end|(* Leading *)Leading_none|Leading_tight|Leading_snug|Leading_normal|Leading_relaxed|Leading_loose|Leadingofint|Leading_stepoffloat(* leading-0.5, a half-step scale value *)|Leading_varofstring(* leading-[var(--value)] *)(* The author's bracket text travels with the line-height it denotes, so the
class name is spelled exactly as it was written. *)|Leading_bracketofstring*Css.line_height(* leading-[1.8], leading-[24px], etc. *)|Leading_rawofstring*string|(* Font-size with explicit line-height modifier (text-sm/6) *)Text_named_lhofstring*lh_modifier|(* Font size named by a project [--text-*] token (text-huge) *)Text_themeofstring|Text_theme_lhofstring*lh_modifier|(* Arbitrary font-size (text-[12px]) *)Text_bracket_fsofstring|(* Arbitrary font-size with line-height (text-[12px]/6) *)Text_bracket_fs_lhofstring*lh_modifier|(* A font-size hint over a value no font-size grammar reads
(text-[length:red]). The hint chooses the longhand and the value is
forwarded verbatim, which is Tailwind's token-stream contract; the
browser discards the declaration. *)Text_bracket_fs_rawofstring*stringandlh_modifier=|Spacingofint(* /6 → calc(var(--spacing) * 6) *)|No_leading(* /none → line-height: 1 *)|Namedofstring(* /snug → var(--leading-snug) *)|Bracketofstring*Css.line_height(* /[4px] → 4px *)|Var_shorthandofstring(* /(--lh) → var(--lh) *)letname="typography_early"(* Most early-typography families (text-align, font, leading) sort at priority
24. font-style (italic) is canonically later (rank 87, after tracking/
whitespace/text-transform), so it joins the late handler's band at priority
26 via a suborder just above text-transform (~8360). *)letpriority=functionItalic|Not_italic->26|_->24leterr_not_utility=Error(`Msg"Not an early typography utility")(* Lookup table for named text sizes: (name, size_var, default_rem) *)lettext_size_data=[("xs",text_xs_var,0.75);("sm",text_sm_var,0.875);("base",text_base_var,1.0);("lg",text_lg_var,1.125);("xl",text_xl_var,1.25);("2xl",text_2xl_var,1.5);("3xl",text_3xl_var,1.875);("4xl",text_4xl_var,2.25);("5xl",text_5xl_var,3.0);("6xl",text_6xl_var,3.75);("7xl",text_7xl_var,4.5);("8xl",text_8xl_var,6.0);("9xl",text_9xl_var,8.0);](* The acceptor for [text-<size>] reads the converter's own table: the two
have to hold the same names, and a name in one but not the other is an
accepted class that emits no declarations at all. *)(* The line-height each text size carries, which its [text-*] utility and its
[--text-*--line-height] token both spell. *)lettext_line_heights:(string*Css.line_height)list=[("xs",Rem1.0);("sm",Rem1.25);("base",Rem1.5);("lg",Rem1.75);("xl",Rem1.75);("2xl",Rem2.0);("3xl",Rem2.25);("4xl",Rem2.5);("5xl",Num1.0);("6xl",Num1.0);("7xl",Num1.0);("8xl",Num1.0);("9xl",Num1.0);]letline_height_ofsize=List.assocsizetext_line_heightsletnamed_sizes=List.map(fun(name,_,_)->name)text_size_dataletis_named_sizes=List.memsnamed_sizes(** Font-size keyword lookup. *)letfont_size_keyword=function|"larger"->Stdlib.Option.SomeCss.Larger|"smaller"->Stdlib.Option.SomeCss.Smaller|"xx-large"->Stdlib.Option.SomeCss.Xx_large|"x-large"->Stdlib.Option.SomeCss.X_large|"large"->Stdlib.Option.SomeCss.Large|"medium"->Stdlib.Option.SomeCss.Medium|"small"->Stdlib.Option.SomeCss.Small|"x-small"->Stdlib.Option.SomeCss.X_small|"xx-small"->Stdlib.Option.SomeCss.Xx_small|"xxx-large"->Stdlib.Option.SomeCss.Xxx_large|_->Stdlib.Option.None(** A bracket font-size is any CSS length, but it has to carry a unit:
Tailwind reads the unitless zero of [text-[0]] as a colour, not a size. *)lettry_parse_length_values=matchParse.arbitrary_lengthswith|Stdlib.Option.SomeCss.Zero->Stdlib.Option.None|len->len(** Split a type prefix like "absolute-size:var(--my-size)" into (prefix,
value). Finds first ':' outside parens/brackets. *)letsplit_type_prefixinner=letlen=String.lengthinnerinletrecfindidepth=ifi>=lenthenStdlib.Option.Noneelsematchinner.[i]with|'('|'['->find(i+1)(depth+1)|')'|']'->find(i+1)(depth-1)|':'whendepth=0->Stdlib.Option.Some(String.subinner0i,String.subinner(i+1)(len-i-1))|_->find(i+1)depthinfind00(* Tailwind's [--spacing(N)] inside an arbitrary value reads the spacing
scale: [text-[--spacing(2)]] is [font-size: calc(var(--spacing) * 2)]. *)(** Does [inner] look like something we can emit as a font-size? Accepts typed
prefix (length/percentage/absolute-size/relative-size), font-size keyword
(medium, xxx-large, larger, ...), any CSS length carrying a unit (16px,
1rem, 1.5em, 3ch, 100%, clamp(...), calc(...)), or var(...). Rejects bare
identifiers that aren't recognised keywords -- e.g. a bare "1A202C" is not
a valid font-size: a length must carry a unit and a hex color must start
with "#" (CSS Color §5.4.6). *)letparse_spacing_calls=Option.bind(Parse.call_body"--spacing"s)float_of_string_opt(* A data-type hint in front of an arbitrary font size says how to read the
value written after it, so [text-[length:1rem]] is the size [1rem] and not
a reference to a custom property called [--1rem]. *)letis_font_size_hint=function|"absolute-size"|"relative-size"|"length"|"percentage"->true|_->false(* The value a bracket font size carries, told apart from the hint that
introduces it. *)letbracket_font_size_valueinner=matchsplit_type_prefixinnerwith|Stdlib.Option.Some(prefix,value)whenis_font_size_hintprefix->(value,true)|_->(inner,false)(* A hint settles the ambiguity [try_parse_length_value] guards against, so a
hinted [0] is the size zero where a bare one is a colour. A bare number
after the hint goes through Tailwind's generator as written and its
minifier reads it as pixels, which is the spelling [--diff] compares and
the one [stroke-[1.5]] already follows; see
docs/token-stream-contract.md. *)letbracket_font_size_lengthvalue~hinted=ifhintedthenmatchParse.arbitrary_lengthvaluewith|Stdlib.Option.Some_aslen->len|Stdlib.Option.None->(matchfloat_of_string_optvaluewith|Somef->Stdlib.Option.Some(Css.Pxf)|None->Stdlib.Option.None)elsetry_parse_length_valuevalue(* A [length:] hint says the bracket is a font size whatever the value turns
out to be, so a value no font-size grammar reads is still a font size -
forwarded verbatim under the token-stream contract. Without the hint the
same text names no utility, because nothing says which longhand it is
for. *)lethinted_font_size_token_streaminner=matchsplit_type_prefixinnerwith|Stdlib.Option.Some(prefix,_)whenis_font_size_hintprefix->Parse.arbitrary_declaration_valueinner|_->Stdlib.Option.Noneletis_valid_bracket_font_size(inner:string):bool=letvalue,hinted=bracket_font_size_valueinnerinmatchfont_size_keywordvaluewith|Some_->true|Stdlib.Option.None->bracket_font_size_lengthvalue~hinted<>Stdlib.Option.None||Parse.is_varvalue||parse_spacing_callvalue<>Stdlib.Option.None(** Check if bracket content looks like a color (should go to color handler).
*)letis_color_bracketinner=(* Hex color *)(String.lengthinner>0&&inner.[0]='#')(* color: typed prefix *)||String.starts_with~prefix:"color:"inner(* bare var() without type prefix defaults to color *)||Parse.is_varinner(* CSS color functions like rgba(...), hsl(...), oklch(...) *)||Parse.is_css_color_fninner(** Split on first '/' outside brackets/parens. *)letsplit_on_slashs=letlen=String.lengthsinletrecfindidepth=ifi>=lenthenStdlib.Option.Noneelsematchs.[i]with|'['|'('->find(i+1)(depth+1)|']'|')'->find(i+1)(depth-1)|'/'whendepth=0->Stdlib.Option.Some(String.subs0i,String.subs(i+1)(len-i-1))|_->find(i+1)depthinfind00(* The one reader for an arbitrary line-height, shared by [leading-[...]] and
by the [/leading] modifier on a text size. The string parsing is cascade's,
so every unit, [calc()] and [var()] spelling it reads is accepted; a
bracket it cannot read is not a line-height and the utility is refused. *)letparse_bracket_leadingraw:Css.line_heightoption=ifParse.is_varrawthenSome(Var(Var.bracketraw))elseletcursor=Cascade.Cursor.of_string(Parse.decode_arbitrary_valueraw)inmatchCascade.Cursor.try_parse_full_errCss.Properties.read_line_heightcursorwith(* The reader keeps a dimension whose unit it does not know as verbatim
text, so a stray source word reading as one would become a utility. The
length grammar is what says whether the unit is a length; asking it
keeps the answer out of a unit table here. *)|Ok(Number{repr;unit=Someunit;_}aslh)->ifCss.parse_length(repr^unit)=NonethenNoneelseSomelh|Oklh->Somelh|Error_->None(* [none] is not here: [parse_lh_modifier] answers it with [No_leading] before
this list is consulted, because Tailwind writes [line-height: 1] for it
rather than reading a [--leading-none] token it never defines. A row here
would contradict that the moment anyone removed the short-circuit. *)letknown_leading_names=["tight";"snug";"normal";"relaxed";"loose"](* The modifier names a [--leading-*] token, and one the project declared
counts the same as a built-in one. *)letparse_lh_modifierthemes=ifs="none"thenStdlib.Option.SomeNo_leadingelseifParse.is_bare_varsthen(* The [(--name)] shorthand reads the variable as the line height, the way
[[var(--name)]] does, and keeps its own spelling in the class. *)Stdlib.Option.Some(Var_shorthand(Parse.bare_var_inners))elseifParse.is_bracket_valuesthenletinner=Parse.bracket_innersinmatchparse_bracket_leadinginnerwith|Stdlib.Option.Somelh->Stdlib.Option.Some(Bracket(inner,lh))|Stdlib.Option.None->Stdlib.Option.NoneelsematchParse.decimal_intswith|Stdlib.Option.Somenwhenn>=0->Stdlib.Option.Some(Spacingn)|_->ifList.memsknown_leading_names||Scheme.theme_value(Sometheme)("leading-"^s)<>NonethenStdlib.Option.Some(Nameds)elseStdlib.Option.None(* A font family the project named in its [@theme]. Gated on the token being
there so a stray source word (font-awesome) is not a utility. *)(* A weight or line height the project named in its [@theme]. The built-in
scales are matched before these are consulted. *)letis_theme_font_weightthemen=Scheme.theme_value(Sometheme)("font-weight-"^n)<>None(* [leading-6] is the spacing-derived numeric form, whatever a project happens
to have declared, so a plain number is never a token name. *)letis_theme_leadingthemen=Parse.decimal_intn=None&&Scheme.theme_value(Sometheme)("leading-"^n)<>Noneletis_named_fontthemen=Scheme.theme_value(Sometheme)("font-"^n)<>Nonelethas_font_featuresthemen=Scheme.theme_value(Sometheme)("font-"^n^"--font-feature-settings")<>None(* A font size the project named in its [@theme]. The built-in scale is
published through the static token catalog, so exclude it: those names have
their own constructors. [text-<name>] also spells a colour, and a project
that declares both under one name gets the colour, as Tailwind does.
[--text-shadow-*] is a namespace of its own, and a doubled hyphen marks a
modifier on another token ([--text-lg--line-height]), so neither names a
size; nor does a token whose value is not a length. *)letis_theme_text_sizethemen=(not(is_named_sizen))&&(not(String.starts_with~prefix:"shadow-"n))&&List.for_all(funseg->seg<>"")(String.split_on_char'-'n)&&Scheme.theme_value(Sometheme)("color-"^n)=None&&matchScheme.theme_value(Sometheme)("text-"^n)with|None->false|Someraw->Css.parse_lengthraw<>Noneletof_classthemeclass_name=letparts=Parse.split_classclass_nameinmatchpartswith|["text";"xs"]->OkText_xs|["text";"sm"]->OkText_sm|["text";"base"]->OkText_base|["text";"lg"]->OkText_lg|["text";"xl"]->OkText_xl|["text";"2xl"]->OkText_2xl|["text";"3xl"]->OkText_3xl|["text";"4xl"]->OkText_4xl|["text";"5xl"]->OkText_5xl|["text";"6xl"]->OkText_6xl|["text";"7xl"]->OkText_7xl|["text";"8xl"]->OkText_8xl|["text";"9xl"]->OkText_9xl|["font";v]whenParse.is_bracket_valuev->(letinner=Parse.bracket_innervin(* A family name is a list of idents or quoted strings, so the docs'
[<value>] placeholder is not one. [_] separates the words of a name
and [\_] is a literal underscore inside one, so the stack is read
decoded while the class keeps the spelling it was written with. *)letfamilyrawvalue=ifParse.is_varvaluethenOk(Font_bracket_family_var(raw,value))elseletdecoded=Parse.decode_underscoresvalueinifis_font_family_valuedecodedthenOk(Font_bracket_family_name(raw,decoded))elseerr_not_utilityinletweightrawvalue=ifParse.is_varvaluethenOk(Font_bracket_weight_var(raw,value))elsematchParse.decimal_intvaluewith|Somen->Ok(Font_bracket_weight(raw,n))|None->err_not_utilityinifString.lengthinner>0&&inner.[0]='"'then(* font-["arial_rounded"] → the decoded bracket text becomes the
literal font-family value, quotes and all; Tailwind never wraps it
in another layer of quoting. *)letdecoded=Parse.decode_underscoresinnerinOk(Font_bracket_family_quoted(inner,decoded))else(* A data-type hint picks which of the two readings applies; the value
written after it is read the way the un-hinted spelling is, so only
a var() reference names a custom property. *)matchsplit_type_prefixinnerwith|Stdlib.Option.Some(("family-name"|"generic-name"),value)->familyinnervalue|Stdlib.Option.Some("number",value)->weightinnervalue|_->ifParse.decimal_intinner<>None||Parse.is_varinnerthenweightinnerinnerelsefamilyinnerinner)|["font";"features";v]whenParse.is_bracket_valuev->(letinner=Parse.bracket_innervinifParse.is_varinnerthenOk(Font_features_varinner)elseifis_feature_tag_listinnerthenOk(Font_features_quotedinner)elsematchParse.arbitrary_declaration_valueinnerwith|Someraw->Ok(Font_features_raw(inner,raw))|None->err_not_utility)|["font";"features";v]whenParse.is_bare_varv->Ok(Font_features_bare_varv)|["font";"thin"]->OkFont_thin|["font";"extralight"]->OkFont_extralight|["font";"light"]->OkFont_light|["font";"normal"]->OkFont_normal|["font";"medium"]->OkFont_medium|["font";"semibold"]->OkFont_semibold|["font";"bold"]->OkFont_bold|["font";"extrabold"]->OkFont_extrabold|["font";"black"]->OkFont_black|["font";"sans"]->Ok(Font_sans(has_font_featurestheme"sans"))|["font";"serif"]->Ok(Font_serif(has_font_featurestheme"serif"))|["font";"mono"]->Ok(Font_mono(has_font_featurestheme"mono"))|"font"::(_::_asrest)whenis_named_fonttheme(String.concat"-"rest)->letname=String.concat"-"restinOk(Font_named(name,has_font_featuresthemename))|"font"::(_::_asrest)whenis_theme_font_weighttheme(String.concat"-"rest)->Ok(Font_weight_theme(String.concat"-"rest))|["italic"]->OkItalic|["not";"italic"]->OkNot_italic|["text";"left"]->OkText_left|["text";"center"]->OkText_center|["text";"right"]->OkText_right|["text";"justify"]->OkText_justify|["text";"start"]->OkText_start|["text";"end"]->OkText_end|["leading";"none"]->OkLeading_none|["leading";"tight"]->OkLeading_tight|["leading";"snug"]->OkLeading_snug|["leading";"normal"]->OkLeading_normal|["leading";"relaxed"]->OkLeading_relaxed|["leading";"loose"]->OkLeading_loose|["leading";v]whenParse.is_bracket_valuev->(letinner=Parse.bracket_innervinifParse.is_varinnerthenOk(Leading_varinner)elsematchparse_bracket_leadinginnerwith|Stdlib.Option.Somelh->Ok(Leading_bracket(inner,lh))|Stdlib.Option.None->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Leading_raw(inner,value))|None->err_not_utility))|"leading"::(_::_asrest)whenis_theme_leadingtheme(String.concat"-"rest)->Ok(Leading_theme(String.concat"-"rest))|["leading";n]->((* Tailwind accepts any non-negative leading-N (or a quarter-step
leading-N.M), derived from spacing. *)matchParse.spacing_value~name:"leading"nwith|OkfwhenFloat.is_integerf->Ok(Leading(int_of_floatf))|Okf->Ok(Leading_stepf)|Error_ase->e)|["text";part]->(matchsplit_on_slashpartwith|Stdlib.Option.Some(base,lh_str)->(matchparse_lh_modifierthemelh_strwith|Stdlib.Option.Somelh_mod->ifParse.is_bracket_valuebasethenletinner=Parse.bracket_innerbaseinifis_color_bracketinnerthenerr_not_utilityelseifis_valid_bracket_font_sizeinnerthenOk(Text_bracket_fs_lh(inner,lh_mod))elseerr_not_utilityelseifis_named_sizebasethenOk(Text_named_lh(base,lh_mod))elseifis_theme_text_sizethemebasethenOk(Text_theme_lh(base,lh_mod))elseerr_not_utility|Stdlib.Option.None->err_not_utility)|Stdlib.Option.None->ifParse.is_bracket_valuepartthenletinner=Parse.bracket_innerpartinifis_color_bracketinnerthenerr_not_utilityelseifis_valid_bracket_font_sizeinnerthenOk(Text_bracket_fsinner)elsematchhinted_font_size_token_streaminnerwith|Stdlib.Option.Somevalue->Ok(Text_bracket_fs_raw(inner,value))|Stdlib.Option.None->err_not_utilityelseifis_theme_text_sizethemepartthenOk(Text_themepart)elseerr_not_utility)(* A project token's name may span several segments, and the line-height
modifier still rides on the last one. *)|"text"::(_::_::_asrest)->(letjoined=String.concat"-"restinmatchsplit_on_slashjoinedwith|Stdlib.Option.Some(base,lh_str)whenis_theme_text_sizethemebase->(matchparse_lh_modifierthemelh_strwith|Stdlib.Option.Somelh_mod->Ok(Text_theme_lh(base,lh_mod))|Stdlib.Option.None->err_not_utility)|_whenis_theme_text_sizethemejoined->Ok(Text_themejoined)|_->err_not_utility)|_->err_not_utilityletlh_to_string=function|Spacingn->string_of_intn|No_leading->"none"|Namedname->name|Bracket(v,_)->"["^v^"]"|Var_shorthandname->"("^name^")"letto_class=function|Text_xs->"text-xs"|Text_sm->"text-sm"|Text_base->"text-base"|Text_lg->"text-lg"|Text_xl->"text-xl"|Text_2xl->"text-2xl"|Text_3xl->"text-3xl"|Text_4xl->"text-4xl"|Text_5xl->"text-5xl"|Text_6xl->"text-6xl"|Text_7xl->"text-7xl"|Text_8xl->"text-8xl"|Text_9xl->"text-9xl"|Font_thin->"font-thin"|Font_extralight->"font-extralight"|Font_light->"font-light"|Font_normal->"font-normal"|Font_medium->"font-medium"|Font_semibold->"font-semibold"|Font_bold->"font-bold"|Font_extrabold->"font-extrabold"|Font_black->"font-black"|Font_bracket_weight(raw,_)->"font-["^raw^"]"|Font_bracket_weight_var(raw,_)->"font-["^raw^"]"|Font_bracket_family_quoted(raw,_)->"font-["^raw^"]"|Font_bracket_family_name(raw,_)->"font-["^raw^"]"|Font_bracket_family_var(raw,_)->"font-["^raw^"]"|Font_features_quotedraw|Font_features_raw(raw,_)->"font-features-["^raw^"]"|Font_features_varraw->"font-features-["^raw^"]"|Font_features_bare_varraw->"font-features-"^raw|Font_named(name,_)->"font-"^name|Font_weight_themename->"font-"^name|Leading_themename->"leading-"^name|Font_sans_->"font-sans"|Font_serif_->"font-serif"|Font_mono_->"font-mono"|Italic->"italic"|Not_italic->"not-italic"|Text_left->"text-left"|Text_center->"text-center"|Text_right->"text-right"|Text_justify->"text-justify"|Text_start->"text-start"|Text_end->"text-end"|Leading_none->"leading-none"|Leading_tight->"leading-tight"|Leading_snug->"leading-snug"|Leading_normal->"leading-normal"|Leading_relaxed->"leading-relaxed"|Leading_loose->"leading-loose"|Leadingn->"leading-"^string_of_intn|Leading_stepf->"leading-"^Spacing.pp_spacing_suffix(`Rem(f*.0.25))|Leading_varv->"leading-["^v^"]"|Leading_bracket(v,_)->"leading-["^v^"]"|Leading_raw(v,_)->"leading-["^v^"]"|Text_named_lh(name,lh)->"text-"^name^"/"^lh_to_stringlh|Text_themename->"text-"^name|Text_theme_lh(name,lh)->"text-"^name^"/"^lh_to_stringlh|Text_bracket_fsraw->"text-["^raw^"]"|Text_bracket_fs_raw(raw,_)->"text-["^raw^"]"|Text_bracket_fs_lh(raw,lh)->"text-["^raw^"]/"^lh_to_stringlh(** {1 Ordering Support} *)letsuborder=function(* Text align comes first - alphabetical order *)|Text_center->1001|Text_end->1002|Text_justify->1003|Text_left->1004|Text_right->1005|Text_start->1006(* Font-family candidates share a slot and sort by class name. A theme
family that also writes feature settings has a wider property set, so
Tailwind places it in the preceding slot. *)|Font_bracket_family_quoted_->1503|Font_bracket_family_var_->1503|Font_bracket_family_name_->1503(* Bracket font weights come before named font weights *)(* Font family - comes between text-align and text-size. *)|Font_monotrue|Font_sanstrue|Font_seriftrue|Font_named(_,true)->1502|Font_monofalse|Font_sansfalse|Font_seriffalse|Font_named(_,false)->1503|Font_weight_theme_->4201|Leading_theme_->3206(* Bracket font-size/line-height candidates follow 9xl in the shared slot;
the candidate-name tiebreak keeps 9xl first. *)|Text_bracket_fs_lh_->2008(* Font sizes come second - alphabetical order *)|Text_2xl->2001|Text_3xl->2002|Text_4xl->2003|Text_5xl->2004|Text_6xl->2005|Text_7xl->2006|Text_8xl->2007|Text_9xl->2008|Text_base->2009|Text_lg->2010|Text_sm->2011|Text_xl->2012|Text_xs->2013(* Named size with line-height modifier — same suborder as base size *)|Text_named_lh(name,_)->(matchnamewith|"2xl"->2001|"3xl"->2002|"4xl"->2003|"5xl"->2004|"6xl"->2005|"7xl"->2006|"8xl"->2007|"9xl"->2008|"base"->2009|"lg"->2010|"sm"->2011|"xl"->2012|"xs"->2013|_->2000)(* A project's own size sorts after the whole built-in scale, the way
Tailwind orders it. *)|Text_theme_->2014|Text_theme_lh_->2014(* Bracket font-size without modifier — after named sizes *)|Text_bracket_fs_|Text_bracket_fs_raw_->2100(* Leading comes third — numeric first, then arbitrary, then named
(alphabetical: loose, none, normal, relaxed, snug, tight) *)|Leadingn->3000+n|Leading_stepf->3000+int_of_float(f*.10.)|Leading_var_->3100|Leading_bracket_->3100|Leading_raw_->3100|Leading_loose->3201|Leading_none->3202|Leading_normal->3203|Leading_relaxed->3204|Leading_snug->3205|Leading_tight->3206(* Bracket font weights come before named font weights *)|Font_bracket_weight_->4000|Font_bracket_weight_var_->4000(* Font weight comes fourth - alphabetical order *)|Font_black->4100|Font_bold->4200|Font_extrabold->4300|Font_extralight->4400|Font_light->4500|Font_medium->4600|Font_normal->4700|Font_semibold->4800|Font_thin->4900(* Font feature settings *)|Font_features_quoted_|Font_features_raw_->1600|Font_features_var_->1600|Font_features_bare_var_->1600(* Italic *)|Italic->8380|Not_italic->8381(* Text utilities use theme record for line height variable reference *)(* Tailwind v4 expresses a rem line-height as the ratio [calc(line-height /
font-size)]; unitless line-heights stay verbatim. *)lettext_line_height~size_rem:Css.line_height->Css.line_height=function|Remlh_rem->letnumf:Css.line_heightCss.calc=NumfinCalc(Css.Calc.div(numlh_rem)(numsize_rem))|other->otherlettext_size_utility(size_var:Css.lengthVar.theme)(lh_var:Css.line_heightVar.theme)size_remlh_value=letsize_decl,size_ref=Var.bindingsize_var(Remsize_rem)inletlh_value=text_line_height~size_remlh_valueinletlh_decl,lh_ref=Var.bindinglh_varlh_valuein(* Use shared theme record - no declaration, just reference *)lettheme=default_line_height_themeinletleading_ref=sndtheme.leadinginletleading_with_fallback=Css.with_fallbackleading_ref(Css.Varlh_ref)instyle[size_decl;lh_decl;font_size(Css.Varsize_ref);line_height(Varleading_with_fallback);]lettext_xs()=text_size_utilitytext_xs_vartext_xs_lh_var0.75(line_height_of"xs")lettext_sm()=text_size_utilitytext_sm_vartext_sm_lh_var0.875(line_height_of"sm")lettext_base()=text_size_utilitytext_base_vartext_base_lh_var1.0(line_height_of"base")lettext_lg()=text_size_utilitytext_lg_vartext_lg_lh_var1.125(line_height_of"lg")lettext_xl()=text_size_utilitytext_xl_vartext_xl_lh_var1.25(line_height_of"xl")lettext_2xl()=text_size_utilitytext_2xl_vartext_2xl_lh_var1.5(line_height_of"2xl")lettext_3xl()=text_size_utilitytext_3xl_vartext_3xl_lh_var1.875(line_height_of"3xl")lettext_4xl()=text_size_utilitytext_4xl_vartext_4xl_lh_var2.25(line_height_of"4xl")lettext_5xl()=text_size_utilitytext_5xl_vartext_5xl_lh_var3.0(line_height_of"5xl")lettext_6xl()=text_size_utilitytext_6xl_vartext_6xl_lh_var3.75(line_height_of"6xl")lettext_7xl()=text_size_utilitytext_7xl_vartext_7xl_lh_var4.5(line_height_of"7xl")lettext_8xl()=text_size_utilitytext_8xl_vartext_8xl_lh_var6.0(line_height_of"8xl")lettext_9xl()=text_size_utilitytext_9xl_vartext_9xl_lh_var8.0(line_height_of"9xl")(* Font weight utilities set --tw-font-weight for animation but use theme var
directly *)letfont_weight_utilityweight_varweight_value=letweight_theme_decl,weight_theme_ref=Var.bindingweight_varweight_valueinletweight_util_decl=Var.setfont_weight_var(Css.Varweight_theme_ref)in(* Get @property rule for font-weight channel (needed for animations) *)letproperty_rules=matchVar.property_rulefont_weight_varwith|None->Css.empty|Somerule->ruleinstyle~property_rules[weight_theme_decl;weight_util_decl;font_weight(Css.Varweight_theme_ref);]letfont_thin=font_weight_utilityfont_weight_thin_var(Weight100.)letfont_extralight=font_weight_utilityfont_weight_extralight_var(Weight200.)letfont_light=font_weight_utilityfont_weight_light_var(Weight300.)letfont_normal=font_weight_utilityfont_weight_normal_var(Weight400.)letfont_medium=font_weight_utilityfont_weight_medium_var(Weight500.)letfont_semibold=font_weight_utilityfont_weight_semibold_var(Weight600.)letfont_bold()=font_weight_utilityfont_weight_bold_var(Weight700.)letfont_extrabold=font_weight_utilityfont_weight_extrabold_var(Weight800.)letfont_black=font_weight_utilityfont_weight_black_var(Weight900.)letfont_serif=letserif_decl,serif_ref=Var.bindingfont_serif_vardefault_serif_stackinstyle[serif_decl;font_family(Css.Varserif_ref)]letfont_mono=letmono_decl,mono_ref=Var.bindingfont_mono_vardefault_mono_stackinstyle[mono_decl;font_family(Css.Varmono_ref)](* Font family utilities use the font variables directly *)letfont_sans=letsans_decl,sans_ref=Var.bindingfont_sans_vardefault_sans_stackinstyle[sans_decl;font_family(Css.Varsans_ref)](* [font-source] reads --font-source from the theme; the token has to be
there, or a stray source word would parse as a utility.
An [@theme inline] token has no declaration of its own, so the utility
carries the value instead of a reference to it. A self-referential one
([--font-a: var(--font-a)]) is the exception: inlining it would leave the
reference dangling, so the token keeps its declaration. *)(* A family the project declared also carries the [--font-<name>--font-
feature-settings] beside it, the way Tailwind emits it. *)(* The feature and variation settings a project declares beside a family, as
[--font-<name>--font-feature-settings]: each is a token of its own that
the utility reads, or the value itself when the block is inline. *)letfont_feature_declsthemename=letsub:typea.string->(Cascade.Cursor.t->a)->aVar.theme->(a->Css.declaration)->(aCss.var->Css.declaration)->Css.declarationlist=funsuffixreadvarpropertyproperty_of_var->lettoken=String.concat""["font-";name;"--";suffix]inmatchScheme.theme_value(Sometheme)tokenwith|None->[]|Someraw->(matchCascade.Cursor.try_parse_full_errread(Cascade.Cursor.of_stringraw)with|Error_->[]|Okvalue->ifScheme.is_inline_tokenthemetokenthen[propertyvalue]elseletdecl,ref_=Var.bindingvarvaluein[decl;property_of_varref_])insub"font-feature-settings"Css.Properties.read_font_feature_settings(font_named_features_varname)font_feature_settings(funv->font_feature_settings(Varv))@sub"font-variation-settings"Css.Properties.read_font_variation_settings(font_named_variations_varname)font_variation_settings(funv->font_variation_settings(Varv))letfont_weight_themethemename=matchScheme.theme_value(Sometheme)("font-weight-"^name)with|None->style[]|Someraw->(matchParse.decimal_intrawwith|None->style[]|Somen->font_weight_utility(font_weight_named_varname)(Css.Weight(float_of_intn)))letfont_named?fallbackthemename=lettoken="font-"^nameinmatchScheme.theme_value(Sometheme)tokenwith|None->(matchfallbackwithSomes->s|None->style[])|Someraw->(matchCss.parse_font_familyrawwith|None->(matchfallbackwithSomes->s|None->style[])|Somefamily->letself_referential=matchfamilywith|Css.Varv->Css.var_namev=token|_->falseinletfeatures=font_feature_declsthemenameinifScheme.is_inline_tokenthemetoken&¬self_referentialthenstyle(font_familyfamily::features)elseletdecl,ref=Var.binding(font_named_varname)familyinstyle(decl::font_family(Css.Varref)::features))letitalic=style[font_styleItalic]letnot_italic=style[font_styleNormal]lettext_left=style[text_alignLeft]lettext_center=style[text_alignCenter]lettext_right=style[text_alignRight]lettext_justify=style[text_alignJustify]lettext_start=style[text_alignStart]lettext_end=style[text_alignEnd]letleading_with_theme_vartheme_vardefault_value=lettheme_decl,theme_ref=Var.bindingtheme_vardefault_valueinletchannel_decl=Var.setleading_var(Css.Vartheme_ref)inletproperty_rules=Var.property_ruleleading_var|>Option.to_list|>Css.concatinstyle~property_rules[theme_decl;channel_decl;line_height(Css.Vartheme_ref)]letleading_themethemename=matchScheme.theme_value(Sometheme)("leading-"^name)with|None->style[]|Someraw->(matchparse_bracket_leadingrawwith|Stdlib.Option.None->style[]|Stdlib.Option.Somelh->leading_with_theme_var(leading_named_varname)lh)letleading_none?theme()=matchScheme.theme_valuetheme"leading-none"with|Some_->leading_with_theme_varleading_none_var(Num1.0)|None->(* Tailwind v4.3 ships no --leading-none token, so inline the literal
rather than minting a var. *)letvalue:line_height=Num1.0inletchannel_decl=Var.setleading_varvalueinletproperty_rules=Var.property_ruleleading_var|>Option.to_list|>Css.concatinstyle~property_rules[channel_decl;line_heightvalue]letleading_tight=leading_with_theme_varleading_tight_var(Num1.25)letleading_snug=leading_with_theme_varleading_snug_var(Num1.375)letleading_normal=leading_with_theme_varleading_normal_var(Num1.5)letleading_relaxed=leading_with_theme_varleading_relaxed_var(Num1.625)letleading_loose=leading_with_theme_varleading_loose_var(Num2.0)(* Shared by the int base ([leading]) and its half-step sibling ([leading']):
[n] is the class number (spacing-scale multiplier), an integer or a
fractional quarter-step alike. *)letleading_calc?theme(n:float)=letname="leading-"^Spacing.pp_spacing_suffix(`Rem(n*.0.25))inmatchScheme.theme_valuethemenamewith|Some_->(* A theme overrides --leading-N: reference it like a named leading. *)lettheme_var=Var.themeCss.Line_heightname~order:(6,53)inleading_with_theme_vartheme_var(Rem(n*.0.25))|Nonewhenn=0.0->(* leading-0 is a literal 0, not calc(var(--spacing) * 0). *)letvalue:line_height=Num0.0inletchannel_decl=Var.setleading_varvalueinletproperty_rules=Var.property_ruleleading_var|>Option.to_list|>Css.concatinstyle~property_rules[channel_decl;line_heightvalue]|None->(* Tailwind v4.3 default: derive numeric leading from the spacing scale,
leading-1 -> var(--spacing), leading-N -> calc(var(--spacing) *
N). *)letspacing_decl=Var.setTheme.spacing_varTheme.spacing_baseinletspacing=Var.nameTheme.spacing_varinletvalue:line_height=ifn=1.0thenCss.Var(Var.theme_refspacing)elseCss.Calc(Css.Calc.mul(Css.Calc.varspacing)(Css.Calc.floatn))inletchannel_decl=Var.setleading_varvalueinletproperty_rules=Var.property_ruleleading_var|>Option.to_list|>Css.concatinstyle~property_rules[spacing_decl;channel_decl;line_heightvalue]letleading?themen=leading_calc?theme(float_of_intn)letleading'?themen=leading_calc?themen(* The leading scale, published the same way. *)let()=List.iter(fun(name,value)->Scheme.register_default_token("leading-"^name)(Css.Pp.to_string~minify:trueCss.Properties.pp_line_heightvalue))[("tight",(Num1.25:Css.line_height));("snug",Num1.375);("normal",Num1.5);("relaxed",Num1.625);("loose",Num2.0);](* Publish the text scale through the theme-token registry, the way rule.ml
publishes the breakpoints, so [theme()] in a project's CSS resolves against
the same values the utilities use. *)let()=List.iter(fun(name,_var,rem)->Scheme.register_default_token("text-"^name)(Css.Pp.to_stringCss.pp_length(Css.Remrem)))text_size_data(* And each size's line-height, which [theme(static)] declares with the rest
of the theme. *)let()=List.iter(fun(name,_var,rem)->Scheme.register_default_token("text-"^name^"--line-height")(Css.Pp.to_string~minify:trueCss.Properties.pp_line_height(text_line_height~size_rem:rem(line_height_ofname))))text_size_data(* A theme token's value is text; read it back at the type the property
wants. *)letparse_theme_line_heightthemetoken=matchScheme.theme_value(Sometheme)tokenwith|None->Stdlib.Option.None|Someraw->(letcursor=Cascade.Cursor.of_stringrawinmatchCascade.Cursor.try_parse_full_errCss.Properties.read_line_heightcursorwith|Oklh->Stdlib.Option.Somelh|Error_->Stdlib.Option.None)(* Both tables are compile-time constants: building them per call allocated
the list and a second remapped copy once per utility rendered. *)letnamed_leading_table:(string*(Css.line_heightVar.theme*Css.line_height))list=[(* [none] is absent for the reason [known_leading_names] gives. *)("tight",(leading_tight_var,(Num1.25:Css.line_height)));("snug",(leading_snug_var,Num1.375));("normal",(leading_normal_var,Num1.5));("relaxed",(leading_relaxed_var,Num1.625));("loose",(leading_loose_var,Num2.0));]lettext_size_table=List.map(fun(n,v,d)->(n,(v,d)))text_size_data(** Convert a line-height modifier to (extra_declarations, line_height_value).
*)letlh_modifier_to_csstheme=function|Spacingn->letspacing_decl,_spacing_ref=Var.bindingTheme.spacing_varTheme.spacing_basein(* [--spacing] holds a length; here it is read as a line height, so the
reference is built at that kind off the same handle. *)letlh_spacing_ref:Css.line_heightCss.var=Var.bracket(Var.nameTheme.spacing_var)inletlh:Css.line_height=Calc(Css.Calc.mul(Varlh_spacing_ref)(Num(float_of_intn)))in([spacing_decl],lh)|No_leading->([],Num1.0)|Namedname->(matchList.assoc_optnamenamed_leading_tablewith|Stdlib.Option.Some(theme_var,default_val)->letdecl,ref_=Var.bindingtheme_vardefault_valin([decl],Varref_)|Stdlib.Option.None->((* A [--leading-*] the project declared: bind it, so the theme layer
carries the token the utility reads. *)matchparse_theme_line_heighttheme("leading-"^name)with|Stdlib.Option.Somelh->letdecl,ref_=Var.binding(leading_named_varname)lhin([decl],Css.Varref_)|Stdlib.Option.None->letref_:Css.line_heightCss.var=Var.bracket("leading-"^name)in([],Css.Varref_)))|Bracket(_,lh)->([],lh)|Var_shorthandname->([],Css.Var(Var.bracket("var("^name^")")))(* A size the project declared, with the line height, letter spacing and font
weight the block may declare beside it as [--text-<name>--line-height] and
its siblings. Each goes through its channel with the token as the fallback,
as a built-in size's line height does, so [leading-*] on the same element
still wins. A [/modifier] names the line height itself, and Tailwind then
writes the size and that line height alone. *)lettext_theme_decls?(modified=false)themename=lettoken="text-"^namein(* The channel keeps the token as its fallback either way; an inline token
puts its value there, as Tailwind writes [var(--tw-leading, 1.2)]. *)letsub:typea.string->(Cascade.Cursor.t->a)->aVar.theme->aVar.channel->(aCss.var->Css.declaration)->Css.declarationlist=funsuffixreadvarchannelproperty->lettoken=String.concat""[token;"--";suffix]inmatchScheme.theme_value(Sometheme)tokenwith|None->[]|Someraw->(matchCascade.Cursor.try_parse_full_errread(Cascade.Cursor.of_stringraw)with|Error_->[]|Okvalue->ifScheme.is_inline_tokenthemetokenthen[property(Var.reference_with_fallbackchannelvalue)]elseletdecl=Var.setvarvaluein[decl;property(Var.reference_with_var_fallbackchannelvarvalue);])inmatchScheme.theme_value(Sometheme)tokenwith|None->[]|Someraw->(matchCss.parse_lengthrawwith|None->[]|Somelen->letsize=ifScheme.is_inline_tokenthemetokenthen[font_sizelen]elseletdecl,ref=Var.binding(text_named_varname)lenin[decl;font_size(Css.Varref)]inifmodifiedthensizeelsesize@sub"line-height"Css.Properties.read_line_height(text_named_lh_varname)leading_var(funv->line_height(Varv))@sub"letter-spacing"(Css.Values.read_length~allow_negative:true~length_only:true)(text_named_tracking_varname)tracking_var(funv->letter_spacing(Varv))@sub"font-weight"Css.Properties.read_font_weight(text_named_weight_varname)font_weight_var(funv->font_weight(Varv)))(** Generate font-size + line-height style for a named text size with
modifier. *)lettext_named_with_lhthemenamelh_mod=matchList.assoc_optnametext_size_tablewith|Stdlib.Option.Some(size_var,default_rem)->letsize_decl,size_ref=Var.bindingsize_var(Remdefault_rem)inletlh_extra,lh_value=lh_modifier_to_cssthemelh_modin((size_decl::lh_extra)@[font_size(Css.Varsize_ref)],[line_heightlh_value])|Stdlib.Option.None->(* Fallback — should not happen for valid named sizes *)([],[])(* font_size_keyword / try_parse_length_value / split_type_prefix are defined
earlier in the module so of_class can validate arbitrary bracket values up
front. *)(* is_valid_bracket_font_size is defined earlier (before of_class). *)(** Parse bracket content as font-size declarations (without line-height).
Caller must have already passed [inner] through
[is_valid_bracket_font_size]. *)letbracket_font_size_declsinner=letvalue,hinted=bracket_font_size_valueinnerinmatchparse_spacing_callvaluewith|Stdlib.Option.Somen->letdecl,len=Theme.spacing_calc_floatnin[decl;font_sizelen]|Stdlib.Option.None->(matchfont_size_keywordvaluewith|Stdlib.Option.Somekw->[Css.font_size_kwkw]|Stdlib.Option.None->(matchbracket_font_size_lengthvalue~hintedwith|Stdlib.Option.Somefs_len->[font_sizefs_len]|Stdlib.Option.None->ifParse.is_varvaluethenletbare=Parse.extract_var_namevaluein[font_size(Css.Var(Var.bracketbare))]elseinvalid_arg("bracket_font_size_decls: not a valid font-size value: "^inner)))(** Generate font-size-only style for bracket value. *)letbracket_font_size_styleraw=style(bracket_font_size_declsraw)letto_styletheme=letleading_none()=leading_none~theme()inletleadingn=leading~themeninletleading_stepf=leading'~themefinfunction|Text_xs->text_xs()|Text_sm->text_sm()|Text_base->text_base()|Text_lg->text_lg()|Text_xl->text_xl()|Text_2xl->text_2xl()|Text_3xl->text_3xl()|Text_4xl->text_4xl()|Text_5xl->text_5xl()|Text_6xl->text_6xl()|Text_7xl->text_7xl()|Text_8xl->text_8xl()|Text_9xl->text_9xl()|Font_thin->font_thin|Font_extralight->font_extralight|Font_light->font_light|Font_normal->font_normal|Font_medium->font_medium|Font_semibold->font_semibold|Font_bold->font_bold()|Font_extrabold->font_extrabold|Font_black->font_black|Font_bracket_weight(_,n)->letweight_util_decl=Var.setfont_weight_var(Weight(float_of_intn))inletproperty_rules=matchVar.property_rulefont_weight_varwith|None->Css.empty|Somerule->ruleinstyle~property_rules[weight_util_decl;font_weight(Weight(float_of_intn))]|Font_bracket_weight_var(_,var_str)->letbare_name=Parse.extract_var_namevar_strinletvar_ref:Css.font_weightCss.var=Var.bracketbare_nameinletweight_util_decl=Var.setfont_weight_var(Css.Varvar_ref)inletproperty_rules=matchVar.property_rulefont_weight_varwith|None->Css.empty|Somerule->ruleinstyle~property_rules[weight_util_decl;font_weight(Varvar_ref)]|Font_bracket_family_quoted(_,decoded)->letn=String.lengthdecodedinifn>=2&&decoded.[0]='"'&&decoded.[n-1]='"'then(* font-["arial_rounded"] → the decoded text is one quoted family
name; strip the quotes cascade's own printer adds back. *)style[font_family(Name(String.subdecoded1(n-2)))]else(* font-["liga"_0x10] → not a single quoted name. The decoded text
already carries its own quoting (a quoted string mixed with other
tokens); tokenize it and pass the tokens through rather than
wrapping the whole string in one more layer of quotes. *)lettokens=Css.Values.read_invalid_value(Cascade.Cursor.of_stringdecoded)instyle[font_family(Invalidtokens)]|Font_bracket_family_name(_,s)->(* Each comma segment is its own [<family-name>] (CSS Fonts 4 sec. 2.1),
so a generic keyword only matches a segment on its own, not the
bracket as a whole; [font-[Papyrus,fantasy]] is the two-entry stack
[Papyrus, fantasy], never the single literal name
["Papyrus,fantasy"]. A quoted segment is always a custom-ident
literal, so it never resolves to a generic keyword either. *)letfamily_of_entryentry:Css.font_family=letentry=String.trimentryinletn=String.lengthentryinifn>=2&&(entry.[0]='"'||entry.[0]='\'')&&entry.[n-1]=entry.[0]thenCss.Name(String.subentry1(n-2))elsematchentrywith|"ui-sans-serif"->Css.Ui_sans_serif|"ui-serif"->Css.Ui_serif|"ui-monospace"->Css.Ui_monospace|"ui-rounded"->Css.Ui_rounded|"sans-serif"->Css.Sans_serif|"serif"->Css.Serif|"monospace"->Css.Monospace|"cursive"->Css.Cursive|"fantasy"->Css.Fantasy|"system-ui"->Css.System_ui|"emoji"->Css.Emoji|"math"->Css.Math|name->Css.Namenameinletfamily=matchString.split_on_char','swith|[single]->family_of_entrysingle|entries->Css.List(List.mapfamily_of_entryentries)instyle[font_familyfamily]|Font_bracket_family_var(_,var_str)->letbare_name=Parse.extract_var_namevar_strinletvar_ref:Css.font_familyCss.var=Var.bracketbare_nameinstyle[font_family(Varvar_ref)]|Font_features_raw(_,v)->style(Option.to_list(Parse.opaque_declaration"font-feature-settings"v))|Font_features_quoteds->(matchCss.parse_declaration"font-feature-settings"(Parse.decode_underscoress)with|Somedecl->style[decl]|None->style[])|Font_features_varvar_str->letbare_name=Parse.extract_var_namevar_strinletvar_ref:Css.font_feature_settingsCss.var=Var.bracketbare_nameinstyle[font_feature_settings(Varvar_ref)]|Font_features_bare_varv->(* bare_var_inner "(--my-features)" → "--my-features"; strip the --
prefix since var_ref adds it *)letinner=Parse.bare_var_innervinletbare_name=Option.value~default:inner(Parse.bare_nameinner)inletvar_ref:Css.font_feature_settingsCss.var=Var.bracketbare_nameinstyle[font_feature_settings(Varvar_ref)]|Font_named(name,_)->font_namedthemename|Font_weight_themename->font_weight_themethemename|Leading_themename->leading_themethemename|Font_sans_->font_named~fallback:font_sanstheme"sans"|Font_serif_->font_named~fallback:font_seriftheme"serif"|Font_mono_->font_named~fallback:font_monotheme"mono"|Italic->italic|Not_italic->not_italic|Text_left->text_left|Text_center->text_center|Text_right->text_right|Text_justify->text_justify|Text_start->text_start|Text_end->text_end|Leading_none->leading_none()|Leading_tight->leading_tight|Leading_snug->leading_snug|Leading_normal->leading_normal|Leading_relaxed->leading_relaxed|Leading_loose->leading_loose|Leadingn->leadingn|Leading_stepf->leading_stepf|Leading_varv->letbare_name=Parse.extract_var_namevinletvar_ref:Css.line_heightCss.var=Var.bracketbare_nameinletchannel_decl=Var.setleading_var(Css.Varvar_ref)inletproperty_rules=Var.property_ruleleading_var|>Option.to_list|>Css.concatinstyle~property_rules[channel_decl;line_height(Css.Varvar_ref)]|Leading_bracket(_,lh)->letchannel_decl=Var.setleading_varlhinletproperty_rules=Var.property_ruleleading_var|>Option.to_list|>Css.concatinstyle~property_rules[channel_decl;line_heightlh]|Leading_raw(_,value)->letchannel_decl=Css.custom_property~layer:"utilities"(Var.css_nameleading_var)valueinletproperty_rules=Var.property_ruleleading_var|>Option.to_list|>Css.concatinletdeclarations=channel_decl::Option.to_list(Parse.opaque_declaration"line-height"value)instyle~metadata:[Var.metadataleading_var]~property_rulesdeclarations|Text_named_lh(name,lh_mod)->letfs_decl,lh_decls=text_named_with_lhthemenamelh_modinstyle(fs_decl@lh_decls)|Text_themename->style(text_theme_declsthemename)|Text_theme_lh(name,lh_mod)->letlh_extra,lh_value=lh_modifier_to_cssthemelh_modinstyle(text_theme_decls~modified:truethemename@lh_extra@[line_heightlh_value])|Text_bracket_fsraw->bracket_font_size_styleraw|Text_bracket_fs_raw(_,value)->style(Option.to_list(Parse.opaque_declaration"font-size"value))|Text_bracket_fs_lh(raw,lh_mod)->letfs_decls=bracket_font_size_declsrawinletlh_extra,lh_value=lh_modifier_to_cssthemelh_modinstyle(fs_decls@lh_extra@[line_heightlh_value])letexamples=[Text_base;Font_normal;Font_sansfalse;Italic;Text_center;Leading_none;]end(** Late typography handler - comes after color utilities (priority 24) *)moduleTypography_late=structopenStyleopenCss(* Tailwind reads the bracket as a colour unless it is a length or a
percentage, so a bare number lands on the colour branch whether it is
written [2] or [1.5]. *)letis_bare_numberstr=letis_number_charc=(c>='0'&&c<='9')||c='.'||c='-'instr<>""&&String.for_allis_number_charstr&&float_of_string_optstr<>None(* A data-type hint chooses which longhand a bracket lands in and says nothing
about the value. [indent-] and [underline-offset-] each write one longhand,
so every hint lands there and the length reader is handed what follows it.
The bracket text kept on the constructor still carries the hint, because
the class name is what the markup holds. *)letarbitrary_length_after_hintinner:Css.lengthoption=Option.bind(Parse.value_after_hintinner)Parse.arbitrary_length(* An arbitrary decoration thickness is any CSS length. *)letparse_decoration_thicknessinner:Css.lengthoption=Parse.arbitrary_lengthinner(* Tailwind's generator writes the percentage through and its minifier folds
it to em, 50% -> .5em, which is the spelling its own test corpus records
and the one [--diff] compares. Reading the unminified sheet instead reports
a difference that is the minifier's. *)letparse_decoration_pctinner:Css.lengthoption=matchParse.arbitrary_lengthinnerwith|Some(Pctpct)->Some(Em(pct/.100.))|_->Nonetypet=|(* Decoration color *)Decoration_colorofColor.color*intoption|Decoration_color_opacityofColor.color*int*Color.opacity_modifier|Decoration_transparent|Decoration_current|Decoration_current_opacityofColor.opacity_modifier|Decoration_inherit|Decoration_bracket_colorofstring*Css.color|Decoration_bracket_invalid_colorofstring|Decoration_bracket_color_opacityofstring*Css.color*Color.opacity_modifier|Decoration_bracket_varofstring|Decoration_bracket_var_opacityofstring*Color.opacity_modifier|Decoration_bracket_color_varofstring|Decoration_bracket_color_var_opacityofstring*Color.opacity_modifier|(* Text decoration lines *)Underline|Overline|Line_through|No_underline|(* Decoration styles *)Decoration_solid|Decoration_double|Decoration_dotted|Decoration_dashed|Decoration_wavy|Decoration_thicknessofint|Decoration_from_font|Decoration_auto|Decoration_bracket_thicknessofstring*Css.length|Decoration_bracket_pctofstring*Css.length|Decoration_bracket_length_varofstring|Decoration_bracket_pct_varofstring|(* Tracking *)(* The author's bracket text travels with the length it denotes, so the
class name is spelled exactly as it was written. *)Tracking_arbitraryofstring*Css.length|Neg_tracking_arbitraryofstring*Css.length|Tracking_rawofstring*string|Neg_tracking_rawofstring*string|Tracking_varofstring|Neg_tracking_varofstring|Tracking_tighter|Tracking_tight|Tracking_normal|Tracking_wide|Tracking_wider|Tracking_widest|(* Letter spacing named by a project [--tracking-*] token *)Tracking_themeofstring|(* Text transform *)Uppercase|Lowercase|Capitalize|Normal_case|(* Whitespace *)Whitespace_normal|Whitespace_nowrap|Whitespace_pre|Whitespace_pre_line|Whitespace_pre_wrap|Whitespace_break_spaces|(* Vertical align *)Align_baseline|Align_top|Align_middle|Align_bottom|Align_text_top|Align_text_bottom|Align_sub|Align_super|Align_arbitrary_varofstring|Align_rawofstring*string(* align-[foo]: one longhand, so the value is forwarded verbatim. *)|(* List utilities *)List_none|List_disc|List_decimal|List_bracket_varofstring|List_bracketofstring*Css.list_style_type|List_inside|List_outside|List_image_none|List_image_bracket_varofstring|List_image_bracketofstring*Css.list_style_image|List_image_rawofstring*string|List_image_urlofstring|(* Underline offset *)Underline_offset_auto|Underline_offset_0|Underline_offset_1|Underline_offset_2|Underline_offset_4|Underline_offset_8|Underline_offset_pxoffloat|Underline_offset_arbitraryofstring*Css.length|Underline_offset_rawofstring*string|Underline_offset_varofstring|Underline_offset_neg_pxoffloat|Underline_offset_neg_arbitraryofstring*Css.length|Underline_offset_neg_varofstring|(* Antialiased *)Antialiased|Subpixel_antialiased|(* Text overflow *)Text_ellipsis|Overflow_ellipsis(* legacy alias for text-ellipsis, keeps its name *)|Text_clip|Truncate|(* Text wrap *)Text_wrap|Text_nowrap|Text_balance|Text_pretty|(* Break utilities *)Break_normal|Break_words|Break_all|Break_keep|(* Overflow wrap *)Overflow_wrap_normal|Overflow_wrap_anywhere|Overflow_wrap_break_word|(* Hyphens *)Hyphens_none|Hyphens_manual|Hyphens_auto|(* Font stretch *)Font_stretch_ultra_condensed|Font_stretch_extra_condensed|Font_stretch_condensed|Font_stretch_semi_condensed|Font_stretch_normal|Font_stretch_semi_expanded|Font_stretch_expanded|Font_stretch_extra_expanded|Font_stretch_ultra_expanded|Font_stretch_percentofint|(* Numeric variants *)Normal_nums|Ordinal|Slashed_zero|Lining_nums|Oldstyle_nums|Proportional_nums|Tabular_nums|Diagonal_fractions|Stacked_fractions|(* Indent and line clamp *)Indentoffloat|Indent_negoffloat|Indent_px|Indent_neg_px|Indent_arbitraryofstring|Indent_rawofstring*string|Indent_neg_arbitraryofstring|Line_clampofint|Line_clamp_arbitraryofint|Line_clamp_rawofstring*string|Line_clamp_none|(* Content *)Content_none|Contentofstring|Content_squoteofstring(* single-quoted arbitrary: content-['x'] *)|Content_rawofstring*[`ContentofCss.content|`Rawofstring](* unquoted arbitrary: content-[attr(before)]; raw class text + value *)|Content_namedofstring(* content-<token> defined in the @theme *)letname="typography_late"(* Most late-typography families (decoration, tracking, whitespace,
word-break) sort at priority 26. Four occupy earlier canonical slots:
list-style (rank 48, by cursor/resize at priority 11),
text-overflow/truncate (rank 62, just before overflow at priority 18),
text-indent and vertical-align (right after text-align in the early handler
at priority 24), and line-clamp (rank 26, with box-sizing at priority
3). *)letpriority=function|List_none|List_disc|List_decimal|List_inside|List_outside|List_image_none|List_image_url_|List_bracket_var_|List_image_bracket_var_|List_bracket_|List_image_bracket_|List_image_raw_->11(* line-clamp (rank 26) sits between box-sizing and the display family, so
it shares box-sizing's priority and sorts after it on suborder. *)|Line_clamp_|Line_clamp_arbitrary_|Line_clamp_raw_|Line_clamp_none->3|Truncate->17|Indent_|Indent_neg_|Indent_px|Indent_neg_px|Indent_arbitrary_|Indent_raw_|Indent_neg_arbitrary_|Align_baseline|Align_top|Align_middle|Align_bottom|Align_sub|Align_super|Align_text_top|Align_text_bottom|Align_arbitrary_var_|Align_raw_->24|Content_none|Content_|Content_squote_|Content_raw_|Content_named_->35|_->26let(>|=)=Parse.(>|=)leterr_not_utility=Error(`Msg"Not a late typography utility")(* The length a [tracking-[...]] bracket denotes, read with cascade's own
grammar so [calc()] and the function forms come for free. [None] is a
bracket no length reads from, and [of_class] refuses the utility rather
than leaving [to_style] to raise. *)letarbitrary_trackinginner:Css.lengthoption=Css.parse_length(Parse.decode_underscoresinner)(* A letter spacing the project named in its [@theme]. The built-in scale is
matched before this is consulted, so those names never reach here. *)letis_theme_trackingthemen=Scheme.theme_value(Sometheme)("tracking-"^n)<>Noneletof_classthemeclass_name=letparts=Parse.split_classclass_nameinmatchpartswith|["underline"]->OkUnderline|["overline"]->OkOverline|["line";"through"]->OkLine_through|["no";"underline"]->OkNo_underline|["decoration";"solid"]->OkDecoration_solid|["decoration";"double"]->OkDecoration_double|["decoration";"dotted"]->OkDecoration_dotted|["decoration";"dashed"]->OkDecoration_dashed|["decoration";"wavy"]->OkDecoration_wavy|["decoration";"from";"font"]->OkDecoration_from_font|["decoration";"auto"]->OkDecoration_auto|["decoration";"transparent"]->OkDecoration_transparent|["decoration";"inherit"]->OkDecoration_inherit|["decoration";n]->((* Parse opacity modifier first *)letbase_str,opacity=Color.parse_opacity_modifier~themenin(* Check for "current" with optional opacity *)match(base_str,opacity)with|"current",Color.No_opacity->OkDecoration_current|"current",opacity->Ok(Decoration_current_opacityopacity)|_->(if(* Check for bracket values *)Parse.is_bracket_valuebase_strthenletinner=Parse.bracket_innerbase_strin(* [raw] is the bracket text the class was written with, hint
included, so the class prints back the way it was read. *)letbracket_colorrawvalue=matchColor.parse_bracket_colorvaluewith|Somec->(matchopacitywith|Color.No_opacity->Ok(Decoration_bracket_color(raw,c))|_->Ok(Decoration_bracket_color_opacity(raw,c,opacity)))|None->err_not_utilityinletbracket_pctrawvalue=matchparse_decoration_pctvaluewith|Somelen->Ok(Decoration_bracket_pct(raw,len))|None->err_not_utilityinletbracket_thicknessrawvalue=ifString.lengthvalue>0&&value.[String.lengthvalue-1]='%'thenbracket_pctrawvalueelsematchparse_decoration_thicknessvaluewith|Somelen->Ok(Decoration_bracket_thickness(raw,len))|None->err_not_utilityin(* A data-type hint says how to read the value written after it,
so only a var() reference there names a custom property. *)ifString.starts_with~prefix:"color:"innerthenletvalue=String.subinner6(String.lengthinner-6)inifParse.is_varvaluethenmatchopacitywith|Color.No_opacity->Ok(Decoration_bracket_color_varvalue)|_->Ok(Decoration_bracket_color_var_opacity(value,opacity))elsebracket_colorinnervalueelseifString.starts_with~prefix:"var("innerthenmatchopacitywith|Color.No_opacity->Ok(Decoration_bracket_varinner)|_->Ok(Decoration_bracket_var_opacity(inner,opacity))elseifString.starts_with~prefix:"length:"innerthenletvalue=String.subinner7(String.lengthinner-7)inifParse.is_varvaluethenOk(Decoration_bracket_length_varvalue)elsebracket_thicknessinnervalueelseifString.starts_with~prefix:"percentage:"innerthenletvalue=String.subinner11(String.lengthinner-11)inifParse.is_varvaluethenOk(Decoration_bracket_pct_varvalue)elsebracket_pctinnervalueelse(* Every colour spelling CSS knows, not only a [#] hex and a
colour function: a named colour and a keyword name a
decoration colour too. The colour reader runs before the
percentage one because [hsl(200_50%_50%)] ends in a [%]. *)matchColor.parse_bracket_colorinnerwith|Some_->bracket_colorinnerinner|None->(ifString.lengthinner>0&&inner.[String.lengthinner-1]='%'thenmatchparse_decoration_pctinnerwith|Somelen->Ok(Decoration_bracket_pct(inner,len))|None->err_not_utilityelseifis_bare_numberinnerthen(* Tailwind resolves the ambiguous unitless spelling
through the colour candidate path. Its declaration is
invalid CSS and browsers discard it; accepting an inert
candidate is therefore equivalent, while treating it as
px is not. *)Ok(Decoration_bracket_invalid_colorinner)elsematchparse_decoration_thicknessinnerwith|Somelen->Ok(Decoration_bracket_thickness(inner,len))|None->err_not_utility)else(* Try parsing as number (decoration thickness) *)matchParse.int_anybase_strwith|Okiwhenopacity=Color.No_opacity->Ok(Decoration_thicknessi)|_->((* A shadeless colour (white, black, a theme name) carries its
opacity on the single segment, so read the segment as a
colour and an opacity together. *)matchColor.shade_and_opacity_of_strings~theme[n]with|Ok(c,_,Color.No_opacity)->Ok(Decoration_color(c,None))|Ok(c,shade,opacity)->Ok(Decoration_color_opacity(c,shade,opacity))|Error_->err_not_utility)))|["decoration";color;shade]->(matchColor.shade_and_opacity_of_strings~theme[color;shade]with|Ok(c,s,Color.No_opacity)->Ok(Decoration_color(c,Somes))|Ok(c,s,opacity)->Ok(Decoration_color_opacity(c,s,opacity))|Error_->err_not_utility)|["tracking";v]whenParse.is_bracket_valuev->(letinner=Parse.bracket_innervinifParse.is_varinnerthenOk(Tracking_varinner)elsematcharbitrary_trackinginnerwith|Somelen->Ok(Tracking_arbitrary(inner,len))|None->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Tracking_raw(inner,value))|None->Error(`Msg("Invalid tracking value: "^inner))))|""::"tracking"::restwhenrest<>[]->(letvalue=String.concat"-"restinifnot(Parse.is_bracket_valuevalue)thenerr_not_utilityelseletinner=Parse.bracket_innervalueinifParse.is_varinnerthenOk(Neg_tracking_varinner)elsematcharbitrary_trackinginnerwith|Somelen->Ok(Neg_tracking_arbitrary(inner,len))|None->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Neg_tracking_raw(inner,value))|None->Error(`Msg("Invalid tracking value: "^inner))))|["tracking";"tighter"]->OkTracking_tighter|["tracking";"tight"]->OkTracking_tight|["tracking";"normal"]->OkTracking_normal|["tracking";"wide"]->OkTracking_wide|["tracking";"wider"]->OkTracking_wider|["tracking";"widest"]->OkTracking_widest|["tracking";name]whenis_theme_trackingthemename->Ok(Tracking_themename)|"tracking"::(_::_::_asrest)whenis_theme_trackingtheme(String.concat"-"rest)->Ok(Tracking_theme(String.concat"-"rest))|["uppercase"]->OkUppercase|["lowercase"]->OkLowercase|["capitalize"]->OkCapitalize|["normal";"case"]->OkNormal_case|["whitespace";"normal"]->OkWhitespace_normal|["whitespace";"nowrap"]->OkWhitespace_nowrap|["whitespace";"pre"]->OkWhitespace_pre|["whitespace";"pre";"line"]->OkWhitespace_pre_line|["whitespace";"pre";"wrap"]->OkWhitespace_pre_wrap|["whitespace";"break";"spaces"]->OkWhitespace_break_spaces|["align";v]whenParse.is_bracket_varv->Ok(Align_arbitrary_var(Parse.bracket_innerv))|["align";v]whenParse.is_bracket_valuev->(letinner=Parse.bracket_innervinmatchParse.arbitrary_declaration_valueinnerwith|Someraw->Ok(Align_raw(inner,raw))|None->err_not_utility)|["align";"baseline"]->OkAlign_baseline|["align";"top"]->OkAlign_top|["align";"middle"]->OkAlign_middle|["align";"bottom"]->OkAlign_bottom|["align";"text";"top"]->OkAlign_text_top|["align";"text";"bottom"]->OkAlign_text_bottom|["align";"sub"]->OkAlign_sub|["align";"super"]->OkAlign_super|["list";value]whenParse.is_bracket_varvalue->Ok(List_bracket_var(Parse.bracket_innervalue))|["list";value]whenParse.is_bracket_valuevalue->(letinner=Parse.bracket_innervalueinmatchCss.parse_list_style_type(Parse.decode_arbitrary_valueinner)with|Somet->Ok(List_bracket(inner,t))|None->err_not_utility)|["list";"none"]->OkList_none|["list";"disc"]->OkList_disc|["list";"decimal"]->OkList_decimal|["list";"inside"]->OkList_inside|["list";"outside"]->OkList_outside|["list";"image";"none"]->OkList_image_none|["list";"image";value]whenParse.is_bracket_varvalue->Ok(List_image_bracket_var(Parse.bracket_innervalue))(* The [(--x)] shorthand names a custom property, the same as
[list-image-[var(--x)]]. *)|["list";"image";value]whenParse.is_bare_varvalue->Ok(List_image_bracket_varvalue)|["list";"image";value]whenParse.is_bracket_valuevalue->(letinner=Parse.bracket_innervalueinmatchCss.parse_list_style_image(Parse.decode_arbitrary_valueinner)with|Somei->Ok(List_image_bracket(inner,i))|None->(matchParse.arbitrary_declaration_valueinnerwith|Someraw->Ok(List_image_raw(inner,raw))|None->err_not_utility))|"list"::"image"::restwhenrest<>[]->leturl=String.concat"-"restinifParse.is_valid_theme_nameurlthenOk(List_image_urlurl)elseerr_not_utility|["underline";"offset";"auto"]->OkUnderline_offset_auto|["underline";"offset";"0"]->OkUnderline_offset_0|["underline";"offset";"1"]->OkUnderline_offset_1|["underline";"offset";"2"]->OkUnderline_offset_2|["underline";"offset";"4"]->OkUnderline_offset_4|["underline";"offset";"8"]->OkUnderline_offset_8|["underline";"offset";n]whenParse.is_bracket_valuen->(letinner=Parse.bracket_innerninifParse.is_varinnerthenOk(Underline_offset_varinner)elsematcharbitrary_length_after_hintinnerwith|Somelen->Ok(Underline_offset_arbitrary(inner,len))|None->(matchParse.arbitrary_declaration_valueinnerwith|Someraw->Ok(Underline_offset_raw(inner,raw))|None->err_not_utility))|["underline";"offset";n]->(matchParse.decimal_floatnwith|Somepx->Ok(Underline_offset_pxpx)|None->err_not_utility)|["";"underline";"offset";n]whenParse.is_bracket_valuen->(letinner=Parse.bracket_innerninifParse.is_varinnerthenOk(Underline_offset_neg_varinner)elsematcharbitrary_length_after_hintinnerwith|Somelen->Ok(Underline_offset_neg_arbitrary(inner,len))|None->err_not_utility)|["";"underline";"offset";n]->(matchParse.decimal_floatnwith|Somepx->Ok(Underline_offset_neg_pxpx)|None->err_not_utility)|["antialiased"]->OkAntialiased|["subpixel";"antialiased"]->OkSubpixel_antialiased|["text";"ellipsis"]->OkText_ellipsis|["overflow";"ellipsis"]->OkOverflow_ellipsis|["text";"clip"]->OkText_clip|["truncate"]->OkTruncate|["text";"wrap"]->OkText_wrap|["text";"nowrap"]->OkText_nowrap|["text";"balance"]->OkText_balance|["text";"pretty"]->OkText_pretty|["break";"normal"]->OkBreak_normal|["break";"words"]->OkBreak_words|["break";"all"]->OkBreak_all|["break";"keep"]->OkBreak_keep|["overflow";"wrap";"normal"]->OkOverflow_wrap_normal|["overflow";"wrap";"anywhere"]->OkOverflow_wrap_anywhere|["overflow";"wrap";"break";"word"]->OkOverflow_wrap_break_word|["hyphens";"none"]->OkHyphens_none|["hyphens";"manual"]->OkHyphens_manual|["hyphens";"auto"]->OkHyphens_auto|["font";"stretch";"ultra";"condensed"]->OkFont_stretch_ultra_condensed|["font";"stretch";"extra";"condensed"]->OkFont_stretch_extra_condensed|["font";"stretch";"condensed"]->OkFont_stretch_condensed|["font";"stretch";"semi";"condensed"]->OkFont_stretch_semi_condensed|["font";"stretch";"normal"]->OkFont_stretch_normal|["font";"stretch";"semi";"expanded"]->OkFont_stretch_semi_expanded|["font";"stretch";"expanded"]->OkFont_stretch_expanded|["font";"stretch";"extra";"expanded"]->OkFont_stretch_extra_expanded|["font";"stretch";"ultra";"expanded"]->OkFont_stretch_ultra_expanded|["font";"stretch";n]whenString.ends_with~suffix:"%"n->letn=String.subn0(String.lengthn-1)inParse.int_bounded~name:"font-stretch"~min:50~max:200n>|=funi->Font_stretch_percenti|["normal";"nums"]->OkNormal_nums|["ordinal"]->OkOrdinal|["slashed";"zero"]->OkSlashed_zero|["lining";"nums"]->OkLining_nums|["oldstyle";"nums"]->OkOldstyle_nums|["proportional";"nums"]->OkProportional_nums|["tabular";"nums"]->OkTabular_nums|["diagonal";"fractions"]->OkDiagonal_fractions|["stacked";"fractions"]->OkStacked_fractions|["indent";"px"]->OkIndent_px|["";"indent";"px"]->OkIndent_neg_px|["indent";n]whenParse.is_bracket_valuen->(letinner=Parse.bracket_innerninmatcharbitrary_length_after_hintinnerwith|Some_->Ok(Indent_arbitraryinner)|None->(matchParse.arbitrary_declaration_valueinnerwith|Someraw->Ok(Indent_raw(inner,raw))|None->err_not_utility))|["";"indent";n]whenParse.is_bracket_valuen->letinner=Parse.bracket_innerninifarbitrary_length_after_hintinner=Nonethenerr_not_utilityelseOk(Indent_neg_arbitraryinner)|["indent";n]->(matchParse.spacing_value~name:"indent"nwith|OkfwhenTheme.has_spacing_step~themef->Ok(Indentf)|Ok_|Error_->err_not_utility)|["";"indent";n]->(matchParse.spacing_value~name:"indent"nwith|OkfwhenTheme.has_spacing_step~themef->Ok(Indent_negf)|Ok_|Error_->err_not_utility)|["line";"clamp";"none"]->OkLine_clamp_none|["line";"clamp";n]whenParse.is_bracket_valuen->(letinner=Parse.bracket_innerninmatchParse.decimal_intinnerwith|Somei->Ok(Line_clamp_arbitraryi)|None->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Line_clamp_raw(inner,value))|None->err_not_utility))|["line";"clamp";n]->Parse.int_bounded~name:"line-clamp"~min:0~max:999n>|=funi->Line_clampi|["content";"none"]->OkContent_none|"content"::rest->letjoined=String.concat"-"restin(* Parse arbitrary value syntax: content-["value"] -> value *)letbracket_quotedq=String.lengthjoined>=4&&String.getjoined0='['&&String.getjoined1=q&&String.getjoined(String.lengthjoined-1)=']'&&String.getjoined(String.lengthjoined-2)=qin(* v4 has content-none, arbitrary content-[...], and content-<token>
when the @theme defines --content-<token>. A bare word like
content-wrapper with no such token is not a utility (it used to be
wrongly accepted). An unquoted bracket (content-[attr(before)])
carries a function value, parsed as a content list. *)ifbracket_quoted'"'thenletvalue=String.subjoined2(String.lengthjoined-4)inOk(Contentvalue)elseifbracket_quoted'\''thenletvalue=String.subjoined2(String.lengthjoined-4)inOk(Content_squotevalue)elseifScheme.theme_value(Sometheme)("content-"^joined)<>NonethenOk(Content_namedjoined)elseifString.lengthjoined>=2&&String.getjoined0='['&&String.getjoined(String.lengthjoined-1)=']'thenbeginletinner=String.subjoined1(String.lengthjoined-2)inletspaced=Parse.decode_underscoresinnerinmatchCascade.Cursor.try_parse_full_errCss.Properties.read_content(Cascade.Cursor.of_stringspaced)with|Okvalue->Ok(Content_raw(inner,`Contentvalue))|Error_->(matchParse.arbitrary_declaration_valueinnerwith|Somevalue->Ok(Content_raw(inner,`Rawvalue))|None->err_not_utility)endelseerr_not_utility|_->err_not_utilityletto_class=function|Decoration_color(color,None)->"decoration-"^Color.ppcolor|Decoration_color(color,Someshade)->"decoration-"^Color.ppcolor^"-"^string_of_intshade|Decoration_color_opacity(color,shade,opacity)->letbase=ifColor.is_shadelesscolorthen"decoration-"^Color.ppcolorelse"decoration-"^Color.ppcolor^"-"^string_of_intshadeinbase^"/"^Color.pp_opacityopacity|Decoration_transparent->"decoration-transparent"|Decoration_current->"decoration-current"|Decoration_current_opacityopacity->"decoration-current/"^Color.pp_opacityopacity|Decoration_inherit->"decoration-inherit"|Decoration_bracket_color(v,_)->"decoration-["^v^"]"|Decoration_bracket_invalid_colorv->"decoration-["^v^"]"|Decoration_bracket_color_opacity(v,_,opacity)->"decoration-["^v^"]/"^Color.pp_opacityopacity|Decoration_bracket_varv->"decoration-["^v^"]"|Decoration_bracket_var_opacity(v,opacity)->"decoration-["^v^"]/"^Color.pp_opacityopacity|Decoration_bracket_color_varv->"decoration-[color:"^v^"]"|Decoration_bracket_color_var_opacity(v,opacity)->"decoration-[color:"^v^"]/"^Color.pp_opacityopacity|Underline->"underline"|Overline->"overline"|Line_through->"line-through"|No_underline->"no-underline"|Decoration_solid->"decoration-solid"|Decoration_double->"decoration-double"|Decoration_dotted->"decoration-dotted"|Decoration_dashed->"decoration-dashed"|Decoration_wavy->"decoration-wavy"|Decoration_thicknessn->"decoration-"^string_of_intn|Decoration_from_font->"decoration-from-font"|Decoration_auto->"decoration-auto"|Decoration_bracket_thickness(v,_)->"decoration-["^v^"]"|Decoration_bracket_pct(v,_)->"decoration-["^v^"]"|Decoration_bracket_length_varv->"decoration-[length:"^v^"]"|Decoration_bracket_pct_varv->"decoration-[percentage:"^v^"]"|Tracking_arbitrary(v,_)->"tracking-["^v^"]"|Neg_tracking_arbitrary(v,_)->"-tracking-["^v^"]"|Tracking_raw(v,_)->"tracking-["^v^"]"|Neg_tracking_raw(v,_)->"-tracking-["^v^"]"|Tracking_varv->"tracking-["^v^"]"|Neg_tracking_varv->"-tracking-["^v^"]"|Tracking_tighter->"tracking-tighter"|Tracking_tight->"tracking-tight"|Tracking_normal->"tracking-normal"|Tracking_wide->"tracking-wide"|Tracking_wider->"tracking-wider"|Tracking_widest->"tracking-widest"|Tracking_themename->"tracking-"^name|Uppercase->"uppercase"|Lowercase->"lowercase"|Capitalize->"capitalize"|Normal_case->"normal-case"|Whitespace_normal->"whitespace-normal"|Whitespace_nowrap->"whitespace-nowrap"|Whitespace_pre->"whitespace-pre"|Whitespace_pre_line->"whitespace-pre-line"|Whitespace_pre_wrap->"whitespace-pre-wrap"|Whitespace_break_spaces->"whitespace-break-spaces"|Align_arbitrary_vars->"align-["^s^"]"|Align_raw(s,_)->"align-["^s^"]"|Align_baseline->"align-baseline"|Align_top->"align-top"|Align_middle->"align-middle"|Align_bottom->"align-bottom"|Align_text_top->"align-text-top"|Align_text_bottom->"align-text-bottom"|Align_sub->"align-sub"|Align_super->"align-super"|List_none->"list-none"|List_disc->"list-disc"|List_decimal->"list-decimal"|List_bracket_vars->"list-["^s^"]"|List_bracket(raw,_)->"list-["^raw^"]"|List_inside->"list-inside"|List_outside->"list-outside"|List_image_none->"list-image-none"|List_image_bracket_vars->ifParse.is_bare_varsthen"list-image-"^selse"list-image-["^s^"]"|List_image_bracket(raw,_)|List_image_raw(raw,_)->"list-image-["^raw^"]"|List_image_urlurl->"list-image-"^url|Underline_offset_auto->"underline-offset-auto"|Underline_offset_0->"underline-offset-0"|Underline_offset_1->"underline-offset-1"|Underline_offset_2->"underline-offset-2"|Underline_offset_4->"underline-offset-4"|Underline_offset_8->"underline-offset-8"|Underline_offset_pxpx->lets=string_of_floatpxinlets=ifString.ends_with~suffix:"."sthenString.subs0(String.lengths-1)elsesin"underline-offset-"^s|Underline_offset_arbitrary(raw,_)|Underline_offset_raw(raw,_)->"underline-offset-["^raw^"]"|Underline_offset_varv->"underline-offset-["^v^"]"|Underline_offset_neg_pxpx->lets=string_of_floatpxinlets=ifString.ends_with~suffix:"."sthenString.subs0(String.lengths-1)elsesin"-underline-offset-"^s|Underline_offset_neg_arbitrary(raw,_)->"-underline-offset-["^raw^"]"|Underline_offset_neg_varv->"-underline-offset-["^v^"]"|Antialiased->"antialiased"|Subpixel_antialiased->"subpixel-antialiased"|Text_ellipsis->"text-ellipsis"|Overflow_ellipsis->"overflow-ellipsis"|Text_clip->"text-clip"|Truncate->"truncate"|Text_wrap->"text-wrap"|Text_nowrap->"text-nowrap"|Text_balance->"text-balance"|Text_pretty->"text-pretty"|Break_normal->"break-normal"|Break_words->"break-words"|Break_all->"break-all"|Break_keep->"break-keep"|Overflow_wrap_normal->"overflow-wrap-normal"|Overflow_wrap_anywhere->"overflow-wrap-anywhere"|Overflow_wrap_break_word->"overflow-wrap-break-word"|Hyphens_none->"hyphens-none"|Hyphens_manual->"hyphens-manual"|Hyphens_auto->"hyphens-auto"|Font_stretch_ultra_condensed->"font-stretch-ultra-condensed"|Font_stretch_extra_condensed->"font-stretch-extra-condensed"|Font_stretch_condensed->"font-stretch-condensed"|Font_stretch_semi_condensed->"font-stretch-semi-condensed"|Font_stretch_normal->"font-stretch-normal"|Font_stretch_semi_expanded->"font-stretch-semi-expanded"|Font_stretch_expanded->"font-stretch-expanded"|Font_stretch_extra_expanded->"font-stretch-extra-expanded"|Font_stretch_ultra_expanded->"font-stretch-ultra-expanded"|Font_stretch_percentn->"font-stretch-"^string_of_intn^"%"|Normal_nums->"normal-nums"|Ordinal->"ordinal"|Slashed_zero->"slashed-zero"|Lining_nums->"lining-nums"|Oldstyle_nums->"oldstyle-nums"|Proportional_nums->"proportional-nums"|Tabular_nums->"tabular-nums"|Diagonal_fractions->"diagonal-fractions"|Stacked_fractions->"stacked-fractions"|Indentn->"indent-"^Spacing.pp_spacing_suffix(`Rem(n*.0.25))|Indent_negn->"-indent-"^Spacing.pp_spacing_suffix(`Rem(n*.0.25))|Indent_px->"indent-px"|Indent_neg_px->"-indent-px"|Indent_arbitrarys->"indent-["^s^"]"|Indent_raw(s,_)->"indent-["^s^"]"|Indent_neg_arbitrarys->"-indent-["^s^"]"|Line_clampn->"line-clamp-"^string_of_intn|Line_clamp_arbitraryn->"line-clamp-["^string_of_intn^"]"|Line_clamp_raw(spelling,_)->"line-clamp-["^spelling^"]"|Line_clamp_none->"line-clamp-none"|Content_none->"content-none"|Contents->"content-[\""^s^"\"]"|Content_squotes->"content-['"^s^"']"|Content_raw(s,_)->"content-["^s^"]"|Content_nameds->"content-"^s(** {1 Ordering Support} *)letsuborder=function(* Text decoration lines - suborder >= 8000 (alphabetical) *)|Line_through->8400|No_underline->8401|Overline->8402|Underline->8403(* The decoration properties follow the line, in Tailwind's own order:
colour, then style, then thickness. All the colour variants share a
suborder so the tie is broken alphabetically by class name. *)|Decoration_bracket_color_->8410|Decoration_bracket_invalid_color_->8410|Decoration_bracket_color_opacity_->8410|Decoration_bracket_color_var_->8411|Decoration_bracket_color_var_opacity_->8411|Decoration_bracket_var_->8412|Decoration_bracket_var_opacity_->8412|Decoration_color_->8413|Decoration_color_opacity_->8413|Decoration_transparent->8413|Decoration_current->8413|Decoration_current_opacity_->8413|Decoration_inherit->8413(* Decoration styles - same suborder, sorted by class name *)|Decoration_solid->8420|Decoration_double->8420|Decoration_dotted->8420|Decoration_dashed->8420|Decoration_wavy->8420(* Decoration thickness - numeric values by n, then brackets, then
auto/from-font. The numeric band is open-ended (the class carries any
integer), so the brackets sit far enough above it that no thickness a
stylesheet would ask for reaches them, and below the underline offset. *)|Decoration_thicknessn->8430+n|Decoration_bracket_thickness_->30000|Decoration_bracket_pct_->30000|Decoration_bracket_length_var_->30000|Decoration_bracket_pct_var_->30000|Decoration_auto->30010|Decoration_from_font->30010(* Tracking — negative first, then arbitrary, then named *)|Neg_tracking_arbitrary_->8245|Neg_tracking_raw_->8245|Neg_tracking_var_->8250|Tracking_arbitrary_->8255|Tracking_raw_->8255|Tracking_var_->8260(* Alphabetical by class name: normal, tight, tighter, wide, wider,
widest *)|Tracking_normal->8300|Tracking_tight->8301|Tracking_tighter->8302|Tracking_wide->8303|Tracking_wider->8304|Tracking_widest->8305|Tracking_theme_->8306(* Text transform - alphabetical order *)|Capitalize->8360|Lowercase->8361|Normal_case->8362|Uppercase->8363(* Whitespace - alphabetical order *)|Whitespace_break_spaces->8330|Whitespace_normal->8331|Whitespace_nowrap->8332|Whitespace_pre->8333|Whitespace_pre_line->8334|Whitespace_pre_wrap->8335(* Vertical align (priority 24) - between text-align (~1005) and font-family
(~1501) in the early handler's suborder space. Alphabetical. *)|Align_arbitrary_var_|Align_raw_->1200|Align_baseline->1200|Align_bottom->1201|Align_middle->1202|Align_sub->1203|Align_super->1204|Align_text_bottom->1205|Align_text_top->1206|Align_top->1207(* List utilities (priority 11) - after resize (~1M), before appearance
(~3M). Position, type, and image form separate property bands; candidate
names settle ties inside each band. *)|List_inside|List_outside->2_000_000|List_bracket_var_|List_bracket_|List_decimal|List_disc|List_none->2_010_000|List_image_bracket_var_|List_image_bracket_|List_image_raw_|List_image_none|List_image_url_->2_020_000(* Underline offset — negatives first, then positives, then auto *)|Underline_offset_neg_pxpx->50000+int_of_floatpx|Underline_offset_neg_arbitrary_->59989|Underline_offset_neg_var_->59990|Underline_offset_0->60000|Underline_offset_1->60001|Underline_offset_2->60002|Underline_offset_4->60003|Underline_offset_8->60004|Underline_offset_pxpx->60005+int_of_floatpx|Underline_offset_arbitrary_|Underline_offset_raw_->69989|Underline_offset_var_->69990|Underline_offset_auto->69999(* Both simple smoothing rules lead the placeholder pseudo-element band.
Keep them in their own slot: variant rules use their selector to settle
ties, while base rules retain handler order. *)|Antialiased->79999|Subpixel_antialiased->79999(* The legacy overflow-ellipsis alias leads the canonical text-overflow
candidates. Truncate sorts before overflow (priority 18) via a suborder
above alignment/gap's range. Tailwind ranks [text-overflow] at 293,
between the word-wrapping families (291/292) below and hyphens (294)
above - it already lands there without adjustment. *)|Overflow_ellipsis->8319|Text_clip->8320|Text_ellipsis->8321|Truncate->9_000_000(* priority 17, after alignment/gap (max ~2100) *)(* Text wrap - Tailwind ranks [text-wrap] at 290, right after letter-spacing
(289, Tracking's suborder tops out at 8306) and before overflow-wrap
(291, Break_normal below). Every variant here writes only [text-wrap], so
they tie and the class name breaks it alphabetically: balance, nowrap,
pretty, wrap.
Not [Property_order.last 290]: that mechanism suits a family whose
neighbours already use it (backgrounds, masks), where every rank in the
shared priority is on the same 1000-wide scale. Here Tracking,
Text_clip/Ellipsis and Whitespace stay on this file's local bare-integer
scale (82xx-83xx) and are not being touched by this fix, so a suborder on
the real-rank scale (290 * 1000 and up) would sort after all of them
regardless of true rank. Every family below that needs to interleave with
an unconverted neighbour stays on this same local scale for the same
reason. *)|Text_balance->8307|Text_nowrap->8307|Text_pretty->8307|Text_wrap->8307(* Break utilities - Tailwind ranks [overflow-wrap] at 291 and [word-break]
at 292. [break-normal] writes both, so it leads the 291-292 stretch;
lib/overflow_wrap.ml's wrap-anywhere/wrap-break-word/ wrap-normal write
only [overflow-wrap], same as [break-words], so all four tie with it at
8309 - a bare constant matching this file's local scale (see the
[Text_wrap] comment above). [break-all] and [break-keep] write only
[word-break] and tie at 8310. *)|Break_normal->8308|Break_words->8309|Break_all->8310|Break_keep->8310(* Overflow wrap - not real Tailwind utility names ([overflow-wrap-normal]
etc. rather than [wrap-normal]); real tailwindcss generates nothing for
them, so there is no oracle order to match. Left where they were. *)|Overflow_wrap_normal->9300|Overflow_wrap_anywhere->9301|Overflow_wrap_break_word->9302(* Hyphens - Tailwind ranks [hyphens] at 294, between text-overflow (293,
Text_ellipsis above) and white-space (295, Whitespace_* below); the
[-webkit-hyphens] declaration every variant also writes carries no rank
of its own, so all three tie on [hyphens] alone and the class name breaks
it alphabetically: auto, manual, none. *)|Hyphens_auto->8325|Hyphens_manual->8325|Hyphens_none->8325(* Font stretch shares one property slot after font style. *)|Font_stretch_percent_|Font_stretch_condensed|Font_stretch_expanded|Font_stretch_extra_condensed|Font_stretch_extra_expanded|Font_stretch_normal|Font_stretch_semi_condensed|Font_stretch_semi_expanded|Font_stretch_ultra_condensed|Font_stretch_ultra_expanded->8382(* Numeric variants - alphabetical order with normal-nums last *)|Diagonal_fractions->8390|Lining_nums->8391|Oldstyle_nums->8392|Ordinal->8393|Proportional_nums->8394|Slashed_zero->8395|Stacked_fractions->8396|Tabular_nums->8397|Normal_nums->8398(* Indent - Tailwind ranks [text-indent] at 282, between [text-align] (281)
and [vertical-align] (283), so the family sits at priority 24 with both:
[Text_align] holds 1001-1006 in the early handler, [Align_*] below holds
1200-1207, and one slot in between puts every spelling of [indent-*]
where Tailwind puts it. Bare rather than [Property_order.slot 282], for
the same reason as [Text_wrap] above: the neighbours it sorts against are
on this file's local scale.
The whole family shares that slot and [Sort.natural_compare] on the class
name separates it, which is the order Tailwind emits: negatives lead on
the [-], digit runs compare numerically so [indent-8] precedes
[indent-10], and the bracket and [px] spellings follow the bare steps.
One slot also keeps an [indent-<n>] of any size inside it, where an
offset derived from [n] would eventually reach [Align_*]. *)|Indent_|Indent_neg_|Indent_px|Indent_neg_px|Indent_arbitrary_|Indent_raw_|Indent_neg_arbitrary_->1100|Line_clampn->10000+n|Line_clamp_arbitrary_|Line_clamp_raw_->11000|Line_clamp_none->12000(* Content *)|Content_none->13000|Content_->10001|Content_squote_->10001|Content_raw_->10001|Content_named_->10002(* Shared utility implementations *)letunderline=style[text_decoration_lineUnderline]letoverline=style[text_decoration_lineOverline]letline_through=style[text_decoration_lineLine_through]letno_underline=style[text_decoration_lineNone]letdecoration_solid=style[text_decoration_styleSolid]letdecoration_double=style[text_decoration_styleDouble]letdecoration_dotted=style[text_decoration_styleDotted]letdecoration_dashed=style[text_decoration_styleDashed]letdecoration_wavy=style[text_decoration_styleWavy]letdecoration_thicknessn=style[text_decoration_thickness(Px(float_of_intn))]letdecoration_from_font=style[text_decoration_thicknessFrom_font]letdecoration_auto=style[text_decoration_thicknessAuto]letdecoration_bracket_thickness(len:Css.length)=style[text_decoration_thicknesslen]letdecoration_bracket_pct(len:Css.length)=style[text_decoration_thicknesslen]letdecoration_bracket_length_varv=letbare_name=Parse.extract_var_namevinletvar_ref:Css.lengthCss.var=Var.bracketbare_nameinstyle[text_decoration_thickness(Varvar_ref)]letdecoration_bracket_pct_var=decoration_bracket_length_varletdecoration_color?theme?(shade=500)(color:Color.color)=ifColor.is_custom_colorcolorthenletcss_color=Color.to_csscolorshadeinstyle[webkit_text_decoration_colorcss_color;text_decoration_colorcss_color;]elseletcolor_var=Color.property_color_var?theme~property_prefix:"text-decoration-color"colorshadeinletcolor_value=Color.property_color_value?theme~property_prefix:"text-decoration-color"colorshadeinletdecls,color=Color.bound?themecolor_varcolor_valueinstyle(decls@[webkit_text_decoration_colorcolor;text_decoration_colorcolor])letdecoration_color_with_opacity?theme(color:Color.color)shadeopacity=letpercent=Color.opacity_to_percentopacityinletscheme=Scheme.or_defaultthemeinletcolor_name=Color.scheme_color_namecolorshadeinmatchColor.opacity_keywordcolorwith|Somekeyword->letcolor=Color.apply_alphaopacitykeywordinstyle[webkit_text_decoration_colorcolor;text_decoration_colorcolor]|None->(matchScheme.hex_colorschemecolor_namewith|Somehex_value->(* Scheme has hex: fallback is hex+alpha, @supports has color-mix
with webkit *)lethex_with_alpha=Color.hex_with_alphahex_valuepercentinletfallback_decl=text_decoration_color(Css.hexhex_with_alpha)inletdecls,color=Color.bound?theme(Color.color_varcolorshade)(Css.hexhex_value)inletoklab_color=Color.mix_alphaopacitycolorinletwebkit_decl=webkit_text_decoration_coloroklab_colorinletoklab_decl=text_decoration_coloroklab_colorinletsupports_block=Color.color_mix_supports[webkit_decl;oklab_decl]instyle~rules:(Some[supports_block])(decls@[fallback_decl])|None->(* No scheme hex: use property-scoped variable *)letcolor_var=Color.property_color_var?theme~property_prefix:"text-decoration-color"colorshadeinletcolor_value=Color.property_color_value?theme~property_prefix:"text-decoration-color"colorshadein(* A project token may be any CSS colour, not necessarily a palette
colour that can be converted to a compile-time hex fallback, so
the modifier's alpha rides on an sRGB mix of the value itself. An
alpha read from a var has no percentage to mix in, leaving the
colour at full opacity. *)letfallback_color=ifColor.opacity_var_bare_ofopacity<>Nonethencolor_valueelseColor.opacity_fallback~percentcolorshadecolor_valueinletfallback_decl=text_decoration_colorfallback_colorinletdecls,color=Color.bound?themecolor_varcolor_valueinletoklab_color=Color.mix_alphaopacitycolorinletwebkit_decl=webkit_text_decoration_coloroklab_colorinletoklab_decl=text_decoration_coloroklab_colorinletsupports_block=Color.color_mix_supports[webkit_decl;oklab_decl]instyle~rules:(Some[supports_block])(decls@[fallback_decl]))letdecoration_transparent=style[text_decoration_color(Css.hex"#0000")]letdecoration_current=style[text_decoration_colorCurrent]letdecoration_inherit=style[webkit_text_decoration_colorInherit;text_decoration_colorInherit]letdecoration_current_with_opacityopacity=letfallback_decl=text_decoration_colorCurrentinletoklab_color=Color.mix_alphaopacityCss.Currentinletwebkit_decl=webkit_text_decoration_coloroklab_colorinletoklab_decl=text_decoration_coloroklab_colorinletsupports_block=Color.color_mix_supports[webkit_decl;oklab_decl]instyle~rules:(Some[supports_block])[fallback_decl]letdecoration_bracket_color_style~themec=letenhanced=Color.resolve_bracket_css_colorcinmatchColor.pre_color_mix_fallbackthemeenhancedwith|None->style~merge_key:"decoration-"[text_decoration_colorenhanced]|Somefallback->letsupports_block=Color.color_mix_supports[webkit_text_decoration_colorenhanced;text_decoration_colorenhanced;]instyle~merge_key:"decoration-"~rules:(Some[supports_block])[text_decoration_colorfallback](* An opacity modifier applies to the colour the bracket was parsed into. The
modifier read the bracket text back as a hex and answered black whenever
that failed, and it wrote the mix in place of the colour for every spelling
that is not a [#] hex, where Tailwind folds the alpha in. *)letdecoration_bracket_color_with_opacity~themecopacity=matchColor.bracket_color_opacity~themecopacitywith|Color.Foldedvalue->style~merge_key:"decoration-"[text_decoration_colorvalue]|Color.Guarded{fallback;mixed}->letsupports_block=Color.color_mix_supports[webkit_text_decoration_colormixed;text_decoration_colormixed]instyle~merge_key:"decoration-"~rules:(Some[supports_block])[text_decoration_colorfallback]letdecoration_bracket_var_stylev=letbare_name=Parse.extract_var_namevinletvar_color:Css.color=Css.Var(Var.bracketbare_name)instyle~merge_key:"decoration-"[webkit_text_decoration_colorvar_color;text_decoration_colorvar_color;]letdecoration_bracket_var_with_opacityvopacity=letbare_name=Parse.extract_var_namevinletvar_color:Css.color=Css.Var(Var.bracketbare_name)inletfallback_webkit=webkit_text_decoration_colorvar_colorinletfallback_decl=text_decoration_colorvar_colorinletoklab_color=Color.mix_alphaopacityvar_colorinletwebkit_decl=webkit_text_decoration_coloroklab_colorinletoklab_decl=text_decoration_coloroklab_colorinletsupports_block=Color.color_mix_supports[webkit_decl;oklab_decl]instyle~merge_key:"decoration-"~rules:(Some[supports_block])[fallback_webkit;fallback_decl]letwhitespace_normal=style[white_spaceNormal]letwhitespace_nowrap=style[white_spaceNowrap]letwhitespace_pre=style[white_spacePre]letwhitespace_pre_line=style[white_spacePre_line]letwhitespace_pre_wrap=style[white_spacePre_wrap]letwhitespace_break_spaces=style[white_spaceBreak_spaces]lettracking_tighter=lettheme_decl,theme_ref=Var.bindingtracking_tighter_var(Em(-0.05))inletchannel_decl=Var.settracking_var(Css.Vartheme_ref)inletproperty_rules=Var.property_ruletracking_var|>Option.to_list|>Css.concatinstyle~property_rules[theme_decl;channel_decl;letter_spacing(Css.Vartheme_ref)]lettracking_tight=(* Theme var: --tracking-tight: -0.025em *)lettheme_decl,theme_ref=Var.bindingtracking_tight_var(Em(-0.025))in(* Channel var: --tw-tracking: var(--tracking-tight) *)letchannel_decl=Var.settracking_var(Css.Vartheme_ref)in(* Property: letter-spacing: var(--tracking-tight) *)letproperty_rules=Var.property_ruletracking_var|>Option.to_list|>Css.concatinstyle~property_rules[theme_decl;channel_decl;letter_spacing(Css.Vartheme_ref)]lettracking_normal()=(* Tailwind's default theme defines --tracking-normal as 0em, not a unitless
0, so keep the explicit em unit. *)lettheme_decl,theme_ref=Var.bindingtracking_normal_var(Em0.0)inletchannel_decl=Var.settracking_var(Css.Vartheme_ref)inletproperty_rules=Var.property_ruletracking_var|>Option.to_list|>Css.concatinstyle~property_rules[theme_decl;channel_decl;letter_spacing(Css.Vartheme_ref)]lettracking_wide=lettheme_decl,theme_ref=Var.bindingtracking_wide_var(Em0.025)inletchannel_decl=Var.settracking_var(Css.Vartheme_ref)inletproperty_rules=Var.property_ruletracking_var|>Option.to_list|>Css.concatinstyle~property_rules[theme_decl;channel_decl;letter_spacing(Css.Vartheme_ref)]lettracking_themethemename=lettoken="tracking-"^nameinmatchScheme.theme_value(Sometheme)tokenwith|Stdlib.Option.None->style[]|Stdlib.Option.Someraw->(matchCss.parse_lengthrawwith|Stdlib.Option.None->style[]|Stdlib.Option.Somelen->lettheme_decl,theme_ref=Var.binding(tracking_named_varname)leninletchannel_decl=Var.settracking_var(Css.Vartheme_ref)inletproperty_rules=Var.property_ruletracking_var|>Option.to_list|>Css.concatinstyle~property_rules[theme_decl;channel_decl;letter_spacing(Css.Vartheme_ref)])lettracking_wider=lettheme_decl,theme_ref=Var.bindingtracking_wider_var(Em0.05)inletchannel_decl=Var.settracking_var(Css.Vartheme_ref)inletproperty_rules=Var.property_ruletracking_var|>Option.to_list|>Css.concatinstyle~property_rules[theme_decl;channel_decl;letter_spacing(Css.Vartheme_ref)]lettracking_widest=lettheme_decl,theme_ref=Var.bindingtracking_widest_var(Em0.1)inletchannel_decl=Var.settracking_var(Css.Vartheme_ref)inletproperty_rules=Var.property_ruletracking_var|>Option.to_list|>Css.concatinstyle~property_rules[theme_decl;channel_decl;letter_spacing(Css.Vartheme_ref)]letuppercase=style[text_transform(CaseUppercase)]letlowercase=style[text_transform(CaseLowercase)]letcapitalize=style[text_transform(CaseCapitalize)]letnormal_case=style[text_transformNone]lettracking_arbitrarylen=letchannel_decl=Var.settracking_varleninletproperty_rules=Var.property_ruletracking_var|>Option.to_list|>Css.concatinstyle~property_rules[channel_decl;letter_spacinglen]lettracking_raw?(negative=false)value=letvalue=ifnegativethen"calc("^value^" * -1)"elsevalueinletchannel_decl=Css.custom_property~layer:"utilities"(Var.css_nametracking_var)valueinletproperty_rules=Var.property_ruletracking_var|>Option.to_list|>Css.concatinletdeclarations=channel_decl::Option.to_list(Parse.opaque_declaration"letter-spacing"value)instyle~metadata:[Var.metadatatracking_var]~property_rulesdeclarationsletnegate_length:Css.length->Css.length=function|Pxn->Px(-.n)|Emn->Em(-.n)|Remn->Rem(-.n)|Cmn->Cm(-.n)|Mmn->Mm(-.n)|Ptn->Pt(-.n)|Pcn->Pc(-.n)|Exn->Ex(-.n)|Chn->Ch(-.n)|Lhn->Lh(-.n)|Vwn->Vw(-.n)|Vhn->Vh(-.n)|Pctn->Pct(-.n)|len->Calc(Calc.mul(Calc.lengthlen)(Calc.float(-1.)))letneg_tracking_arbitrarylen=(* --tw-tracking uses calc() form, letter-spacing uses direct negation *)letcalc_neg:Css.length=Calc(Calc.mul(Calc.lengthlen)(Calc.float(-1.)))inletdirect_neg=negate_lengthleninletchannel_decl=Var.settracking_varcalc_neginletproperty_rules=Var.property_ruletracking_var|>Option.to_list|>Css.concatinstyle~property_rules[channel_decl;letter_spacingdirect_neg]letunderline_offset_auto?theme()=matchScheme.theme_valuetheme"text-underline-offset-auto"with|Some_->letdecl,ref_=Var.bindingunderline_offset_auto_var(Auto:Css.length)instyle[decl;text_underline_offset(Varref_)]|None->style[text_underline_offset(Auto:Css.length)]letunderline_offset_0=style[text_underline_offsetZero]letunderline_offset_1=style[text_underline_offset(Px1.)]letunderline_offset_2=style[text_underline_offset(Px2.)]letunderline_offset_4=style[text_underline_offset(Px4.)]letunderline_offset_8=style[text_underline_offset(Px8.)]letantialiased=style[webkit_font_smoothingAntialiased;moz_osx_font_smoothingGrayscale]letsubpixel_antialiased=style[webkit_font_smoothingAuto;moz_osx_font_smoothingAuto]letlist_image_urlurl=style[list_style_image(Image(Urlurl))]lettext_indent_lengthlength=text_indent(Indent{length;hanging=false;each_line=false})letindentn=letspacing_decl,spacing_ref=Var.bindingTheme.spacing_varTheme.spacing_baseinletbase:Css.length_percentage=Length(Css.Varspacing_ref)inletcalc:Css.length_percentageCss.calc=Expr(Valbase,Mul,Numn)inletlength:Css.length_percentage=Calccalcinstyle[spacing_decl;text_indent_lengthlength]letindent_arbitrarys=matcharbitrary_length_after_hintswith|Somelen->style[text_indent_length(Lengthlen)]|None->style[text_indent_length(Length(Px0.))](* A negated arbitrary length is [calc(<value> * -1)], the spelling Tailwind
writes whatever the unit. [negate_length] still folds the sign for
[-indent-px], where the CLI writes [-1px] itself. *)letindent_neg_arbitrarys=matcharbitrary_length_after_hintswith|Somelen->style[text_indent_length(Length(Calc(Calc.mul(Calc.lengthlen)(Calc.float(-1.)))));]|None->style[text_indent_length(Length(Px0.))]letline_clampn=style[webkit_line_clamp(Linesn);webkit_box_orientVertical;displayWebkit_box;overflowHidden;]letline_clamp_rawvalue=letclamp=Option.to_list(Parse.opaque_declaration"-webkit-line-clamp"value)instyle(clamp@[webkit_box_orientVertical;displayWebkit_box;overflowHidden])(* A project [@theme] token is a namespace key, not a value Tailwind type-
checks at build time: any override of [--line-clamp-none], decimal or not,
switches the utility to the variable-driven form. Reading it with
[int_of_string_opt] let an OCaml-only spelling like [0x3] through by
accident and rejected a spelling like [banana] that Tailwind still honours,
so the branch no longer parses the value at all. *)letline_clamp_none_style?theme()=matchScheme.theme_valuetheme"line-clamp-none"with|Somevalue_str->letdecl=Css.custom_property~layer:"theme""--line-clamp-none"value_strinletref:Css.webkit_line_clampCss.var=Var.theme_ref"line-clamp-none"~default:(Css.Unset:Css.webkit_line_clamp)~default_css:"unset"instyle[decl;webkit_line_clamp(Varref);webkit_box_orientVertical;displayWebkit_box;overflowHidden;]|None->style[webkit_line_clampUnset;webkit_box_orientHorizontal;displayBlock;overflowVisible;](* content-none sets --tw-content: none and uses a literal [content: none], so
it never references var(--tw-content) and Tailwind emits no @property rule
for it (unlike the value/theme variants below, which do reference the var).
The set-based auto-collection is suppressed for --tw-content in
build.ml. *)letcontent_none=letcontent_decl,_content_ref=Var.bindingcontent_varNoneinstyle[content_decl;contentNone]letcontents=letvalue=Parse.decode_underscoressinletcontent_decl,content_ref=Var.bindingcontent_var(Stringvalue)inletproperty_rules=Var.property_rulescontent_varinstyle~property_rules[content_decl;content(Css.Varcontent_ref)](* content-<token> referencing a --content-<token> theme value (of_class only
builds this when the token exists in the theme). *)letcontent_named?themename=letvar_name="content-"^nameinletcontent_decl,content_ref=matchScheme.theme_valuethemevar_namewith|Some_->lettv=Var.themeCss.Contentvar_name~order:(6,60)inlettheme_decl,theme_ref=Var.bindingtv(String"")inletcd,cr=Var.bindingcontent_var(Vartheme_ref)in([theme_decl;cd],cr)|None->lettheme_ref:Css.contentCss.var=Var.theme_refvar_name~default:(String"":Css.content)~default_css:"\"\""inletcd,cr=Var.bindingcontent_var(Vartheme_ref)in([cd],cr)inletproperty_rules=Var.property_rulescontent_varinletc:Css.content=Css.Varcontent_refinstyle~property_rules(content_decl@[Css.contentc])letcontent_squotes=(* Single-quoted arbitrary content (content-['x']) keeps the single quote.
[_] is a space and [\_] a literal underscore. *)letvalue=Parse.decode_underscoressinletquoted:Css.content=Css.Quoted{value;quote='\'';repr=None}inletcontent_decl,content_ref=Var.bindingcontent_varquotedinletproperty_rules=Var.property_rulescontent_varinletc:Css.content=Css.Varcontent_refinstyle~property_rules[content_decl;Css.contentc](* Unquoted arbitrary content (content-[attr(before)]) binds the parsed value
verbatim. *)letcontent_raw(value:Css.content)=letcontent_decl,content_ref=Var.bindingcontent_varvalueinletproperty_rules=Var.property_rulescontent_varinstyle~property_rules[content_decl;Css.content(Css.Varcontent_ref)]letcontent_raw_valuevalue=letcontent_decl=Css.custom_property~layer:"utilities"(Var.css_namecontent_var)valueinletcontent_ref=Var.referencecontent_varinletproperty_rules=Var.property_rulescontent_varinstyle~metadata:[Var.metadatacontent_var]~property_rules[content_decl;Css.content(Css.Varcontent_ref)]letalign_baseline=style[vertical_alignBaseline]letalign_top=style[vertical_alignTop]letalign_middle=style[vertical_alignMiddle]letalign_bottom=style[vertical_alignBottom]letalign_text_top=style[vertical_alignText_top]letalign_text_bottom=style[vertical_alignText_bottom]letalign_sub=style[vertical_alignSub]letalign_super=style[vertical_alignSuper]letlist_none?theme()=letvar_name="list-style-type-none"inletref=Var.theme_refvar_name~default:(None:Css.list_style_type)~default_css:"none"inmatchScheme.theme_valuethemevar_namewith|Somevalue->lettheme_decl=Css.custom_property~layer:"theme"("--"^var_name)valueinstyle[theme_decl;list_style_type(Varref)](* Without a theme override Tailwind writes the keyword, not a reference to
a token nothing declares. *)|None->style[list_style_typeNone]letlist_bracket_vars=letinner=Parse.extract_var_namesinletref:Css.list_style_typeCss.var=Var.bracketinnerinstyle[list_style_type(Varref)]letlist_disc=style[list_style_typeDisc]letlist_decimal=style[list_style_typeDecimal]letlist_inside=style[list_style_positionInside]letlist_outside=style[list_style_positionOutside]letlist_image_bracket_vars=(* Written either as [[var(--x)]] or as the [(--x)] shorthand. *)letinner=ifParse.is_bare_varsthenletbare=Parse.bare_var_innersinOption.value~default:bare(Parse.bare_namebare)elseParse.extract_var_namesinletref:Css.list_style_imageCss.var=Var.bracketinnerinstyle[list_style_image(Varref)]letlist_image_none?theme()=letvar_name="list-style-image-none"inletref=Var.theme_refvar_name~default:(None:Css.list_style_image)~default_css:"none"inmatchScheme.theme_valuethemevar_namewith|Somevalue->lettheme_decl=Css.custom_property~layer:"theme"("--"^var_name)valueinstyle[theme_decl;list_style_image(Varref)]|None->style[list_style_imageNone]lettext_ellipsis=style[text_overflowEllipsis]lettext_clip=style[text_overflowClip]lettruncate=style[text_overflowEllipsis;white_spaceNowrap;overflowHidden]lettext_wrap=style[Css.text_wrapWrap]lettext_nowrap=style[Css.text_wrapNo_wrap]lettext_balance=style[Css.text_wrapBalance]lettext_pretty=style[Css.text_wrapPretty]letbreak_normal=style[overflow_wrapNormal;word_breakNormal]letbreak_words=style[overflow_wrapBreak_word]letbreak_all=style[word_breakBreak_all]letbreak_keep=style[word_breakKeep_all]letoverflow_wrap_normal=style[overflow_wrapNormal]letoverflow_wrap_anywhere=style[overflow_wrapAnywhere]letoverflow_wrap_break_word=style[overflow_wrapBreak_word]lethyphens_none=style[webkit_hyphensNone;hyphensNone]lethyphens_manual=style[webkit_hyphensManual;hyphensManual]lethyphens_auto=style[webkit_hyphensAuto;hyphensAuto]letfont_stretch_ultra_condensed=style[font_stretchUltra_condensed]letfont_stretch_extra_condensed=style[font_stretchExtra_condensed]letfont_stretch_condensed=style[font_stretchCondensed]letfont_stretch_semi_condensed=style[font_stretchSemi_condensed]letfont_stretch_normal=style[font_stretchNormal]letfont_stretch_semi_expanded=style[font_stretchSemi_expanded]letfont_stretch_expanded=style[font_stretchExpanded]letfont_stretch_extra_expanded=style[font_stretchExtra_expanded]letfont_stretch_ultra_expanded=style[font_stretchUltra_expanded]letfont_stretch_percentn=style[font_stretch(Pct(float_of_intn))]letnormal_nums=style[font_variant_numeric(Tokens[Normal])]letfont_variant_numeric_utilityvar_to_setvalue=(* Use the shared default theme *)lettheme=default_font_variant_themein(* Override the specific variable being set - with EMPTY fallback to match
Tailwind *)letupdated_theme=matchvar_to_setwith|`Ordinal->letnew_decl,new_ref=Var.bindingordinal_var~fallback:Css.Emptyvaluein{themewithordinal=(new_decl,new_ref)}|`Slashed->letnew_decl,new_ref=Var.bindingslashed_var~fallback:Css.Emptyvaluein{themewithslashed=(new_decl,new_ref)}|`Figure->letnew_decl,new_ref=Var.bindingfigure_var~fallback:Css.Emptyvaluein{themewithfigure=(new_decl,new_ref)}|`Spacing->letnew_decl,new_ref=Var.bindingspacing_var~fallback:Css.Emptyvaluein{themewithspacing=(new_decl,new_ref)}|`Fraction->letnew_decl,new_ref=Var.bindingfraction_var~fallback:Css.Emptyvaluein{themewithfraction=(new_decl,new_ref)}in(* Extract only the active declaration (the one being set) *)letactive_decl=matchvar_to_setwith|`Ordinal->fstupdated_theme.ordinal|`Slashed->fstupdated_theme.slashed|`Figure->fstupdated_theme.figure|`Spacing->fstupdated_theme.spacing|`Fraction->fstupdated_theme.fractionin(* Compose the font-variant-numeric value using all var references *)letcomposed_value=Composed{ordinal=Some(Css.Var(sndupdated_theme.ordinal));slashed_zero=Some(Css.Var(sndupdated_theme.slashed));numeric_figure=Some(Css.Var(sndupdated_theme.figure));numeric_spacing=Some(Css.Var(sndupdated_theme.spacing));numeric_fraction=Some(Css.Var(sndupdated_theme.fraction));}in(* Get property rules for animation support *)letproperty_rules=List.filter_map(funx->x)[Var.property_ruleordinal_var;Var.property_ruleslashed_var;Var.property_rulefigure_var;Var.property_rulespacing_var;Var.property_rulefraction_var;]instyle~property_rules:(Css.concatproperty_rules)[active_decl;font_variant_numericcomposed_value]letordinal=font_variant_numeric_utility`OrdinalOrdinalletslashed_zero=font_variant_numeric_utility`SlashedSlashed_zeroletlining_nums=font_variant_numeric_utility`FigureLining_numsletoldstyle_nums=font_variant_numeric_utility`FigureOldstyle_numsletproportional_nums=font_variant_numeric_utility`SpacingProportional_numslettabular_nums=font_variant_numeric_utility`SpacingTabular_numsletdiagonal_fractions=font_variant_numeric_utility`FractionDiagonal_fractionsletstacked_fractions=font_variant_numeric_utility`FractionStacked_fractionsletto_styletheme=letdecoration_color?shadecolor=decoration_color~theme?shadecolorinletdecoration_color_with_opacitycolorshadeopacity=decoration_color_with_opacity~themecolorshadeopacityinletunderline_offset_auto()=underline_offset_auto~theme()inletline_clamp_none_style()=line_clamp_none_style~theme()inletcontent_namedname=content_named~themenameinletlist_none()=list_none~theme()inletlist_image_none()=list_image_none~theme()infunction|Decoration_color(color,None)->decoration_colorcolor|Decoration_color(color,Someshade)->decoration_color~shadecolor|Decoration_color_opacity(color,shade,opacity)->decoration_color_with_opacitycolorshadeopacity|Decoration_transparent->decoration_transparent|Decoration_current->decoration_current|Decoration_current_opacityopacity->decoration_current_with_opacityopacity|Decoration_inherit->decoration_inherit|Decoration_bracket_color(_,c)->decoration_bracket_color_style~themec|Decoration_bracket_invalid_color_->style[]|Decoration_bracket_color_opacity(_,c,opacity)->decoration_bracket_color_with_opacity~themecopacity|Decoration_bracket_varv->decoration_bracket_var_stylev|Decoration_bracket_var_opacity(v,opacity)->decoration_bracket_var_with_opacityvopacity|Decoration_bracket_color_varv->decoration_bracket_var_stylev|Decoration_bracket_color_var_opacity(v,opacity)->decoration_bracket_var_with_opacityvopacity|Underline->underline|Overline->overline|Line_through->line_through|No_underline->no_underline|Decoration_solid->decoration_solid|Decoration_double->decoration_double|Decoration_dotted->decoration_dotted|Decoration_dashed->decoration_dashed|Decoration_wavy->decoration_wavy|Decoration_thicknessn->decoration_thicknessn|Decoration_from_font->decoration_from_font|Decoration_auto->decoration_auto|Decoration_bracket_thickness(_,len)->decoration_bracket_thicknesslen|Decoration_bracket_pct(_,len)->decoration_bracket_pctlen|Decoration_bracket_length_varv->decoration_bracket_length_varv|Decoration_bracket_pct_varv->decoration_bracket_pct_varv|Tracking_arbitrary(_,len)->tracking_arbitrarylen|Neg_tracking_arbitrary(_,len)->neg_tracking_arbitrarylen|Tracking_raw(_,value)->tracking_rawvalue|Neg_tracking_raw(_,value)->tracking_raw~negative:truevalue|Tracking_varv->letbare_name=Parse.extract_var_namevinletvar_ref:Css.lengthCss.var=Var.bracketbare_nameinletchannel_decl=Var.settracking_var(Css.Varvar_ref)inletproperty_rules=Var.property_ruletracking_var|>Option.to_list|>Css.concatinstyle~property_rules[channel_decl;letter_spacing(Css.Varvar_ref)]|Neg_tracking_varv->letbare_name=Parse.extract_var_namevinletneg_len:Css.length=Calc(Calc.mul(Calc.varbare_name)(Calc.float(-1.)))inletchannel_decl=Var.settracking_varneg_leninletproperty_rules=Var.property_ruletracking_var|>Option.to_list|>Css.concatinstyle~property_rules[channel_decl;letter_spacingneg_len]|Tracking_tighter->tracking_tighter|Tracking_tight->tracking_tight|Tracking_normal->tracking_normal()|Tracking_wide->tracking_wide|Tracking_wider->tracking_wider|Tracking_widest->tracking_widest|Tracking_themename->tracking_themethemename|Uppercase->uppercase|Lowercase->lowercase|Capitalize->capitalize|Normal_case->normal_case|Whitespace_normal->whitespace_normal|Whitespace_nowrap->whitespace_nowrap|Whitespace_pre->whitespace_pre|Whitespace_pre_line->whitespace_pre_line|Whitespace_pre_wrap->whitespace_pre_wrap|Whitespace_break_spaces->whitespace_break_spaces|Align_arbitrary_varvar_str->letbare_name=Parse.extract_var_namevar_strinstyle[vertical_align(Var(Var.bracketbare_name))]|Align_raw(_,v)->style(Option.to_list(Parse.opaque_declaration"vertical-align"v))|Align_baseline->align_baseline|Align_top->align_top|Align_middle->align_middle|Align_bottom->align_bottom|Align_text_top->align_text_top|Align_text_bottom->align_text_bottom|Align_sub->align_sub|Align_super->align_super|List_none->list_none()|List_disc->list_disc|List_decimal->list_decimal|List_bracket_vars->list_bracket_vars|List_bracket(_,t)->style[list_style_typet]|List_inside->list_inside|List_outside->list_outside|List_image_none->list_image_none()|List_image_bracket_vars->list_image_bracket_vars|List_image_bracket(_,i)->style[list_style_imagei]|List_image_raw(_,v)->style(Option.to_list(Parse.opaque_declaration"list-style-image"v))|List_image_urlurl->list_image_urlurl|Underline_offset_auto->underline_offset_auto()|Underline_offset_0->underline_offset_0|Underline_offset_1->underline_offset_1|Underline_offset_2->underline_offset_2|Underline_offset_4->underline_offset_4|Underline_offset_8->underline_offset_8|Underline_offset_pxpx->style[text_underline_offset(Pxpx)]|Underline_offset_arbitrary(_,len)->style[text_underline_offsetlen]|Underline_offset_raw(_,v)->style(Option.to_list(Parse.opaque_declaration"text-underline-offset"v))|Underline_offset_varv->letbare_name=Parse.extract_var_namevinletvar_ref:Css.lengthCss.var=Var.bracketbare_nameinstyle[text_underline_offset(Varvar_ref)]|Underline_offset_neg_pxpx->style[text_underline_offset(Calc(Calc.mul(Calc.length(Pxpx))(Calc.float(-1.))));]|Underline_offset_neg_arbitrary(_,len)->style[text_underline_offset(Calc(Calc.mul(Calc.lengthlen)(Calc.float(-1.))));]|Underline_offset_neg_varv->letbare_name=Parse.extract_var_namevinstyle[text_underline_offset(Calc(Calc.mul(Calc.varbare_name)(Calc.float(-1.))));]|Antialiased->antialiased|Subpixel_antialiased->subpixel_antialiased|Text_ellipsis->text_ellipsis|Overflow_ellipsis->text_ellipsis|Text_clip->text_clip|Truncate->truncate|Text_wrap->text_wrap|Text_nowrap->text_nowrap|Text_balance->text_balance|Text_pretty->text_pretty|Break_normal->break_normal|Break_words->break_words|Break_all->break_all|Break_keep->break_keep|Overflow_wrap_normal->overflow_wrap_normal|Overflow_wrap_anywhere->overflow_wrap_anywhere|Overflow_wrap_break_word->overflow_wrap_break_word|Hyphens_none->hyphens_none|Hyphens_manual->hyphens_manual|Hyphens_auto->hyphens_auto|Font_stretch_ultra_condensed->font_stretch_ultra_condensed|Font_stretch_extra_condensed->font_stretch_extra_condensed|Font_stretch_condensed->font_stretch_condensed|Font_stretch_semi_condensed->font_stretch_semi_condensed|Font_stretch_normal->font_stretch_normal|Font_stretch_semi_expanded->font_stretch_semi_expanded|Font_stretch_expanded->font_stretch_expanded|Font_stretch_extra_expanded->font_stretch_extra_expanded|Font_stretch_ultra_expanded->font_stretch_ultra_expanded|Font_stretch_percentn->font_stretch_percentn|Normal_nums->normal_nums|Ordinal->ordinal|Slashed_zero->slashed_zero|Lining_nums->lining_nums|Oldstyle_nums->oldstyle_nums|Proportional_nums->proportional_nums|Tabular_nums->tabular_nums|Diagonal_fractions->diagonal_fractions|Stacked_fractions->stacked_fractions|Indentn->indentn|Indent_negn->indent(-.n)|Indent_px->style[text_indent_length(Length(Px1.))]|Indent_neg_px->style[text_indent_length(Length(Px(-1.)))]|Indent_arbitrarys->indent_arbitrarys|Indent_raw(_,v)->style(Option.to_list(Parse.opaque_declaration"text-indent"v))|Indent_neg_arbitrarys->indent_neg_arbitrarys|Line_clampn->line_clampn|Line_clamp_arbitraryn->line_clampn|Line_clamp_raw(_,value)->line_clamp_rawvalue|Line_clamp_none->line_clamp_none_style()|Content_none->content_none|Contents->contents|Content_raw(_,`Contentc)->content_rawc|Content_raw(_,`Rawvalue)->content_raw_valuevalue|Content_squotes->content_squotes|Content_namedname->content_namednameletexamples=[Align_baseline;Antialiased;Break_normal;Capitalize;Content_none;Decoration_transparent;Decoration_solid;Decoration_auto;Font_stretch_normal;Hyphens_none;Indent_px;Line_clamp_none;Line_through;List_none;List_image_none;List_inside;Normal_nums;Overflow_wrap_normal;Text_balance;Text_ellipsis;Text_nowrap;Tracking_normal;Underline_offset_auto;Whitespace_normal;]endmoduleEarly_utility=Utility.Make(Typography_early)moduleLate_utility=Utility.Make(Typography_late)(* Public API - using appropriate handler for each utility *)(* Early typography utilities - priority 22 *)letutility_early=Early_utility.vlettext_xs=utility_earlyTypography_early.Text_xslettext_sm=utility_earlyTypography_early.Text_smlettext_base=utility_earlyTypography_early.Text_baselettext_lg=utility_earlyTypography_early.Text_lglettext_xl=utility_earlyTypography_early.Text_xllettext_2xl=utility_earlyTypography_early.Text_2xllettext_3xl=utility_earlyTypography_early.Text_3xllettext_4xl=utility_earlyTypography_early.Text_4xllettext_5xl=utility_earlyTypography_early.Text_5xllettext_6xl=utility_earlyTypography_early.Text_6xllettext_7xl=utility_earlyTypography_early.Text_7xllettext_8xl=utility_earlyTypography_early.Text_8xllettext_9xl=utility_earlyTypography_early.Text_9xlletfont_medium=utility_earlyTypography_early.Font_mediumletfont_thin=utility_earlyTypography_early.Font_thinletfont_extralight=utility_earlyTypography_early.Font_extralightletfont_light=utility_earlyTypography_early.Font_lightletfont_normal=utility_earlyTypography_early.Font_normalletfont_semibold=utility_earlyTypography_early.Font_semiboldletfont_bold=utility_earlyTypography_early.Font_boldletfont_extrabold=utility_earlyTypography_early.Font_extraboldletfont_black=utility_earlyTypography_early.Font_blackletfont_mono=utility_early(Typography_early.Font_monofalse)letfont_sans=utility_early(Typography_early.Font_sansfalse)letfont_serif=utility_early(Typography_early.Font_seriffalse)letitalic=utility_earlyTypography_early.Italicletnot_italic=utility_earlyTypography_early.Not_italiclettext_left=utility_earlyTypography_early.Text_leftlettext_center=utility_earlyTypography_early.Text_centerlettext_right=utility_earlyTypography_early.Text_rightlettext_justify=utility_earlyTypography_early.Text_justifylettext_start=utility_earlyTypography_early.Text_startlettext_end=utility_earlyTypography_early.Text_endletleading_none=utility_earlyTypography_early.Leading_noneletleading_tight=utility_earlyTypography_early.Leading_tightletleading_snug=utility_earlyTypography_early.Leading_snugletleading_normal=utility_earlyTypography_early.Leading_normalletleading_relaxed=utility_earlyTypography_early.Leading_relaxedletleading_loose=utility_earlyTypography_early.Leading_looseletleadingn=utility_early(Typography_early.Leadingn)letleading'n=utility_early(Typography_early.Leading_stepn)(* Late typography utilities - priority 24 *)letutility_late=Late_utility.vletdecoration_color?(shade=500)color=Color.check_shade~utility:"decoration_color"colorshade;utility_late(Typography_late.Decoration_color(color,Someshade))letunderline=utility_lateTypography_late.Underlineletoverline=utility_lateTypography_late.Overlineletline_through=utility_lateTypography_late.Line_throughletno_underline=utility_lateTypography_late.No_underlineletdecoration_solid=utility_lateTypography_late.Decoration_solidletdecoration_double=utility_lateTypography_late.Decoration_doubleletdecoration_dotted=utility_lateTypography_late.Decoration_dottedletdecoration_dashed=utility_lateTypography_late.Decoration_dashedletdecoration_wavy=utility_lateTypography_late.Decoration_wavyletdecoration_thicknessn=utility_late(Typography_late.Decoration_thicknessn)letdecoration_from_font=utility_lateTypography_late.Decoration_from_fontletwhitespace_normal=utility_lateTypography_late.Whitespace_normalletwhitespace_nowrap=utility_lateTypography_late.Whitespace_nowrapletwhitespace_pre=utility_lateTypography_late.Whitespace_preletwhitespace_pre_line=utility_lateTypography_late.Whitespace_pre_lineletwhitespace_pre_wrap=utility_lateTypography_late.Whitespace_pre_wrapletwhitespace_break_spaces=utility_lateTypography_late.Whitespace_break_spaceslettracking_tighter=utility_lateTypography_late.Tracking_tighterlettracking_tight=utility_lateTypography_late.Tracking_tightlettracking_normal=utility_lateTypography_late.Tracking_normallettracking_wide=utility_lateTypography_late.Tracking_widelettracking_wider=utility_lateTypography_late.Tracking_widerlettracking_widest=utility_lateTypography_late.Tracking_widestletuppercase=utility_lateTypography_late.Uppercaseletlowercase=utility_lateTypography_late.Lowercaseletcapitalize=utility_lateTypography_late.Capitalizeletnormal_case=utility_lateTypography_late.Normal_caseletunderline_offsetn=utility_late(Typography_late.Underline_offset_px(float_of_intn))letunderline_offset_auto=utility_lateTypography_late.Underline_offset_autoletunderline_offset_0=utility_lateTypography_late.Underline_offset_0letunderline_offset_1=utility_lateTypography_late.Underline_offset_1letunderline_offset_2=utility_lateTypography_late.Underline_offset_2letunderline_offset_4=utility_lateTypography_late.Underline_offset_4letunderline_offset_8=utility_lateTypography_late.Underline_offset_8letantialiased=utility_lateTypography_late.Antialiasedletsubpixel_antialiased=utility_lateTypography_late.Subpixel_antialiasedletalign_baseline=utility_lateTypography_late.Align_baselineletalign_top=utility_lateTypography_late.Align_topletalign_middle=utility_lateTypography_late.Align_middleletalign_bottom=utility_lateTypography_late.Align_bottomletalign_text_top=utility_lateTypography_late.Align_text_topletalign_text_bottom=utility_lateTypography_late.Align_text_bottomletalign_sub=utility_lateTypography_late.Align_subletalign_super=utility_lateTypography_late.Align_superletlist_none=utility_lateTypography_late.List_noneletlist_disc=utility_lateTypography_late.List_discletlist_decimal=utility_lateTypography_late.List_decimalletlist_inside=utility_lateTypography_late.List_insideletlist_outside=utility_lateTypography_late.List_outsideletlist_image_none=utility_lateTypography_late.List_image_noneletlist_image_urlurl=utility_late(Typography_late.List_image_urlurl)letindent'n=utility_late(Typography_late.Indentn)letindentn=indent'(float_of_intn)letline_clampn=utility_late(Typography_late.Line_clampn)letcontent_none=utility_lateTypography_late.Content_noneletcontents=utility_late(Typography_late.Contents)lettext_ellipsis=utility_lateTypography_late.Text_ellipsislettext_clip=utility_lateTypography_late.Text_cliplettext_wrap=utility_lateTypography_late.Text_wraplettext_nowrap=utility_lateTypography_late.Text_nowraplettext_balance=utility_lateTypography_late.Text_balancelettext_pretty=utility_lateTypography_late.Text_prettyletbreak_normal=utility_lateTypography_late.Break_normalletbreak_words=utility_lateTypography_late.Break_wordsletbreak_all=utility_lateTypography_late.Break_allletbreak_keep=utility_lateTypography_late.Break_keepletoverflow_wrap_normal=utility_lateTypography_late.Overflow_wrap_normalletoverflow_wrap_anywhere=utility_lateTypography_late.Overflow_wrap_anywhereletoverflow_wrap_break_word=utility_lateTypography_late.Overflow_wrap_break_wordlethyphens_none=utility_lateTypography_late.Hyphens_nonelethyphens_manual=utility_lateTypography_late.Hyphens_manuallethyphens_auto=utility_lateTypography_late.Hyphens_autoletfont_stretch_ultra_condensed=utility_lateTypography_late.Font_stretch_ultra_condensedletfont_stretch_extra_condensed=utility_lateTypography_late.Font_stretch_extra_condensedletfont_stretch_condensed=utility_lateTypography_late.Font_stretch_condensedletfont_stretch_semi_condensed=utility_lateTypography_late.Font_stretch_semi_condensedletfont_stretch_normal=utility_lateTypography_late.Font_stretch_normalletfont_stretch_semi_expanded=utility_lateTypography_late.Font_stretch_semi_expandedletfont_stretch_expanded=utility_lateTypography_late.Font_stretch_expandedletfont_stretch_extra_expanded=utility_lateTypography_late.Font_stretch_extra_expandedletfont_stretch_ultra_expanded=utility_lateTypography_late.Font_stretch_ultra_expandedletfont_stretch_percentn=utility_late(Typography_late.Font_stretch_percentn)(** {1 Numeric Variants} *)letnormal_nums=utility_lateTypography_late.Normal_numsletordinal=utility_lateTypography_late.Ordinalletslashed_zero=utility_lateTypography_late.Slashed_zeroletlining_nums=utility_lateTypography_late.Lining_numsletoldstyle_nums=utility_lateTypography_late.Oldstyle_numsletproportional_nums=utility_lateTypography_late.Proportional_numslettabular_nums=utility_lateTypography_late.Tabular_numsletdiagonal_fractions=utility_lateTypography_late.Diagonal_fractionsletstacked_fractions=utility_lateTypography_late.Stacked_fractions