[{"data":1,"prerenderedAt":68805},["ShallowReactive",2],{"page-\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransitioning-display-with-allow-discrete\u002F":3,"content-all-pages":1461},{"id":4,"title":5,"body":6,"description":1443,"extension":1444,"meta":1445,"navigation":333,"path":1457,"seo":1458,"stem":1459,"__hash__":1460},"content\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransitioning-display-with-allow-discrete\u002Findex.md","Transitioning display with transition-behavior: allow-discrete and @starting-style",{"type":7,"value":8,"toc":1433},"minimark",[9,13,43,46,49,54,90,114,116,120,132,256,258,262,280,436,899,925,927,931,967,969,973,990,1294,1296,1300,1311,1313,1317,1344,1358,1364,1386,1388,1392,1429],[10,11,5],"h1",{"id":12},"transitioning-display-with-transition-behavior-allow-discrete-and-starting-style",[14,15,16,17,21,22,25,26,31,32,35,36,39,40,42],"p",{},"For years one animation stayed stubbornly out of reach in pure CSS: smoothly removing an element from the page. You could fade something in, but fading it out and then setting ",[18,19,20],"code",{},"display: none"," meant the element vanished the instant the display value changed, cutting the exit transition short. Developers reached for JavaScript ",[18,23,24],{},"setTimeout"," calls timed to the transition, or kept invisible elements in the layout forever. This guide, part of the ",[27,28,30],"a",{"href":29},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002F","CSS Transition Fundamentals"," section, shows how ",[18,33,34],{},"transition-behavior: allow-discrete"," together with ",[18,37,38],{},"@starting-style"," finally make JavaScript-free enter and exit transitions possible, even when the element toggles to and from ",[18,41,20],{},".",[14,44,45],{},"The exact problem: a popover, dropdown, or modal that should fade and scale in when shown, and fade and scale out before leaving the document — with no script orchestrating the timing.",[47,48],"hr",{},[50,51,53],"h2",{"id":52},"why-this-is-a-css-problem-and-not-a-javascript-one","Why this is a CSS problem and not a JavaScript one",[14,55,56,57,60,61,65,66,69,70,73,74,77,78,80,81,84,85,89],{},"Two distinct obstacles block a CSS-only exit. First, ",[18,58,59],{},"display"," is a ",[62,63,64],"strong",{},"discrete"," (non-interpolatable) property. There is no halfway point between ",[18,67,68],{},"block"," and ",[18,71,72],{},"none",", so by default the browser flips it at the start of the transition, removing the element before ",[18,75,76],{},"opacity"," has a chance to animate. Second, when an element first appears — whether on initial render or when it switches from ",[18,79,20],{}," to ",[18,82,83],{},"display: block"," — there is no previous computed style to transition ",[86,87,88],"em",{},"from",", so the browser jumps straight to the final state and the entry animation never plays.",[14,91,92,93,95,96,98,99,69,101,104,105,107,108,110,111,113],{},"The platform now solves both directly. ",[18,94,34],{}," tells the engine to defer the discrete flip until the end of the transition, keeping ",[18,97,83],{}," applied while ",[18,100,76],{},[18,102,103],{},"transform"," animate, then switching to ",[18,106,72],{}," on the final frame. ",[18,109,38],{}," supplies the \"before\" computed values an element should animate from the first time it becomes visible. Used together they cover the full lifecycle without a single line of script, which keeps the interaction resilient, accessible by default, and free of the timing drift that ",[18,112,24],{},"-based approaches always risk. The behaviour still degrades cleanly: where the features are absent, the element simply appears and disappears at once.",[47,115],{},[50,117,119],{"id":118},"the-enter-and-exit-timeline","The enter and exit timeline",[14,121,122,123,125,126,128,129,131],{},"The diagram traces what the browser does across an entry and an exit when both features are in play. On entry it reads the ",[18,124,38],{}," values, then transitions to the visible state. On exit it animates the visible properties while holding ",[18,127,59],{},", flipping it to ",[18,130,72],{}," only at the end.",[133,134,140,141,140,145,140,149,140,140,156,140,162,140,170,140,179,140,184,140,189,140,196,140,200,140,204,140,140,207,140,212,140,215,140,218,140,223,140,227,140,230,140,233,140,236,140,239],"svg",{"viewBox":135,"role":136,"ariaLabel":137,"xmlns":138,"style":139},"0 0 720 360","img","Timeline of an element entering and exiting using starting-style and allow-discrete","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;height:auto;max-width:720px;margin:2rem 0","\n  ",[142,143,144],"title",{},"Enter and exit transition timeline",[146,147,148],"desc",{},"A two-track timeline showing entry using @starting-style and exit using allow-discrete, with the discrete display flip held until the final frame.",[150,151,155],"text",{"x":152,"y":153,"style":154},"360","28","text-anchor:middle;fill:currentColor;font:700 18px sans-serif","Enter \u002F exit lifecycle",[150,157,161],{"x":158,"y":159,"style":160},"24","86","text-anchor:start;fill:#7aa2ff;font:600 14px sans-serif","ENTER",[163,164],"line",{"x1":158,"y1":165,"x2":166,"y2":165,"stroke":167,"strokeWidth":168,"opacity":169},"100","696","currentColor","1.5","0.4",[171,172],"rect",{"x":158,"y":173,"width":174,"height":175,"rx":176,"fill":177,"opacity":178},"108","200","46","8","#7aa2ff","0.18",[150,180,38],{"x":181,"y":182,"style":183},"124","130","text-anchor:middle;fill:currentColor;font:12px ui-monospace,monospace",[150,185,188],{"x":181,"y":186,"style":187},"147","text-anchor:middle;fill:currentColor;font:11px sans-serif;opacity:0.75","opacity:0; scale .9",[163,190],{"x1":191,"y1":192,"x2":193,"y2":192,"stroke":177,"strokeWidth":194,"markerEnd":195},"224","131","476","2","url(#arr)",[171,197],{"x":198,"y":173,"width":174,"height":175,"rx":176,"fill":177,"opacity":199},"496","0.3",[150,201,203],{"x":202,"y":182,"style":183},"596","visible state",[150,205,206],{"x":202,"y":186,"style":187},"opacity:1; scale 1",[150,208,211],{"x":158,"y":209,"style":210},"222","text-anchor:start;fill:currentColor;font:600 14px sans-serif","EXIT",[163,213],{"x1":158,"y1":214,"x2":166,"y2":214,"stroke":167,"strokeWidth":168,"opacity":169},"236",[171,216],{"x":158,"y":217,"width":174,"height":175,"rx":176,"fill":177,"opacity":199},"244",[150,219,222],{"x":181,"y":220,"style":221},"266","text-anchor:middle;fill:currentColor;font:11px sans-serif;opacity:0.85","visible, display:block",[150,224,226],{"x":181,"y":225,"style":187},"283","animate opacity out",[163,228],{"x1":191,"y1":229,"x2":193,"y2":229,"stroke":167,"strokeWidth":194,"markerEnd":195},"267",[171,231],{"x":198,"y":217,"width":174,"height":175,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},"0.6",[150,234,235],{"x":202,"y":220,"style":183},"display:none",[150,237,238],{"x":202,"y":225,"style":187},"held to last frame",[240,241,242,243,140],"defs",{},"\n    ",[244,245,251,252,242],"marker",{"id":246,"markerWidth":247,"markerHeight":247,"refX":248,"refY":249,"orient":250},"arr","9","7","4","auto","\n      ",[253,254],"path",{"d":255,"fill":167},"M0 0 L8 4 L0 8 z",[47,257],{},[50,259,261],{"id":260},"a-complete-working-implementation-a-css-only-popover","A complete working implementation: a CSS-only popover",[14,263,264,265,268,269,272,273,276,277,42],{},"The example uses the native ",[18,266,267],{},"popover"," attribute so there is genuinely zero JavaScript — the button toggles the popover, and CSS handles both transitions. The same technique applies to ",[18,270,271],{},"[open]"," toggles, ",[18,274,275],{},":has()","-driven panels, or the native ",[18,278,279],{},"\u003Cdialog>",[281,282,287],"pre",{"className":283,"code":284,"language":285,"meta":286,"style":286},"language-html shiki shiki-themes github-light github-dark","\u003Cbutton popovertarget=\"menu\" class=\"trigger\">Open menu\u003C\u002Fbutton>\n\n\u003Cdiv id=\"menu\" popover class=\"pop\">\n  \u003Cp class=\"pop__title\">Account\u003C\u002Fp>\n  \u003Cbutton type=\"button\">Profile\u003C\u002Fbutton>\n  \u003Cbutton type=\"button\">Sign out\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n","html","",[18,288,289,328,335,362,384,406,426],{"__ignoreMap":286},[290,291,293,297,301,305,308,312,315,317,320,323,325],"span",{"class":163,"line":292},1,[290,294,296],{"class":295},"sVt8B","\u003C",[290,298,300],{"class":299},"s9eBZ","button",[290,302,304],{"class":303},"sScJk"," popovertarget",[290,306,307],{"class":295},"=",[290,309,311],{"class":310},"sZZnC","\"menu\"",[290,313,314],{"class":303}," class",[290,316,307],{"class":295},[290,318,319],{"class":310},"\"trigger\"",[290,321,322],{"class":295},">Open menu\u003C\u002F",[290,324,300],{"class":299},[290,326,327],{"class":295},">\n",[290,329,331],{"class":163,"line":330},2,[290,332,334],{"emptyLinePlaceholder":333},true,"\n",[290,336,338,340,343,346,348,350,353,355,357,360],{"class":163,"line":337},3,[290,339,296],{"class":295},[290,341,342],{"class":299},"div",[290,344,345],{"class":303}," id",[290,347,307],{"class":295},[290,349,311],{"class":310},[290,351,352],{"class":303}," popover",[290,354,314],{"class":303},[290,356,307],{"class":295},[290,358,359],{"class":310},"\"pop\"",[290,361,327],{"class":295},[290,363,365,368,370,372,374,377,380,382],{"class":163,"line":364},4,[290,366,367],{"class":295},"  \u003C",[290,369,14],{"class":299},[290,371,314],{"class":303},[290,373,307],{"class":295},[290,375,376],{"class":310},"\"pop__title\"",[290,378,379],{"class":295},">Account\u003C\u002F",[290,381,14],{"class":299},[290,383,327],{"class":295},[290,385,387,389,391,394,396,399,402,404],{"class":163,"line":386},5,[290,388,367],{"class":295},[290,390,300],{"class":299},[290,392,393],{"class":303}," type",[290,395,307],{"class":295},[290,397,398],{"class":310},"\"button\"",[290,400,401],{"class":295},">Profile\u003C\u002F",[290,403,300],{"class":299},[290,405,327],{"class":295},[290,407,409,411,413,415,417,419,422,424],{"class":163,"line":408},6,[290,410,367],{"class":295},[290,412,300],{"class":299},[290,414,393],{"class":303},[290,416,307],{"class":295},[290,418,398],{"class":310},[290,420,421],{"class":295},">Sign out\u003C\u002F",[290,423,300],{"class":299},[290,425,327],{"class":295},[290,427,429,432,434],{"class":163,"line":428},7,[290,430,431],{"class":295},"\u003C\u002F",[290,433,342],{"class":299},[290,435,327],{"class":295},[281,437,441],{"className":438,"code":439,"language":440,"meta":286,"style":286},"language-css shiki shiki-themes github-light github-dark",".pop {\n  \u002F* Visible (open) state — what we transition TO on enter, FROM on exit *\u002F\n  opacity: 1;\n  transform: translateY(0) scale(1);\n\n  \u002F* allow-discrete lets display (and the top-layer 'overlay') animate.\n     Listing them in transition-property keeps the element rendered\n     until the opacity\u002Ftransform animation finishes. *\u002F\n  transition:\n    opacity 220ms ease,\n    transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1),\n    overlay 220ms ease allow-discrete,\n    display 220ms ease allow-discrete;\n}\n\n\u002F* Closed state: a popover that is not open computes to display:none *\u002F\n.pop:not(:popover-open) {\n  opacity: 0;\n  transform: translateY(-8px) scale(0.96);\n}\n\n\u002F* ENTER: the values the popover animates FROM the first frame it shows.\n   Nested inside the selector so it only applies to .pop. *\u002F\n@starting-style {\n  .pop:popover-open {\n    opacity: 0;\n    transform: translateY(-8px) scale(0.96);\n  }\n}\n\n.trigger { padding: 0.5rem 1rem; }\n.pop { padding: 0.75rem; border-radius: 10px; border: 1px solid currentColor; }\n\n\u002F* Respect motion preferences: skip the animation, keep the toggle *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .pop { transition: none; }\n}\n","css",[18,442,443,451,457,472,501,505,510,515,521,530,549,584,600,615,621,626,632,647,658,686,691,696,702,708,715,728,740,766,772,777,782,810,858,863,869,878,894],{"__ignoreMap":286},[290,444,445,448],{"class":163,"line":292},[290,446,447],{"class":303},".pop",[290,449,450],{"class":295}," {\n",[290,452,453],{"class":163,"line":330},[290,454,456],{"class":455},"sJ8bj","  \u002F* Visible (open) state — what we transition TO on enter, FROM on exit *\u002F\n",[290,458,459,463,466,469],{"class":163,"line":337},[290,460,462],{"class":461},"sj4cs","  opacity",[290,464,465],{"class":295},": ",[290,467,468],{"class":461},"1",[290,470,471],{"class":295},";\n",[290,473,474,477,479,482,485,488,491,494,496,498],{"class":163,"line":364},[290,475,476],{"class":461},"  transform",[290,478,465],{"class":295},[290,480,481],{"class":461},"translateY",[290,483,484],{"class":295},"(",[290,486,487],{"class":461},"0",[290,489,490],{"class":295},") ",[290,492,493],{"class":461},"scale",[290,495,484],{"class":295},[290,497,468],{"class":461},[290,499,500],{"class":295},");\n",[290,502,503],{"class":163,"line":386},[290,504,334],{"emptyLinePlaceholder":333},[290,506,507],{"class":163,"line":408},[290,508,509],{"class":455},"  \u002F* allow-discrete lets display (and the top-layer 'overlay') animate.\n",[290,511,512],{"class":163,"line":428},[290,513,514],{"class":455},"     Listing them in transition-property keeps the element rendered\n",[290,516,518],{"class":163,"line":517},8,[290,519,520],{"class":455},"     until the opacity\u002Ftransform animation finishes. *\u002F\n",[290,522,524,527],{"class":163,"line":523},9,[290,525,526],{"class":461},"  transition",[290,528,529],{"class":295},":\n",[290,531,533,536,539,543,546],{"class":163,"line":532},10,[290,534,535],{"class":295},"    opacity ",[290,537,538],{"class":461},"220",[290,540,542],{"class":541},"szBVR","ms",[290,544,545],{"class":461}," ease",[290,547,548],{"class":295},",\n",[290,550,552,555,557,559,562,564,567,570,573,575,577,579,581],{"class":163,"line":551},11,[290,553,554],{"class":295},"    transform ",[290,556,538],{"class":461},[290,558,542],{"class":541},[290,560,561],{"class":461}," cubic-bezier",[290,563,484],{"class":295},[290,565,566],{"class":461},"0.2",[290,568,569],{"class":295},", ",[290,571,572],{"class":461},"0.8",[290,574,569],{"class":295},[290,576,566],{"class":461},[290,578,569],{"class":295},[290,580,468],{"class":461},[290,582,583],{"class":295},"),\n",[290,585,587,590,593,595,597],{"class":163,"line":586},12,[290,588,589],{"class":461},"    overlay",[290,591,592],{"class":461}," 220",[290,594,542],{"class":541},[290,596,545],{"class":461},[290,598,599],{"class":295}," allow-discrete,\n",[290,601,603,606,608,610,612],{"class":163,"line":602},13,[290,604,605],{"class":295},"    display ",[290,607,538],{"class":461},[290,609,542],{"class":541},[290,611,545],{"class":461},[290,613,614],{"class":295}," allow-discrete;\n",[290,616,618],{"class":163,"line":617},14,[290,619,620],{"class":295},"}\n",[290,622,624],{"class":163,"line":623},15,[290,625,334],{"emptyLinePlaceholder":333},[290,627,629],{"class":163,"line":628},16,[290,630,631],{"class":455},"\u002F* Closed state: a popover that is not open computes to display:none *\u002F\n",[290,633,635,638,641,644],{"class":163,"line":634},17,[290,636,637],{"class":303},".pop:not",[290,639,640],{"class":295},"(:",[290,642,643],{"class":299},"popover-open",[290,645,646],{"class":295},") {\n",[290,648,650,652,654,656],{"class":163,"line":649},18,[290,651,462],{"class":461},[290,653,465],{"class":295},[290,655,487],{"class":461},[290,657,471],{"class":295},[290,659,661,663,665,667,669,672,675,677,679,681,684],{"class":163,"line":660},19,[290,662,476],{"class":461},[290,664,465],{"class":295},[290,666,481],{"class":461},[290,668,484],{"class":295},[290,670,671],{"class":461},"-8",[290,673,674],{"class":541},"px",[290,676,490],{"class":295},[290,678,493],{"class":461},[290,680,484],{"class":295},[290,682,683],{"class":461},"0.96",[290,685,500],{"class":295},[290,687,689],{"class":163,"line":688},20,[290,690,620],{"class":295},[290,692,694],{"class":163,"line":693},21,[290,695,334],{"emptyLinePlaceholder":333},[290,697,699],{"class":163,"line":698},22,[290,700,701],{"class":455},"\u002F* ENTER: the values the popover animates FROM the first frame it shows.\n",[290,703,705],{"class":163,"line":704},23,[290,706,707],{"class":455},"   Nested inside the selector so it only applies to .pop. *\u002F\n",[290,709,711,713],{"class":163,"line":710},24,[290,712,38],{"class":541},[290,714,450],{"class":295},[290,716,718,721,724,726],{"class":163,"line":717},25,[290,719,720],{"class":303},"  .pop",[290,722,723],{"class":295},":",[290,725,643],{"class":299},[290,727,450],{"class":295},[290,729,731,734,736,738],{"class":163,"line":730},26,[290,732,733],{"class":461},"    opacity",[290,735,465],{"class":295},[290,737,487],{"class":461},[290,739,471],{"class":295},[290,741,743,746,748,750,752,754,756,758,760,762,764],{"class":163,"line":742},27,[290,744,745],{"class":461},"    transform",[290,747,465],{"class":295},[290,749,481],{"class":461},[290,751,484],{"class":295},[290,753,671],{"class":461},[290,755,674],{"class":541},[290,757,490],{"class":295},[290,759,493],{"class":461},[290,761,484],{"class":295},[290,763,683],{"class":461},[290,765,500],{"class":295},[290,767,769],{"class":163,"line":768},28,[290,770,771],{"class":295},"  }\n",[290,773,775],{"class":163,"line":774},29,[290,776,620],{"class":295},[290,778,780],{"class":163,"line":779},30,[290,781,334],{"emptyLinePlaceholder":333},[290,783,785,788,791,794,796,799,802,805,807],{"class":163,"line":784},31,[290,786,787],{"class":303},".trigger",[290,789,790],{"class":295}," { ",[290,792,793],{"class":461},"padding",[290,795,465],{"class":295},[290,797,798],{"class":461},"0.5",[290,800,801],{"class":541},"rem",[290,803,804],{"class":461}," 1",[290,806,801],{"class":541},[290,808,809],{"class":295},"; }\n",[290,811,813,815,817,819,821,824,826,829,832,834,837,839,841,844,846,848,850,853,856],{"class":163,"line":812},32,[290,814,447],{"class":303},[290,816,790],{"class":295},[290,818,793],{"class":461},[290,820,465],{"class":295},[290,822,823],{"class":461},"0.75",[290,825,801],{"class":541},[290,827,828],{"class":295},"; ",[290,830,831],{"class":461},"border-radius",[290,833,465],{"class":295},[290,835,836],{"class":461},"10",[290,838,674],{"class":541},[290,840,828],{"class":295},[290,842,843],{"class":461},"border",[290,845,465],{"class":295},[290,847,468],{"class":461},[290,849,674],{"class":541},[290,851,852],{"class":461}," solid",[290,854,855],{"class":461}," currentColor",[290,857,809],{"class":295},[290,859,861],{"class":163,"line":860},33,[290,862,334],{"emptyLinePlaceholder":333},[290,864,866],{"class":163,"line":865},34,[290,867,868],{"class":455},"\u002F* Respect motion preferences: skip the animation, keep the toggle *\u002F\n",[290,870,872,875],{"class":163,"line":871},35,[290,873,874],{"class":541},"@media",[290,876,877],{"class":295}," (prefers-reduced-motion: reduce) {\n",[290,879,881,883,885,888,890,892],{"class":163,"line":880},36,[290,882,720],{"class":303},[290,884,790],{"class":295},[290,886,887],{"class":461},"transition",[290,889,465],{"class":295},[290,891,72],{"class":461},[290,893,809],{"class":295},[290,895,897],{"class":163,"line":896},37,[290,898,620],{"class":295},[14,900,901,902,904,905,908,909,69,911,913,914,917,918,920,921,924],{},"When the button opens the popover, the engine reads the ",[18,903,38],{}," block, paints the popover at ",[18,906,907],{},"opacity: 0"," and slightly raised, then transitions it to its visible state. When it closes, ",[18,910,76],{},[18,912,103],{}," animate back out while ",[18,915,916],{},"allow-discrete"," keeps ",[18,919,59],{}," (and the top-layer ",[18,922,923],{},"overlay"," property) applied until the final frame — so the exit is fully visible before the element leaves the document.",[47,926],{},[50,928,930],{"id":929},"the-key-technique-deferring-the-discrete-switch","The key technique: deferring the discrete switch",[14,932,933,934,936,937,940,941,943,944,946,947,949,950,953,954,957,958,960,961,963,964,966],{},"The mechanism that does the heavy lifting is the ",[18,935,916],{}," keyword on ",[18,938,939],{},"transition-behavior"," (here written inline in the ",[18,942,887],{}," shorthand). Discrete properties such as ",[18,945,59],{}," normally toggle at time zero. ",[18,948,916],{}," changes the rule to: keep the ",[86,951,952],{},"before"," value applied for the whole duration when transitioning toward a hidden state, and flip to the ",[86,955,956],{},"after"," value only on the last frame. That single change is what keeps the element in the box-tree long enough for ",[18,959,76],{}," to reach zero before ",[18,962,20],{}," takes effect. Pairing it with ",[18,965,38],{}," — which manufactures the missing \"from\" state on first render — closes the loop and gives you symmetric enter and exit motion.",[47,968],{},[50,970,972],{"id":971},"variation-a-native-dialog-with-a-backdrop","Variation: a native dialog with a backdrop",[14,974,975,976,978,979,982,983,69,985,987,988,42],{},"The same two features animate a ",[18,977,279],{}," and its ",[18,980,981],{},"::backdrop",", which live in the top layer. Because the dialog's ",[18,984,59],{},[18,986,923],{}," are discrete, both need ",[18,989,916],{},[281,991,993],{"className":438,"code":992,"language":440,"meta":286,"style":286},"dialog {\n  opacity: 1;\n  transform: scale(1);\n  transition:\n    opacity 200ms ease,\n    transform 200ms ease,\n    overlay 200ms ease allow-discrete,\n    display 200ms ease allow-discrete;\n}\ndialog:not([open]) { opacity: 0; transform: scale(0.95); }\n\n@starting-style {\n  dialog[open] { opacity: 0; transform: scale(0.95); }\n}\n\ndialog::backdrop {\n  background: rgb(0 0 0 \u002F 0.4);\n  transition: background 200ms ease, overlay 200ms ease allow-discrete, display 200ms ease allow-discrete;\n}\n@starting-style {\n  dialog[open]::backdrop { background: rgb(0 0 0 \u002F 0); }\n}\n",[18,994,995,1002,1012,1026,1032,1044,1056,1069,1081,1085,1123,1127,1133,1166,1170,1174,1182,1208,1245,1249,1255,1290],{"__ignoreMap":286},[290,996,997,1000],{"class":163,"line":292},[290,998,999],{"class":299},"dialog",[290,1001,450],{"class":295},[290,1003,1004,1006,1008,1010],{"class":163,"line":330},[290,1005,462],{"class":461},[290,1007,465],{"class":295},[290,1009,468],{"class":461},[290,1011,471],{"class":295},[290,1013,1014,1016,1018,1020,1022,1024],{"class":163,"line":337},[290,1015,476],{"class":461},[290,1017,465],{"class":295},[290,1019,493],{"class":461},[290,1021,484],{"class":295},[290,1023,468],{"class":461},[290,1025,500],{"class":295},[290,1027,1028,1030],{"class":163,"line":364},[290,1029,526],{"class":461},[290,1031,529],{"class":295},[290,1033,1034,1036,1038,1040,1042],{"class":163,"line":386},[290,1035,535],{"class":295},[290,1037,174],{"class":461},[290,1039,542],{"class":541},[290,1041,545],{"class":461},[290,1043,548],{"class":295},[290,1045,1046,1048,1050,1052,1054],{"class":163,"line":408},[290,1047,554],{"class":295},[290,1049,174],{"class":461},[290,1051,542],{"class":541},[290,1053,545],{"class":461},[290,1055,548],{"class":295},[290,1057,1058,1060,1063,1065,1067],{"class":163,"line":428},[290,1059,589],{"class":461},[290,1061,1062],{"class":461}," 200",[290,1064,542],{"class":541},[290,1066,545],{"class":461},[290,1068,599],{"class":295},[290,1070,1071,1073,1075,1077,1079],{"class":163,"line":517},[290,1072,605],{"class":295},[290,1074,174],{"class":461},[290,1076,542],{"class":541},[290,1078,545],{"class":461},[290,1080,614],{"class":295},[290,1082,1083],{"class":163,"line":523},[290,1084,620],{"class":295},[290,1086,1087,1089,1092,1095,1098,1101,1103,1105,1107,1109,1111,1113,1115,1117,1120],{"class":163,"line":532},[290,1088,999],{"class":299},[290,1090,1091],{"class":303},":not",[290,1093,1094],{"class":295},"([",[290,1096,1097],{"class":303},"open",[290,1099,1100],{"class":295},"]) { ",[290,1102,76],{"class":461},[290,1104,465],{"class":295},[290,1106,487],{"class":461},[290,1108,828],{"class":295},[290,1110,103],{"class":461},[290,1112,465],{"class":295},[290,1114,493],{"class":461},[290,1116,484],{"class":295},[290,1118,1119],{"class":461},"0.95",[290,1121,1122],{"class":295},"); }\n",[290,1124,1125],{"class":163,"line":551},[290,1126,334],{"emptyLinePlaceholder":333},[290,1128,1129,1131],{"class":163,"line":586},[290,1130,38],{"class":541},[290,1132,450],{"class":295},[290,1134,1135,1138,1141,1143,1146,1148,1150,1152,1154,1156,1158,1160,1162,1164],{"class":163,"line":602},[290,1136,1137],{"class":299},"  dialog",[290,1139,1140],{"class":295},"[",[290,1142,1097],{"class":303},[290,1144,1145],{"class":295},"] { ",[290,1147,76],{"class":461},[290,1149,465],{"class":295},[290,1151,487],{"class":461},[290,1153,828],{"class":295},[290,1155,103],{"class":461},[290,1157,465],{"class":295},[290,1159,493],{"class":461},[290,1161,484],{"class":295},[290,1163,1119],{"class":461},[290,1165,1122],{"class":295},[290,1167,1168],{"class":163,"line":617},[290,1169,620],{"class":295},[290,1171,1172],{"class":163,"line":623},[290,1173,334],{"emptyLinePlaceholder":333},[290,1175,1176,1178,1180],{"class":163,"line":628},[290,1177,999],{"class":299},[290,1179,981],{"class":303},[290,1181,450],{"class":295},[290,1183,1184,1187,1189,1192,1194,1196,1199,1201,1204,1206],{"class":163,"line":634},[290,1185,1186],{"class":461},"  background",[290,1188,465],{"class":295},[290,1190,1191],{"class":461},"rgb",[290,1193,484],{"class":295},[290,1195,487],{"class":461},[290,1197,1198],{"class":461}," 0",[290,1200,1198],{"class":461},[290,1202,1203],{"class":295}," \u002F ",[290,1205,169],{"class":461},[290,1207,500],{"class":295},[290,1209,1210,1212,1214,1218,1220,1222,1224,1226,1228,1230,1232,1234,1237,1239,1241,1243],{"class":163,"line":649},[290,1211,526],{"class":461},[290,1213,465],{"class":295},[290,1215,1217],{"class":1216},"s7hpK","background",[290,1219,1062],{"class":461},[290,1221,542],{"class":541},[290,1223,545],{"class":461},[290,1225,569],{"class":295},[290,1227,923],{"class":461},[290,1229,1062],{"class":461},[290,1231,542],{"class":541},[290,1233,545],{"class":461},[290,1235,1236],{"class":295}," allow-discrete, display ",[290,1238,174],{"class":461},[290,1240,542],{"class":541},[290,1242,545],{"class":461},[290,1244,614],{"class":295},[290,1246,1247],{"class":163,"line":660},[290,1248,620],{"class":295},[290,1250,1251,1253],{"class":163,"line":688},[290,1252,38],{"class":541},[290,1254,450],{"class":295},[290,1256,1257,1259,1261,1263,1266,1268,1270,1272,1274,1276,1278,1280,1282,1284,1286,1288],{"class":163,"line":693},[290,1258,1137],{"class":299},[290,1260,1140],{"class":295},[290,1262,1097],{"class":303},[290,1264,1265],{"class":295},"]",[290,1267,981],{"class":303},[290,1269,790],{"class":295},[290,1271,1217],{"class":461},[290,1273,465],{"class":295},[290,1275,1191],{"class":461},[290,1277,484],{"class":295},[290,1279,487],{"class":461},[290,1281,1198],{"class":461},[290,1283,1198],{"class":461},[290,1285,1203],{"class":295},[290,1287,487],{"class":461},[290,1289,1122],{"class":295},[290,1291,1292],{"class":163,"line":698},[290,1293,620],{"class":295},[47,1295],{},[50,1297,1299],{"id":1298},"browser-support-note","Browser support note",[14,1301,1302,69,1304,1306,1307,1310],{},[18,1303,34],{},[18,1305,38],{}," shipped together in Chrome and Edge 117+, Safari 17.4+, and Firefox 129+, so as of mid-2026 they are available across all evergreen browsers. The degradation is naturally safe: any engine that does not recognise them ignores the discrete part of the transition, so the element appears and disappears instantly while remaining fully functional. If you want to be explicit you can guard enhancement behind ",[18,1308,1309],{},"@supports (transition-behavior: allow-discrete) { ... }",", but it is rarely necessary because the unsupported path is already acceptable.",[47,1312],{},[50,1314,1316],{"id":1315},"faq","FAQ",[14,1318,1319,1322,1323,1325,1326,1328,1329,1331,1332,1334,1335,1337,1338,1340,1341,1343],{},[62,1320,1321],{},"Why does my element disappear instantly instead of fading out?","\nBecause ",[18,1324,59],{}," is a discrete property that flips at the start of the transition by default, removing the element before ",[18,1327,76],{}," can animate. Add ",[18,1330,916],{}," to the ",[18,1333,59],{}," (and ",[18,1336,923],{},") entry in your ",[18,1339,887],{}," so the browser holds the visible display value until the animation finishes, then switches to ",[18,1342,72],{}," on the last frame.",[14,1345,1346,1349,1351,1352,1354,1355,1357],{},[62,1347,1348],{},"What is @starting-style used for?",[18,1350,38],{}," defines the computed values an element transitions ",[86,1353,88],{}," the first time it renders or when it changes from ",[18,1356,20],{}," to a visible state. Without it there is no \"before\" state to interpolate from, so the entry transition is skipped and the element simply pops in.",[14,1359,1360,1363],{},[62,1361,1362],{},"Which browsers support transition-behavior and @starting-style?","\nBoth shipped together in Chrome and Edge 117+, Safari 17.4+, and Firefox 129+. In older browsers the element appears and disappears instantly, which is a safe functional fallback rather than a broken state.",[14,1365,1366,1369,1370,69,1372,1374,1375,69,1377,1379,1380,1382,1383,1385],{},[62,1367,1368],{},"Do I still need keyframes or JavaScript for modal enter and exit animations?","\nNo. With ",[18,1371,916],{},[18,1373,38],{}," you can transition ",[18,1376,76],{},[18,1378,103],{}," on both entry and exit entirely in CSS, including the discrete ",[18,1381,59],{}," and the top-layer ",[18,1384,923],{}," properties used by dialogs and popovers.",[47,1387],{},[50,1389,1391],{"id":1390},"related","Related",[1393,1394,1395,1401,1408,1415,1422],"ul",{},[1396,1397,1398,1400],"li",{},[27,1399,30],{"href":29}," — the parent guide on transition syntax and compositing.",[1396,1402,1403,1407],{},[27,1404,1406],{"href":1405},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fcss-transition-timing-functions\u002F","CSS Transition Timing Functions"," — choose the easing curves for these enter and exit transitions.",[1396,1409,1410,1414],{},[27,1411,1413],{"href":1412},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Freducing-motion-preferences-in-css\u002F","Reducing motion preferences in CSS"," — disable the discrete transitions for users who request reduced motion.",[1396,1416,1417,1421],{},[27,1418,1420],{"href":1419},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002F","Keyframe Animation Patterns"," — when a multi-step sequence is a better fit than a state transition.",[1396,1423,1424,1428],{},[27,1425,1427],{"href":1426},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fbuilding-responsive-cards-with-container-queries\u002F","Building responsive cards with container queries"," — pair animated popovers with layout-aware components.",[1430,1431,1432],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":286,"searchDepth":330,"depth":330,"links":1434},[1435,1436,1437,1438,1439,1440,1441,1442],{"id":52,"depth":330,"text":53},{"id":118,"depth":330,"text":119},{"id":260,"depth":330,"text":261},{"id":929,"depth":330,"text":930},{"id":971,"depth":330,"text":972},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Animate to and from display:none using transition-behavior: allow-discrete and @starting-style. Build JS-free enter and exit transitions for popovers and modals.","md",{"seoTitle":1446,"datePublished":1447,"dateModified":1447,"faq":1448},"Transition display with allow-discrete","2026-06-18",[1449,1451,1453,1455],{"q":1321,"a":1450},"Because display is a discrete property and by default it flips immediately. Add transition-behavior: allow-discrete (or use the all keyword) so the browser keeps display:block applied until the rest of the transition finishes, then switches to none at the very end.",{"q":1348,"a":1452},"@starting-style defines the values an element transitions from the first time it is rendered or when it changes from display:none to a visible state. Without it there is no before state to interpolate from, so the entry transition does not run.",{"q":1362,"a":1454},"Both shipped together in Chrome and Edge 117+, Safari 17.4+, and Firefox 129+. In older browsers the element appears and disappears instantly, which is a safe functional fallback.",{"q":1368,"a":1456},"No. With allow-discrete and @starting-style you can transition opacity and transform on both entry and exit purely in CSS, including the discrete display and the top-layer overlay properties for dialogs and popovers.","\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransitioning-display-with-allow-discrete",{"title":5,"description":1443},"css-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransitioning-display-with-allow-discrete\u002Findex","xDKm-dQQ-ebvtQzrJ2L5YHxpCLUDKhlpywknSI3eUKs",[1462,2568,4074,5033,5814,6569,8809,10134,11280,12402,13603,14943,16370,17430,18742,19597,21672,22481,25310,26326,27804,29440,31898,32950,34074,34880,35682,35772,36826,37834,38757,39952,40891,41950,43179,44284,46222,47413,49843,51222,52725,53692,54823,56675,57738,59183,60306,61385,63375,64100,65813,67309],{"id":1463,"title":1464,"body":1465,"description":2551,"extension":1444,"meta":2552,"navigation":333,"path":2564,"seo":2565,"stem":2566,"__hash__":2567},"content\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fcreating-accessible-focus-indicators\u002Findex.md","Creating Accessible Focus Indicators: A CSS Reference for Developers & Designers",{"type":7,"value":1466,"toc":2539},[1467,1470,1483,1488,1514,1521,1540,1545,1677,1686,1696,1705,1710,1733,1737,1759,1769,1845,1850,2064,2077,2081,2100,2105,2234,2245,2249,2313,2326,2330,2426,2428,2451,2469,2486,2498,2500,2536],[10,1468,1464],{"id":1469},"creating-accessible-focus-indicators-a-css-reference-for-developers-designers",[14,1471,1472,1473,1477,1478,1482],{},"Creating accessible focus indicators requires balancing strict WCAG 2.2 compliance with modern rendering performance. This page is part of the ",[27,1474,1476],{"href":1475},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002F","Accessibility in CSS Animations"," guide and delivers a precise, production-ready implementation strategy for keyboard navigation states, eliminating visual noise while guaranteeing discoverability. By prioritizing native browser heuristics and compositing-friendly properties, these patterns integrate seamlessly into broader ",[27,1479,1481],{"href":1480},"\u002Fcss-only-micro-interactions-animations\u002F","CSS-Only Micro-Interactions & Animations"," workflows while maintaining strict adherence to accessibility standards.",[14,1484,1485],{},[62,1486,1487],{},"Key Implementation Rules:",[1393,1489,1490,1497,1508,1511],{},[1396,1491,1492,1493,1496],{},"Prioritize ",[18,1494,1495],{},":focus-visible"," for keyboard-only states",[1396,1498,1499,1500,1503,1504,1507],{},"Use ",[18,1501,1502],{},"outline"," over ",[18,1505,1506],{},"box-shadow"," for reliability and performance",[1396,1509,1510],{},"Implement CSS custom properties for theme consistency",[1396,1512,1513],{},"Validate against WCAG 2.2 Success Criterion 2.4.11",[50,1515,1517,1518,1520],{"id":1516},"the-focus-visible-foundation","The ",[18,1519,1495],{}," Foundation",[14,1522,1523,1524,1533,1534,1536,1537,1539],{},"Understanding the distinction between ",[27,1525,1527,1529,1530],{"href":1526},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Ffocus-visible-vs-focus-polyfill-alternatives\u002F",[18,1528,1495],{}," vs ",[18,1531,1532],{},":focus"," is critical for modern accessible UI states. Modern browsers apply native heuristics to detect input modality: ",[18,1535,1495],{}," triggers exclusively during keyboard navigation, while ",[18,1538,1532],{}," fires on any activation method (mouse, touch, script). This progressive enhancement strategy prevents intrusive rings on pointer interactions while preserving essential keyboard discoverability.",[14,1541,1542],{},[62,1543,1544],{},"Implementation Pattern:",[281,1546,1548],{"className":438,"code":1547,"language":440,"meta":286,"style":286},":root {\n  --focus-ring-color: #005fcc;\n  --focus-ring-width: 3px;\n  --focus-ring-offset: 2px;\n}\n\nbutton:focus-visible {\n  outline: var(--focus-ring-width) solid var(--focus-ring-color);\n  outline-offset: var(--focus-ring-offset);\n  border-radius: 0.25rem;\n}\n",[18,1549,1550,1557,1570,1584,1597,1601,1605,1613,1643,1659,1673],{"__ignoreMap":286},[290,1551,1552,1555],{"class":163,"line":292},[290,1553,1554],{"class":303},":root",[290,1556,450],{"class":295},[290,1558,1559,1563,1565,1568],{"class":163,"line":330},[290,1560,1562],{"class":1561},"s4XuR","  --focus-ring-color",[290,1564,465],{"class":295},[290,1566,1567],{"class":461},"#005fcc",[290,1569,471],{"class":295},[290,1571,1572,1575,1577,1580,1582],{"class":163,"line":337},[290,1573,1574],{"class":1561},"  --focus-ring-width",[290,1576,465],{"class":295},[290,1578,1579],{"class":461},"3",[290,1581,674],{"class":541},[290,1583,471],{"class":295},[290,1585,1586,1589,1591,1593,1595],{"class":163,"line":364},[290,1587,1588],{"class":1561},"  --focus-ring-offset",[290,1590,465],{"class":295},[290,1592,194],{"class":461},[290,1594,674],{"class":541},[290,1596,471],{"class":295},[290,1598,1599],{"class":163,"line":386},[290,1600,620],{"class":295},[290,1602,1603],{"class":163,"line":408},[290,1604,334],{"emptyLinePlaceholder":333},[290,1606,1607,1609,1611],{"class":163,"line":428},[290,1608,300],{"class":299},[290,1610,1495],{"class":303},[290,1612,450],{"class":295},[290,1614,1615,1618,1620,1623,1625,1628,1630,1633,1636,1638,1641],{"class":163,"line":517},[290,1616,1617],{"class":461},"  outline",[290,1619,465],{"class":295},[290,1621,1622],{"class":461},"var",[290,1624,484],{"class":295},[290,1626,1627],{"class":1561},"--focus-ring-width",[290,1629,490],{"class":295},[290,1631,1632],{"class":461},"solid",[290,1634,1635],{"class":461}," var",[290,1637,484],{"class":295},[290,1639,1640],{"class":1561},"--focus-ring-color",[290,1642,500],{"class":295},[290,1644,1645,1648,1650,1652,1654,1657],{"class":163,"line":523},[290,1646,1647],{"class":461},"  outline-offset",[290,1649,465],{"class":295},[290,1651,1622],{"class":461},[290,1653,484],{"class":295},[290,1655,1656],{"class":1561},"--focus-ring-offset",[290,1658,500],{"class":295},[290,1660,1661,1664,1666,1669,1671],{"class":163,"line":532},[290,1662,1663],{"class":461},"  border-radius",[290,1665,465],{"class":295},[290,1667,1668],{"class":461},"0.25",[290,1670,801],{"class":541},[290,1672,471],{"class":295},[290,1674,1675],{"class":163,"line":551},[290,1676,620],{"class":295},[14,1678,1679,1682,1683,1685],{},[86,1680,1681],{},"Implementation Notes:"," Relies on native ",[18,1684,1495],{}," for keyboard-only targeting. Ensure the ring color meets the 3:1 contrast ratio against adjacent backgrounds per WCAG 2.2 focus appearance guidelines.",[50,1687,1689,1690,1692,1693],{"id":1688},"precision-styling-with-outline-outline-offset","Precision Styling with ",[18,1691,1502],{}," & ",[18,1694,1695],{},"outline-offset",[14,1697,1517,1698,1700,1701,1704],{},[18,1699,1502],{}," property renders outside the element's box model, preventing layout shifts and respecting component boundaries. Unlike decorative alternatives, it does not trigger paint recalculations and natively supports ",[18,1702,1703],{},"CSS outline-offset"," for precise spacing control.",[14,1706,1707],{},[62,1708,1709],{},"Critical Rules for Custom Focus Ring CSS:",[1393,1711,1712,1719,1724],{},[1396,1713,1714,1715,1718],{},"Never apply ",[18,1716,1717],{},"outline: none"," without an explicit, WCAG-compliant replacement.",[1396,1720,1499,1721,1723],{},[18,1722,1695],{}," to prevent visual overlap with existing borders, padding, or inner shadows.",[1396,1725,1726,1727,69,1729,1732],{},"Handle clipping on ",[18,1728,831],{},[18,1730,1731],{},"overflow: hidden"," containers by increasing the offset value or migrating to a pseudo-element approach.",[50,1734,1736],{"id":1735},"performance-gpu-acceleration","Performance & GPU Acceleration",[14,1738,1739,1740,569,1742,1745,1746,1749,1750,69,1752,1754,1755,1758],{},"Focus transitions must render on the compositor thread to maintain 60fps interactions and prevent input latency. Animating ",[18,1741,1506],{},[18,1743,1744],{},"border-width",", or ",[18,1747,1748],{},"width"," forces synchronous layout and paint phases, directly degrading Core Web Vitals (specifically INP). Instead, isolate the focus ring to a pseudo-element and animate ",[18,1751,103],{},[18,1753,76],{}," exclusively. Any motion you add to a focus ring must also respect ",[27,1756,1757],{"href":1412},"reducing motion preferences",", so scale or fade transitions should collapse to instant state changes when the user opts out.",[14,1760,1761,1762,1765,1766,1768],{},"The diagram below maps how a single keyboard ",[18,1763,1764],{},"Tab"," event resolves into a visible, GPU-composited ring through the ",[18,1767,1495],{}," heuristic.",[133,1770,140,1773,140,1776,140,1779,140,1783,140,1790,140,1796,140,1800,140,1803,140,1806,140,1810,140,1814,140,1818,140,1820,140,1825,140,1828,140,1832,140,1837,140,1841],{"viewBox":1771,"role":136,"ariaLabel":1772,"xmlns":138,"style":139},"0 0 720 250","Flow from a keyboard tab event through the focus-visible heuristic to a composited focus ring",[142,1774,1775],{},"Focus-visible resolution flow",[146,1777,1778],{},"A Tab keypress is detected as keyboard modality, matches focus-visible, and paints a composited ring; a pointer click skips the ring.",[150,1780,1782],{"x":152,"y":153,"style":1781},"text-anchor:middle;fill:currentColor;font:700 17px sans-serif","How a focus ring becomes visible",[171,1784],{"x":1785,"y":1786,"width":1787,"height":1788,"rx":176,"fill":177,"opacity":178,"stroke":167,"strokeWidth":1789},"20","60","150","56","1.2",[150,1791,1795],{"x":1792,"y":1793,"style":1794},"95","84","text-anchor:middle;fill:currentColor;font:13px sans-serif","Keyboard Tab",[150,1797,1495],{"x":1792,"y":1798,"style":1799},"103","text-anchor:middle;fill:currentColor;font:12px ui-monospace,monospace;opacity:0.85",[171,1801],{"x":1802,"y":1786,"width":1787,"height":1788,"rx":176,"fill":72,"stroke":167,"strokeWidth":1789},"285",[150,1804,1805],{"x":152,"y":1793,"style":1794},"Modality check",[150,1807,1809],{"x":152,"y":1798,"style":1808},"text-anchor:middle;fill:currentColor;font:12px sans-serif;opacity:0.7","browser heuristic",[171,1811],{"x":1812,"y":1786,"width":1787,"height":1788,"rx":176,"fill":177,"opacity":1813,"stroke":167,"strokeWidth":1789},"550","0.30",[150,1815,1817],{"x":1816,"y":1793,"style":1794},"625","Composited ring",[150,1819,1502],{"x":1816,"y":1798,"style":1799},[163,1821],{"x1":1822,"y1":1823,"x2":1802,"y2":1823,"stroke":167,"strokeWidth":1824},"170","88","1.4",[163,1826],{"x1":1827,"y1":1823,"x2":1812,"y2":1823,"stroke":167,"strokeWidth":1824},"435",[171,1829],{"x":1802,"y":1830,"width":1787,"height":1831,"rx":176,"fill":72,"stroke":167,"strokeWidth":1789,"opacity":232},"160","50",[150,1833,1836],{"x":152,"y":1834,"style":1835},"182","text-anchor:middle;fill:currentColor;font:13px sans-serif;opacity:0.8","Pointer click",[150,1838,1840],{"x":152,"y":174,"style":1839},"text-anchor:middle;fill:currentColor;font:12px sans-serif;opacity:0.6","ring suppressed",[163,1842],{"x1":152,"y1":1843,"x2":152,"y2":1830,"stroke":167,"strokeWidth":1789,"strokeDashArray":1844,"opacity":232},"116",[249,249],[14,1846,1847],{},[62,1848,1849],{},"GPU-Accelerated Pattern:",[281,1851,1853],{"className":438,"code":1852,"language":440,"meta":286,"style":286},".interactive-el {\n  position: relative;\n  transition: transform 0.15s ease-out;\n}\n\n.interactive-el::after {\n  content: \"\";\n  position: absolute;\n  inset: -3px;\n  border: 2px solid transparent;\n  border-radius: inherit;\n  transition:\n    border-color 0.15s ease-out,\n    transform 0.15s ease-out;\n  pointer-events: none;\n}\n\n.interactive-el:focus-visible::after {\n  border-color: var(--focus-ring-color);\n  transform: scale(1.02);\n}\n",[18,1854,1855,1862,1874,1892,1896,1900,1907,1919,1930,1944,1962,1973,1979,1992,2004,2015,2019,2023,2030,2045,2060],{"__ignoreMap":286},[290,1856,1857,1860],{"class":163,"line":292},[290,1858,1859],{"class":303},".interactive-el",[290,1861,450],{"class":295},[290,1863,1864,1867,1869,1872],{"class":163,"line":330},[290,1865,1866],{"class":461},"  position",[290,1868,465],{"class":295},[290,1870,1871],{"class":461},"relative",[290,1873,471],{"class":295},[290,1875,1876,1878,1881,1884,1887,1890],{"class":163,"line":337},[290,1877,526],{"class":461},[290,1879,1880],{"class":295},": transform ",[290,1882,1883],{"class":461},"0.15",[290,1885,1886],{"class":541},"s",[290,1888,1889],{"class":461}," ease-out",[290,1891,471],{"class":295},[290,1893,1894],{"class":163,"line":364},[290,1895,620],{"class":295},[290,1897,1898],{"class":163,"line":386},[290,1899,334],{"emptyLinePlaceholder":333},[290,1901,1902,1905],{"class":163,"line":408},[290,1903,1904],{"class":303},".interactive-el::after",[290,1906,450],{"class":295},[290,1908,1909,1912,1914,1917],{"class":163,"line":428},[290,1910,1911],{"class":461},"  content",[290,1913,465],{"class":295},[290,1915,1916],{"class":310},"\"\"",[290,1918,471],{"class":295},[290,1920,1921,1923,1925,1928],{"class":163,"line":517},[290,1922,1866],{"class":461},[290,1924,465],{"class":295},[290,1926,1927],{"class":461},"absolute",[290,1929,471],{"class":295},[290,1931,1932,1935,1937,1940,1942],{"class":163,"line":523},[290,1933,1934],{"class":461},"  inset",[290,1936,465],{"class":295},[290,1938,1939],{"class":461},"-3",[290,1941,674],{"class":541},[290,1943,471],{"class":295},[290,1945,1946,1949,1951,1953,1955,1957,1960],{"class":163,"line":532},[290,1947,1948],{"class":461},"  border",[290,1950,465],{"class":295},[290,1952,194],{"class":461},[290,1954,674],{"class":541},[290,1956,852],{"class":461},[290,1958,1959],{"class":461}," transparent",[290,1961,471],{"class":295},[290,1963,1964,1966,1968,1971],{"class":163,"line":551},[290,1965,1663],{"class":461},[290,1967,465],{"class":295},[290,1969,1970],{"class":461},"inherit",[290,1972,471],{"class":295},[290,1974,1975,1977],{"class":163,"line":586},[290,1976,526],{"class":461},[290,1978,529],{"class":295},[290,1980,1981,1984,1986,1988,1990],{"class":163,"line":602},[290,1982,1983],{"class":295},"    border-color ",[290,1985,1883],{"class":461},[290,1987,1886],{"class":541},[290,1989,1889],{"class":461},[290,1991,548],{"class":295},[290,1993,1994,1996,1998,2000,2002],{"class":163,"line":617},[290,1995,554],{"class":295},[290,1997,1883],{"class":461},[290,1999,1886],{"class":541},[290,2001,1889],{"class":461},[290,2003,471],{"class":295},[290,2005,2006,2009,2011,2013],{"class":163,"line":623},[290,2007,2008],{"class":461},"  pointer-events",[290,2010,465],{"class":295},[290,2012,72],{"class":461},[290,2014,471],{"class":295},[290,2016,2017],{"class":163,"line":628},[290,2018,620],{"class":295},[290,2020,2021],{"class":163,"line":634},[290,2022,334],{"emptyLinePlaceholder":333},[290,2024,2025,2028],{"class":163,"line":649},[290,2026,2027],{"class":303},".interactive-el:focus-visible::after",[290,2029,450],{"class":295},[290,2031,2032,2035,2037,2039,2041,2043],{"class":163,"line":660},[290,2033,2034],{"class":461},"  border-color",[290,2036,465],{"class":295},[290,2038,1622],{"class":461},[290,2040,484],{"class":295},[290,2042,1640],{"class":1561},[290,2044,500],{"class":295},[290,2046,2047,2049,2051,2053,2055,2058],{"class":163,"line":688},[290,2048,476],{"class":461},[290,2050,465],{"class":295},[290,2052,493],{"class":461},[290,2054,484],{"class":295},[290,2056,2057],{"class":461},"1.02",[290,2059,500],{"class":295},[290,2061,2062],{"class":163,"line":693},[290,2063,620],{"class":295},[14,2065,2066,2068,2069,2072,2073,2076],{},[86,2067,1681],{}," Moves focus styling to a pseudo-element to avoid layout recalculation. ",[18,2070,2071],{},"transform: scale()"," triggers GPU compositing. Ideal for complex micro-interactions. Apply ",[18,2074,2075],{},"will-change: transform"," only during active focus states to prevent unnecessary memory allocation.",[50,2078,2080],{"id":2079},"fallbacks-cross-browser-debugging","Fallbacks & Cross-Browser Debugging",[14,2082,2083,2084,2087,2088,2091,2092,2095,2096,2099],{},"Legacy environments and OS-level accessibility overrides require explicit fallback strategies. Use ",[18,2085,2086],{},"@supports"," to degrade gracefully, and leverage ",[18,2089,2090],{},"forced-colors"," to respect system themes. When debugging rendering conflicts in complex DOM trees, verify stacking contexts with ",[18,2093,2094],{},"isolation: isolate"," or explicit ",[18,2097,2098],{},"z-index"," to prevent focus rings from being obscured by overlapping components.",[14,2101,2102],{},[62,2103,2104],{},"Fallback & High Contrast Implementation:",[281,2106,2108],{"className":438,"code":2107,"language":440,"meta":286,"style":286},"@supports not selector(:focus-visible) {\n  button:focus {\n    outline: var(--focus-ring-width) solid var(--focus-ring-color);\n    outline-offset: var(--focus-ring-offset);\n  }\n}\n\n@media (forced-colors: active) {\n  button:focus-visible {\n    outline: 2px solid CanvasText;\n    outline-offset: 2px;\n  }\n}\n",[18,2109,2110,2123,2132,2157,2172,2176,2180,2184,2191,2199,2214,2226,2230],{"__ignoreMap":286},[290,2111,2112,2114,2117,2120],{"class":163,"line":292},[290,2113,2086],{"class":541},[290,2115,2116],{"class":541}," not",[290,2118,2119],{"class":461}," selector",[290,2121,2122],{"class":295},"(:focus-visible) {\n",[290,2124,2125,2128,2130],{"class":163,"line":330},[290,2126,2127],{"class":299},"  button",[290,2129,1532],{"class":303},[290,2131,450],{"class":295},[290,2133,2134,2137,2139,2141,2143,2145,2147,2149,2151,2153,2155],{"class":163,"line":337},[290,2135,2136],{"class":461},"    outline",[290,2138,465],{"class":295},[290,2140,1622],{"class":461},[290,2142,484],{"class":295},[290,2144,1627],{"class":1561},[290,2146,490],{"class":295},[290,2148,1632],{"class":461},[290,2150,1635],{"class":461},[290,2152,484],{"class":295},[290,2154,1640],{"class":1561},[290,2156,500],{"class":295},[290,2158,2159,2162,2164,2166,2168,2170],{"class":163,"line":364},[290,2160,2161],{"class":461},"    outline-offset",[290,2163,465],{"class":295},[290,2165,1622],{"class":461},[290,2167,484],{"class":295},[290,2169,1656],{"class":1561},[290,2171,500],{"class":295},[290,2173,2174],{"class":163,"line":386},[290,2175,771],{"class":295},[290,2177,2178],{"class":163,"line":408},[290,2179,620],{"class":295},[290,2181,2182],{"class":163,"line":428},[290,2183,334],{"emptyLinePlaceholder":333},[290,2185,2186,2188],{"class":163,"line":517},[290,2187,874],{"class":541},[290,2189,2190],{"class":295}," (forced-colors: active) {\n",[290,2192,2193,2195,2197],{"class":163,"line":523},[290,2194,2127],{"class":299},[290,2196,1495],{"class":303},[290,2198,450],{"class":295},[290,2200,2201,2203,2205,2207,2209,2211],{"class":163,"line":532},[290,2202,2136],{"class":461},[290,2204,465],{"class":295},[290,2206,194],{"class":461},[290,2208,674],{"class":541},[290,2210,852],{"class":461},[290,2212,2213],{"class":295}," CanvasText;\n",[290,2215,2216,2218,2220,2222,2224],{"class":163,"line":551},[290,2217,2161],{"class":461},[290,2219,465],{"class":295},[290,2221,194],{"class":461},[290,2223,674],{"class":541},[290,2225,471],{"class":295},[290,2227,2228],{"class":163,"line":586},[290,2229,771],{"class":295},[290,2231,2232],{"class":163,"line":602},[290,2233,620],{"class":295},[14,2235,2236,2238,2239,2241,2242,2244],{},[86,2237,1681],{}," Provides baseline ",[18,2240,1532],{}," fallback for older Safari\u002FChrome. ",[18,2243,2090],{}," media query ensures visibility in Windows High Contrast mode.",[50,2246,2248],{"id":2247},"browser-support","Browser Support",[2250,2251,2252,2268],"table",{},[2253,2254,2255],"thead",{},[2256,2257,2258,2262,2265],"tr",{},[2259,2260,2261],"th",{},"Browser",[2259,2263,2264],{},"Version",[2259,2266,2267],{},"Notes",[2269,2270,2271,2283,2293,2304],"tbody",{},[2256,2272,2273,2277,2280],{},[2274,2275,2276],"td",{},"Chrome",[2274,2278,2279],{},"86+",[2274,2281,2282],{},"Full native support",[2256,2284,2285,2288,2291],{},[2274,2286,2287],{},"Firefox",[2274,2289,2290],{},"82+",[2274,2292,2282],{},[2256,2294,2295,2298,2301],{},[2274,2296,2297],{},"Safari",[2274,2299,2300],{},"15.4+",[2274,2302,2303],{},"Partial 13.1+ (requires polyfill)",[2256,2305,2306,2309,2311],{},[2274,2307,2308],{},"Edge",[2274,2310,2279],{},[2274,2312,2282],{},[14,2314,2315,2318,2319,2322,2323,2325],{},[86,2316,2317],{},"Polyfill Strategy:"," Use ",[18,2320,2321],{},"focus-visible"," polyfill (v5+) for Safari \u003C15.4 and legacy mobile WebKit. ",[18,2324,2090],{}," is supported in Chromium 89+, Firefox 108+, and Safari 15.4+.",[50,2327,2329],{"id":2328},"common-issues-direct-solutions","Common Issues & Direct Solutions",[2250,2331,2332,2342],{},[2253,2333,2334],{},[2256,2335,2336,2339],{},[2259,2337,2338],{},"Issue",[2259,2340,2341],{},"Solution",[2269,2343,2344,2366,2387,2414],{},[2256,2345,2346,2354],{},[2274,2347,2348,2349,2351,2352],{},"Focus ring clipped by ",[18,2350,1731],{}," or ",[18,2353,831],{},[2274,2355,2356,2357,2359,2360,69,2363,42],{},"Increase ",[18,2358,1695],{}," to push the ring outside the clipping boundary, or switch to a pseudo-element approach with ",[18,2361,2362],{},"transform: scale(1.05)",[18,2364,2365],{},"pointer-events: none",[2256,2367,2368,2371],{},[2274,2369,2370],{},"Focus state triggers on mouse click\u002Ftap",[2274,2372,2373,2374,2376,2377,2379,2380,2382,2383,2386],{},"Replace all global ",[18,2375,1532],{}," rules with ",[18,2378,1495],{},". Reserve ",[18,2381,1532],{}," only for non-interactive elements like ",[18,2384,2385],{},"\u003Csummary>"," or custom widgets requiring persistent state.",[2256,2388,2389,2394],{},[2274,2390,2391,2393],{},[18,2392,1506],{}," animations cause layout thrashing and jank",[2274,2395,2396,2397,2351,2399,2401,2402,2404,2405,69,2407,2409,2410,2413],{},"Avoid animating ",[18,2398,1506],{},[18,2400,1744],{},". Use ",[18,2403,1502],{}," for static rings, or animate ",[18,2406,103],{},[18,2408,76],{}," on a dedicated ",[18,2411,2412],{},"::after"," pseudo-element for GPU compositing.",[2256,2415,2416,2419],{},[2274,2417,2418],{},"Low contrast in dark mode or custom themes",[2274,2420,2421,2422,2425],{},"Use relative luminance calculations or CSS ",[18,2423,2424],{},"color-mix()"," to dynamically adjust ring brightness. Always validate against WCAG 2.4.11's 3:1 contrast requirement against adjacent colors.",[50,2427,1316],{"id":1315},[14,2429,2430,2438,2439,2441,2442,2444,2445,2447,2448,2450],{},[62,2431,2432,2433,2351,2435,2437],{},"Should I use ",[18,2434,1502],{},[18,2436,1506],{}," for accessible focus indicators?","\nAlways prefer ",[18,2440,1502],{}," for primary focus rings. It is natively accessible, doesn't trigger layout recalculations, and respects ",[18,2443,1695],{},". ",[18,2446,1506],{}," should only be used for decorative secondary states and must be paired with ",[18,2449,1502],{}," to maintain accessibility.",[14,2452,2453,2456,2457,569,2459,1745,2461,2463,2464,69,2466,2468],{},[62,2454,2455],{},"Does animating focus rings impact Core Web Vitals or performance?","\nYes, if implemented incorrectly. Animating ",[18,2458,1506],{},[18,2460,1748],{},[18,2462,1744],{}," triggers layout\u002Fpaint phases, increasing CLS and INP. Use ",[18,2465,103],{},[18,2467,76],{}," on pseudo-elements to keep rendering on the compositor thread, ensuring smooth 60fps interactions.",[14,2470,2471,2474,2475,2478,2479,2351,2482,2485],{},[62,2472,2473],{},"How do I handle focus visibility in Windows High Contrast Mode?","\nUse the ",[18,2476,2477],{},"@media (forced-colors: active)"," query to override custom colors with system-defined values like ",[18,2480,2481],{},"CanvasText",[18,2483,2484],{},"Highlight",". Avoid relying solely on color changes; ensure the focus ring maintains a minimum 3px thickness and clear geometric shape.",[14,2487,2488,2491,2492,2494,2495,2497],{},[62,2489,2490],{},"Can I completely remove the default browser focus ring?","\nNever remove it without providing an explicit, WCAG-compliant replacement. If you must reset it for design consistency, apply a custom ",[18,2493,1502],{}," or pseudo-element ring on ",[18,2496,1495],{},". Removing focus indicators entirely violates WCAG 2.2 SC 2.4.7 and 2.4.11.",[50,2499,1391],{"id":1390},[1393,2501,2502,2507,2513,2520,2527],{},[1396,2503,2504,2506],{},[27,2505,1476],{"href":1475}," — the parent guide covering accessible motion and interaction states.",[1396,2508,2509,2512],{},[27,2510,2511],{"href":1412},"Reducing Motion Preferences in CSS"," — collapse any focus-ring motion when users request reduced motion.",[1396,2514,2515,2519],{},[27,2516,2518],{"href":2517},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fvestibular-safe-animation-patterns\u002F","Vestibular-Safe Animation Patterns"," — keep focus transitions safe for motion-sensitive users.",[1396,2521,2522,2526],{},[27,2523,2525],{"href":2524},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fprefers-reduced-motion-recipes\u002F","prefers-reduced-motion Recipes"," — ready-made overrides for animated focus styles.",[1396,2528,2529,2535],{},[27,2530,2531,1529,2533],{"href":1526},[18,2532,1495],{},[18,2534,1532],{}," — choosing the right modality selector for keyboard-only rings.",[1430,2537,2538],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":286,"searchDepth":330,"depth":330,"links":2540},[2541,2543,2545,2546,2547,2548,2549,2550],{"id":1516,"depth":330,"text":2542},"The :focus-visible Foundation",{"id":1688,"depth":330,"text":2544},"Precision Styling with outline & outline-offset",{"id":1735,"depth":330,"text":1736},{"id":2079,"depth":330,"text":2080},{"id":2247,"depth":330,"text":2248},{"id":2328,"depth":330,"text":2329},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Build accessible CSS focus indicators meeting WCAG 2.2: keyboard navigation states, compositing-friendly properties, and cross-browser visual clarity patterns.",{"seoTitle":2553,"datePublished":1447,"dateModified":1447,"faq":2554},"Accessible Focus Indicators in CSS",[2555,2558,2560,2562],{"q":2556,"a":2557},"Should I use outline or box-shadow for accessible focus indicators?","Always prefer outline for primary focus rings. It is natively accessible, doesn't trigger layout recalculations, and respects outline-offset. Use box-shadow only for decorative secondary states, paired with an outline to keep accessibility intact.",{"q":2455,"a":2559},"Yes, if implemented incorrectly. Animating box-shadow, width, or border-width triggers layout and paint, increasing CLS and INP. Animate transform and opacity on a pseudo-element to keep rendering on the compositor thread for smooth 60fps interactions.",{"q":2473,"a":2561},"Use the forced-colors: active media query to override custom colors with system values like CanvasText or Highlight. Do not rely on color alone; keep the ring at least 3px thick with a clear geometric shape.",{"q":2490,"a":2563},"Never remove it without an explicit WCAG-compliant replacement. If you reset it for design consistency, apply a custom outline or pseudo-element ring on focus-visible. Removing focus indicators entirely violates WCAG 2.2 SC 2.4.7 and 2.4.11.","\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fcreating-accessible-focus-indicators",{"title":1464,"description":2551},"css-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fcreating-accessible-focus-indicators\u002Findex","gXfDPRtZqPqp-ea9CTji6eTiV3-0TBk1DKJc9swQt0w",{"id":2569,"title":2570,"body":2571,"description":4059,"extension":1444,"meta":4060,"navigation":333,"path":4070,"seo":4071,"stem":4072,"__hash__":4073},"content\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Findex.md","Accessibility in CSS Animations: Patterns, Specs & Best Practices",{"type":7,"value":2572,"toc":4042},[2573,2576,2586,2648,2652,2658,2663,2733,2744,2748,2756,2761,2782,2888,2892,2902,3011,3016,3052,3056,3068,3072,3083,3327,3335,3339,3352,3356,3359,3674,3679,3725,3729,3836,3841,3862,3866,3973,3975,3984,3993,4004,4006,4008,4039],[10,2574,2570],{"id":2575},"accessibility-in-css-animations-patterns-specs-best-practices",[14,2577,2578,2579,2581,2582,2585],{},"Motion enhances UI feedback, but uncontrolled animations can trigger vestibular disorders and cognitive overload. This guide bridges ",[27,2580,1481],{"href":1480}," with spec-compliant accessibility patterns, ensuring your frontend implementations respect user preferences without sacrificing interactivity. We will cover WCAG 2.2 compliance for motion, practical ",[18,2583,2584],{},"prefers-reduced-motion"," implementation, and strategies for balancing UX feedback with motion safety.",[133,2587,140,2590,140,2593,140,2596,140,2599,140,2603,140,2608,140,2611,140,2616,140,2620,140,2624,140,2628,140,2631,140,2635,140,2639,140,2642,140,2645],{"viewBox":2588,"role":136,"ariaLabel":2589,"xmlns":138,"style":139},"0 0 720 320","Decision flow for serving full motion or reduced motion based on user preference",[142,2591,2592],{},"Motion safety decision flow",[146,2594,2595],{},"A branch on prefers-reduced-motion that routes to full motion or a static, non-spatial fallback.",[150,2597,2592],{"x":152,"y":2598,"style":154},"30",[171,2600],{"x":2601,"y":1788,"width":2602,"height":1788,"rx":836,"fill":177,"opacity":178,"stroke":167,"strokeWidth":168},"270","180",[150,2604,2607],{"x":152,"y":2605,"style":2606},"82","text-anchor:middle;fill:currentColor;font:600 13px sans-serif","prefers-reduced",[150,2609,2610],{"x":152,"y":165,"style":2606},"-motion?",[163,2612],{"x1":2613,"y1":2614,"x2":1830,"y2":2615,"stroke":167,"strokeWidth":168,"opacity":232},"320","112","190",[163,2617],{"x1":2618,"y1":2614,"x2":2619,"y2":2615,"stroke":167,"strokeWidth":168,"opacity":232},"400","560",[150,2621,2623],{"x":2622,"y":1830,"style":1808},"210","no-preference",[150,2625,2627],{"x":2626,"y":1830,"style":1808},"510","reduce",[171,2629],{"x":1786,"y":174,"width":174,"height":2630,"rx":836,"fill":177,"opacity":1813,"stroke":167,"strokeWidth":168},"64",[150,2632,2634],{"x":1830,"y":2633,"style":2606},"228","full transform",[150,2636,2638],{"x":1830,"y":2637,"style":2606},"248","+ opacity motion",[171,2640],{"x":2641,"y":174,"width":174,"height":2630,"rx":836,"fill":177,"opacity":1813,"stroke":167,"strokeWidth":168},"460",[150,2643,2644],{"x":2619,"y":2633,"style":2606},"static cue:",[150,2646,2647],{"x":2619,"y":2637,"style":2606},"color \u002F outline",[50,2649,2651],{"id":2650},"the-vestibular-spectrum-motion-triggers","The Vestibular Spectrum & Motion Triggers",[14,2653,2654,2655,42],{},"Vestibular disorder CSS considerations are no longer optional; they are foundational to modern frontend architecture. Certain animation types—particularly parallax scrolling, rapid zoom, and high-frequency flashing—can induce nausea, dizziness, or seizures in sensitive users. Understanding the physiological impact of motion allows you to classify safe vs. unsafe animation vectors before they reach the rendering pipeline, a process detailed in ",[27,2656,2657],{"href":2517},"vestibular-safe animation patterns",[14,2659,2660],{},[62,2661,2662],{},"WCAG Animation Compliance Thresholds:",[1393,2664,2665,2677,2683,2688,2700,2712],{},[1396,2666,2667,2670,2671,2351,2673,2676],{},[62,2668,2669],{},"WCAG 2.3.1 (Three Flashes or Below Threshold):"," Flashing content must not exceed 3 flashes per second. Avoid rapid ",[18,2672,76],{},[18,2674,2675],{},"background-color"," toggles.",[1396,2678,2679,2682],{},[62,2680,2681],{},"WCAG 2.3.3 (Animation from Interactions):"," Motion triggered by user interaction must be pausable or avoidable, unless it is essential to the functionality.",[1396,2684,2685],{},[62,2686,2687],{},"Property Safety Matrix:",[1396,2689,2690,2691,2694,2695,569,2697,2699],{},"✅ ",[62,2692,2693],{},"GPU-Composited (Safe):"," ",[18,2696,103],{},[18,2698,76],{}," (bypasses layout\u002Fpaint, runs on compositor thread)",[1396,2701,2702,2703,2694,2706,569,2708,2711],{},"️ ",[62,2704,2705],{},"Context-Dependent:",[18,2707,493],{},[18,2709,2710],{},"translate"," (safe at low velocities, dangerous at high displacement)",[1396,2713,2714,2715,2694,2718,569,2720,569,2723,569,2726,569,2729,2732],{},"❌ ",[62,2716,2717],{},"Layout-Thrashing (Unsafe for motion):",[18,2719,1748],{},[18,2721,2722],{},"height",[18,2724,2725],{},"margin",[18,2727,2728],{},"top",[18,2730,2731],{},"left"," (forces synchronous reflow, causes jank and visual disorientation)",[14,2734,2735,2736,2739,2740,2743],{},"Map animation velocity to user tolerance thresholds by capping displacement at ",[18,2737,2738],{},"100px",", maintaining durations above ",[18,2741,2742],{},"200ms",", and avoiding acceleration curves that produce sudden directional changes. Progressive enhancement dictates that motion should be additive, never subtractive from core functionality.",[50,2745,2747],{"id":2746},"respecting-system-level-motion-preferences","Respecting System-Level Motion Preferences",[14,2749,1517,2750,2752,2753,2755],{},[18,2751,2584],{}," media query is the industry standard for respecting OS-level accessibility toggles. Building on the principles outlined in ",[27,2754,1413],{"href":1412},", your implementation should gracefully degrade heavy motion to static states or subtle crossfades rather than stripping all interactivity.",[2757,2758,2760],"h3",{"id":2759},"global-motion-reset-pattern","Global Motion Reset Pattern",[14,2762,2763,2764,69,2767,2770,2771,2774,2775,2778,2779,42],{},"Apply a baseline override that neutralizes non-essential motion while preserving ",[18,2765,2766],{},"animationend",[18,2768,2769],{},"transitionend"," event firing. Using ",[18,2772,2773],{},"0.01ms"," instead of ",[18,2776,2777],{},"0ms"," prevents race conditions in older WebKit\u002FBlink engines. For copy-paste snippets covering common components, see the collection of ",[27,2780,2781],{"href":2524},"prefers-reduced-motion recipes",[281,2783,2785],{"className":438,"code":2784,"language":440,"meta":286,"style":286},"\u002F* Global motion safety override *\u002F\n@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n    scroll-behavior: auto !important;\n  }\n}\n",[18,2786,2787,2792,2798,2805,2814,2822,2839,2852,2867,2880,2884],{"__ignoreMap":286},[290,2788,2789],{"class":163,"line":292},[290,2790,2791],{"class":455},"\u002F* Global motion safety override *\u002F\n",[290,2793,2794,2796],{"class":163,"line":330},[290,2795,874],{"class":541},[290,2797,877],{"class":295},[290,2799,2800,2803],{"class":163,"line":337},[290,2801,2802],{"class":299},"  *",[290,2804,548],{"class":295},[290,2806,2807,2809,2812],{"class":163,"line":364},[290,2808,2802],{"class":299},[290,2810,2811],{"class":303},"::before",[290,2813,548],{"class":295},[290,2815,2816,2818,2820],{"class":163,"line":386},[290,2817,2802],{"class":299},[290,2819,2412],{"class":303},[290,2821,450],{"class":295},[290,2823,2824,2827,2829,2832,2834,2837],{"class":163,"line":408},[290,2825,2826],{"class":461},"    animation-duration",[290,2828,465],{"class":295},[290,2830,2831],{"class":461},"0.01",[290,2833,542],{"class":541},[290,2835,2836],{"class":541}," !important",[290,2838,471],{"class":295},[290,2840,2841,2844,2846,2848,2850],{"class":163,"line":428},[290,2842,2843],{"class":461},"    animation-iteration-count",[290,2845,465],{"class":295},[290,2847,468],{"class":461},[290,2849,2836],{"class":541},[290,2851,471],{"class":295},[290,2853,2854,2857,2859,2861,2863,2865],{"class":163,"line":517},[290,2855,2856],{"class":461},"    transition-duration",[290,2858,465],{"class":295},[290,2860,2831],{"class":461},[290,2862,542],{"class":541},[290,2864,2836],{"class":541},[290,2866,471],{"class":295},[290,2868,2869,2872,2874,2876,2878],{"class":163,"line":523},[290,2870,2871],{"class":461},"    scroll-behavior",[290,2873,465],{"class":295},[290,2875,250],{"class":461},[290,2877,2836],{"class":541},[290,2879,471],{"class":295},[290,2881,2882],{"class":163,"line":532},[290,2883,771],{"class":295},[290,2885,2886],{"class":163,"line":551},[290,2887,620],{"class":295},[2757,2889,2891],{"id":2890},"progressive-enhancement-legacy-fallbacks","Progressive Enhancement & Legacy Fallbacks",[14,2893,2894,2895,2898,2899,723],{},"For environments where the media query isn't supported (legacy Safari \u003C 10.1, older Android WebViews), implement a lightweight JS detection layer that applies a ",[18,2896,2897],{},".motion-reduced"," class to ",[18,2900,2901],{},"\u003Chtml>",[281,2903,2907],{"className":2904,"code":2905,"language":2906,"meta":286,"style":286},"language-js shiki shiki-themes github-light github-dark","if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) {\n  document.documentElement.classList.add(\"motion-reduced\");\n}\nwindow\n  .matchMedia(\"(prefers-reduced-motion: reduce)\")\n  .addEventListener(\"change\", (e) => {\n    document.documentElement.classList.toggle(\"motion-reduced\", e.matches);\n  });\n","js",[18,2908,2909,2928,2943,2947,2952,2966,2991,3006],{"__ignoreMap":286},[290,2910,2911,2914,2917,2920,2922,2925],{"class":163,"line":292},[290,2912,2913],{"class":541},"if",[290,2915,2916],{"class":295}," (window.",[290,2918,2919],{"class":303},"matchMedia",[290,2921,484],{"class":295},[290,2923,2924],{"class":310},"\"(prefers-reduced-motion: reduce)\"",[290,2926,2927],{"class":295},").matches) {\n",[290,2929,2930,2933,2936,2938,2941],{"class":163,"line":330},[290,2931,2932],{"class":295},"  document.documentElement.classList.",[290,2934,2935],{"class":303},"add",[290,2937,484],{"class":295},[290,2939,2940],{"class":310},"\"motion-reduced\"",[290,2942,500],{"class":295},[290,2944,2945],{"class":163,"line":337},[290,2946,620],{"class":295},[290,2948,2949],{"class":163,"line":364},[290,2950,2951],{"class":295},"window\n",[290,2953,2954,2957,2959,2961,2963],{"class":163,"line":386},[290,2955,2956],{"class":295},"  .",[290,2958,2919],{"class":303},[290,2960,484],{"class":295},[290,2962,2924],{"class":310},[290,2964,2965],{"class":295},")\n",[290,2967,2968,2970,2973,2975,2978,2981,2984,2986,2989],{"class":163,"line":408},[290,2969,2956],{"class":295},[290,2971,2972],{"class":303},"addEventListener",[290,2974,484],{"class":295},[290,2976,2977],{"class":310},"\"change\"",[290,2979,2980],{"class":295},", (",[290,2982,2983],{"class":1561},"e",[290,2985,490],{"class":295},[290,2987,2988],{"class":541},"=>",[290,2990,450],{"class":295},[290,2992,2993,2996,2999,3001,3003],{"class":163,"line":428},[290,2994,2995],{"class":295},"    document.documentElement.classList.",[290,2997,2998],{"class":303},"toggle",[290,3000,484],{"class":295},[290,3002,2940],{"class":310},[290,3004,3005],{"class":295},", e.matches);\n",[290,3007,3008],{"class":163,"line":517},[290,3009,3010],{"class":295},"  });\n",[14,3012,3013],{},[62,3014,3015],{},"DevTools Debugging Steps:",[3017,3018,3019,3030,3036,3045],"ol",{},[1396,3020,3021,3022,3025,3026,3029],{},"Open Chrome\u002FFirefox DevTools → ",[62,3023,3024],{},"Rendering"," panel (Chrome) or ",[62,3027,3028],{},"Accessibility"," inspector (Firefox).",[1396,3031,3032,3033,42],{},"Toggle ",[18,3034,3035],{},"Emulate CSS prefers-reduced-motion",[1396,3037,3038,3039,3041,3042,3044],{},"Verify that ",[18,3040,103],{},"\u002F",[18,3043,2710],{}," animations collapse to instant state changes.",[1396,3046,3047,3048,3051],{},"Audit with ",[18,3049,3050],{},"axe DevTools"," or Lighthouse to confirm WCAG 2.3.3 compliance.",[50,3053,3055],{"id":3054},"accessible-state-transitions-timing","Accessible State Transitions & Timing",[14,3057,3058,3059,3061,3062,69,3064,3067],{},"State changes must communicate clearly without overwhelming the user's cognitive processing window. Reference ",[27,3060,30],{"href":29}," for baseline easing curves that prevent motion sickness. The optimal duration for UI feedback sits between ",[18,3063,2742],{},[18,3065,3066],{},"300ms",", aligning with human reaction time and reducing perceived latency.",[2757,3069,3071],{"id":3070},"timing-contrast-preservation","Timing & Contrast Preservation",[14,3073,3074,3075,3078,3079,3082],{},"During animated states, maintain a minimum contrast ratio of ",[18,3076,3077],{},"4.5:1"," (WCAG AA). Avoid spring physics (",[18,3080,3081],{},"cubic-bezier(0.68, -0.55, 0.265, 1.55)",") that overshoot target values, as the oscillation can trigger vestibular discomfort.",[281,3084,3086],{"className":438,"code":3085,"language":440,"meta":286,"style":286},"\u002F* Accessible micro-interaction with controlled timing *\u002F\n.interactive-card {\n  transition:\n    transform 250ms cubic-bezier(0.25, 0.1, 0.25, 1),\n    box-shadow 250ms ease-out,\n    opacity 200ms linear;\n}\n\n.interactive-card:hover {\n  transform: translateY(-4px) scale(1.01);\n  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .interactive-card:hover {\n    transform: none;\n    box-shadow: 0 0 0 2px var(--focus-ring, #005fcc);\n    opacity: 0.95; \u002F* Subtle visual cue without spatial movement *\u002F\n  }\n}\n",[18,3087,3088,3093,3100,3106,3136,3149,3162,3166,3170,3177,3203,3244,3248,3252,3258,3265,3275,3306,3319,3323],{"__ignoreMap":286},[290,3089,3090],{"class":163,"line":292},[290,3091,3092],{"class":455},"\u002F* Accessible micro-interaction with controlled timing *\u002F\n",[290,3094,3095,3098],{"class":163,"line":330},[290,3096,3097],{"class":303},".interactive-card",[290,3099,450],{"class":295},[290,3101,3102,3104],{"class":163,"line":337},[290,3103,526],{"class":461},[290,3105,529],{"class":295},[290,3107,3108,3110,3113,3115,3117,3119,3121,3123,3126,3128,3130,3132,3134],{"class":163,"line":364},[290,3109,554],{"class":295},[290,3111,3112],{"class":461},"250",[290,3114,542],{"class":541},[290,3116,561],{"class":461},[290,3118,484],{"class":295},[290,3120,1668],{"class":461},[290,3122,569],{"class":295},[290,3124,3125],{"class":461},"0.1",[290,3127,569],{"class":295},[290,3129,1668],{"class":461},[290,3131,569],{"class":295},[290,3133,468],{"class":461},[290,3135,583],{"class":295},[290,3137,3138,3141,3143,3145,3147],{"class":163,"line":386},[290,3139,3140],{"class":295},"    box-shadow ",[290,3142,3112],{"class":461},[290,3144,542],{"class":541},[290,3146,1889],{"class":461},[290,3148,548],{"class":295},[290,3150,3151,3153,3155,3157,3160],{"class":163,"line":408},[290,3152,535],{"class":295},[290,3154,174],{"class":461},[290,3156,542],{"class":541},[290,3158,3159],{"class":461}," linear",[290,3161,471],{"class":295},[290,3163,3164],{"class":163,"line":428},[290,3165,620],{"class":295},[290,3167,3168],{"class":163,"line":517},[290,3169,334],{"emptyLinePlaceholder":333},[290,3171,3172,3175],{"class":163,"line":523},[290,3173,3174],{"class":303},".interactive-card:hover",[290,3176,450],{"class":295},[290,3178,3179,3181,3183,3185,3187,3190,3192,3194,3196,3198,3201],{"class":163,"line":532},[290,3180,476],{"class":461},[290,3182,465],{"class":295},[290,3184,481],{"class":461},[290,3186,484],{"class":295},[290,3188,3189],{"class":461},"-4",[290,3191,674],{"class":541},[290,3193,490],{"class":295},[290,3195,493],{"class":461},[290,3197,484],{"class":295},[290,3199,3200],{"class":461},"1.01",[290,3202,500],{"class":295},[290,3204,3205,3208,3210,3212,3215,3217,3220,3222,3225,3227,3229,3231,3233,3235,3237,3239,3242],{"class":163,"line":551},[290,3206,3207],{"class":461},"  box-shadow",[290,3209,465],{"class":295},[290,3211,487],{"class":461},[290,3213,3214],{"class":461}," 8",[290,3216,674],{"class":541},[290,3218,3219],{"class":461}," 24",[290,3221,674],{"class":541},[290,3223,3224],{"class":461}," rgba",[290,3226,484],{"class":295},[290,3228,487],{"class":461},[290,3230,569],{"class":295},[290,3232,487],{"class":461},[290,3234,569],{"class":295},[290,3236,487],{"class":461},[290,3238,569],{"class":295},[290,3240,3241],{"class":461},"0.12",[290,3243,500],{"class":295},[290,3245,3246],{"class":163,"line":586},[290,3247,620],{"class":295},[290,3249,3250],{"class":163,"line":602},[290,3251,334],{"emptyLinePlaceholder":333},[290,3253,3254,3256],{"class":163,"line":617},[290,3255,874],{"class":541},[290,3257,877],{"class":295},[290,3259,3260,3263],{"class":163,"line":623},[290,3261,3262],{"class":303},"  .interactive-card:hover",[290,3264,450],{"class":295},[290,3266,3267,3269,3271,3273],{"class":163,"line":628},[290,3268,745],{"class":461},[290,3270,465],{"class":295},[290,3272,72],{"class":461},[290,3274,471],{"class":295},[290,3276,3277,3280,3282,3284,3286,3288,3291,3293,3295,3297,3300,3302,3304],{"class":163,"line":634},[290,3278,3279],{"class":461},"    box-shadow",[290,3281,465],{"class":295},[290,3283,487],{"class":461},[290,3285,1198],{"class":461},[290,3287,1198],{"class":461},[290,3289,3290],{"class":461}," 2",[290,3292,674],{"class":541},[290,3294,1635],{"class":461},[290,3296,484],{"class":295},[290,3298,3299],{"class":1561},"--focus-ring",[290,3301,569],{"class":295},[290,3303,1567],{"class":461},[290,3305,500],{"class":295},[290,3307,3308,3310,3312,3314,3316],{"class":163,"line":649},[290,3309,733],{"class":461},[290,3311,465],{"class":295},[290,3313,1119],{"class":461},[290,3315,828],{"class":295},[290,3317,3318],{"class":455},"\u002F* Subtle visual cue without spatial movement *\u002F\n",[290,3320,3321],{"class":163,"line":660},[290,3322,771],{"class":295},[290,3324,3325],{"class":163,"line":688},[290,3326,620],{"class":295},[14,3328,3329,3330,69,3332,3334],{},"This pattern ensures that when motion is restricted, the UI still provides a clear affordance via ",[18,3331,1506],{},[18,3333,76],{}," shifts, maintaining accessible micro-interactions without spatial displacement.",[50,3336,3338],{"id":3337},"component-architecture-for-focus-hover-safety","Component Architecture for Focus & Hover Safety",[14,3340,3341,3342,3346,3347,3351],{},"Scalable motion architecture requires decoupling animation logic from component state. This approach aligns with ",[27,3343,3345],{"href":3344},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002F","Hover & Focus State Design"," and integrates seamlessly with ",[27,3348,3350],{"href":3349},"\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fcreating-accessible-focus-indicators\u002F","Creating accessible focus indicators"," to ensure keyboard navigation remains predictable and screen-reader compatible.",[2757,3353,3355],{"id":3354},"css-custom-properties-architecture","CSS Custom Properties Architecture",[14,3357,3358],{},"Use custom properties to toggle motion globally or per-component, enabling runtime overrides without specificity wars.",[281,3360,3362],{"className":438,"code":3361,"language":440,"meta":286,"style":286},":root {\n  --motion-enabled: 1;\n  --transition-base: 250ms cubic-bezier(0.2, 0.8, 0.2, 1);\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :root {\n    --motion-enabled: 0;\n    --transition-base: 0.01ms linear;\n  }\n}\n\n.btn-motion {\n  \u002F* Base state *\u002F\n  transition:\n    transform var(--transition-base),\n    opacity var(--transition-base);\n  will-change: transform, opacity;\n}\n\n.btn-motion:hover,\n.btn-motion:focus-visible {\n  transform: translateY(calc(-2px * var(--motion-enabled)));\n  opacity: calc(0.9 + (0.1 * var(--motion-enabled)));\n}\n\n\u002F* Fallback for focus ring when motion is disabled *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .btn-motion:focus-visible {\n    outline: 2px solid var(--focus-ring);\n    outline-offset: 2px;\n  }\n}\n",[18,3363,3364,3370,3381,3412,3416,3420,3426,3433,3444,3459,3463,3467,3471,3478,3483,3489,3502,3514,3522,3526,3530,3537,3544,3577,3608,3612,3616,3621,3627,3634,3654,3666,3670],{"__ignoreMap":286},[290,3365,3366,3368],{"class":163,"line":292},[290,3367,1554],{"class":303},[290,3369,450],{"class":295},[290,3371,3372,3375,3377,3379],{"class":163,"line":330},[290,3373,3374],{"class":1561},"  --motion-enabled",[290,3376,465],{"class":295},[290,3378,468],{"class":461},[290,3380,471],{"class":295},[290,3382,3383,3386,3388,3390,3392,3394,3396,3398,3400,3402,3404,3406,3408,3410],{"class":163,"line":337},[290,3384,3385],{"class":1561},"  --transition-base",[290,3387,465],{"class":295},[290,3389,3112],{"class":461},[290,3391,542],{"class":541},[290,3393,561],{"class":461},[290,3395,484],{"class":295},[290,3397,566],{"class":461},[290,3399,569],{"class":295},[290,3401,572],{"class":461},[290,3403,569],{"class":295},[290,3405,566],{"class":461},[290,3407,569],{"class":295},[290,3409,468],{"class":461},[290,3411,500],{"class":295},[290,3413,3414],{"class":163,"line":364},[290,3415,620],{"class":295},[290,3417,3418],{"class":163,"line":386},[290,3419,334],{"emptyLinePlaceholder":333},[290,3421,3422,3424],{"class":163,"line":408},[290,3423,874],{"class":541},[290,3425,877],{"class":295},[290,3427,3428,3431],{"class":163,"line":428},[290,3429,3430],{"class":303},"  :root",[290,3432,450],{"class":295},[290,3434,3435,3438,3440,3442],{"class":163,"line":517},[290,3436,3437],{"class":1561},"    --motion-enabled",[290,3439,465],{"class":295},[290,3441,487],{"class":461},[290,3443,471],{"class":295},[290,3445,3446,3449,3451,3453,3455,3457],{"class":163,"line":523},[290,3447,3448],{"class":1561},"    --transition-base",[290,3450,465],{"class":295},[290,3452,2831],{"class":461},[290,3454,542],{"class":541},[290,3456,3159],{"class":461},[290,3458,471],{"class":295},[290,3460,3461],{"class":163,"line":532},[290,3462,771],{"class":295},[290,3464,3465],{"class":163,"line":551},[290,3466,620],{"class":295},[290,3468,3469],{"class":163,"line":586},[290,3470,334],{"emptyLinePlaceholder":333},[290,3472,3473,3476],{"class":163,"line":602},[290,3474,3475],{"class":303},".btn-motion",[290,3477,450],{"class":295},[290,3479,3480],{"class":163,"line":617},[290,3481,3482],{"class":455},"  \u002F* Base state *\u002F\n",[290,3484,3485,3487],{"class":163,"line":623},[290,3486,526],{"class":461},[290,3488,529],{"class":295},[290,3490,3491,3493,3495,3497,3500],{"class":163,"line":628},[290,3492,554],{"class":295},[290,3494,1622],{"class":461},[290,3496,484],{"class":295},[290,3498,3499],{"class":1561},"--transition-base",[290,3501,583],{"class":295},[290,3503,3504,3506,3508,3510,3512],{"class":163,"line":634},[290,3505,535],{"class":295},[290,3507,1622],{"class":461},[290,3509,484],{"class":295},[290,3511,3499],{"class":1561},[290,3513,500],{"class":295},[290,3515,3516,3519],{"class":163,"line":649},[290,3517,3518],{"class":461},"  will-change",[290,3520,3521],{"class":295},": transform, opacity;\n",[290,3523,3524],{"class":163,"line":660},[290,3525,620],{"class":295},[290,3527,3528],{"class":163,"line":688},[290,3529,334],{"emptyLinePlaceholder":333},[290,3531,3532,3535],{"class":163,"line":693},[290,3533,3534],{"class":303},".btn-motion:hover",[290,3536,548],{"class":295},[290,3538,3539,3542],{"class":163,"line":698},[290,3540,3541],{"class":303},".btn-motion:focus-visible",[290,3543,450],{"class":295},[290,3545,3546,3548,3550,3552,3554,3557,3559,3562,3564,3567,3569,3571,3574],{"class":163,"line":704},[290,3547,476],{"class":461},[290,3549,465],{"class":295},[290,3551,481],{"class":461},[290,3553,484],{"class":295},[290,3555,3556],{"class":461},"calc",[290,3558,484],{"class":295},[290,3560,3561],{"class":461},"-2",[290,3563,674],{"class":541},[290,3565,3566],{"class":541}," *",[290,3568,1635],{"class":461},[290,3570,484],{"class":295},[290,3572,3573],{"class":1561},"--motion-enabled",[290,3575,3576],{"class":295},")));\n",[290,3578,3579,3581,3583,3585,3587,3590,3593,3596,3598,3600,3602,3604,3606],{"class":163,"line":710},[290,3580,462],{"class":461},[290,3582,465],{"class":295},[290,3584,3556],{"class":461},[290,3586,484],{"class":295},[290,3588,3589],{"class":461},"0.9",[290,3591,3592],{"class":541}," +",[290,3594,3595],{"class":295}," (",[290,3597,3125],{"class":461},[290,3599,3566],{"class":541},[290,3601,1635],{"class":461},[290,3603,484],{"class":295},[290,3605,3573],{"class":1561},[290,3607,3576],{"class":295},[290,3609,3610],{"class":163,"line":717},[290,3611,620],{"class":295},[290,3613,3614],{"class":163,"line":730},[290,3615,334],{"emptyLinePlaceholder":333},[290,3617,3618],{"class":163,"line":742},[290,3619,3620],{"class":455},"\u002F* Fallback for focus ring when motion is disabled *\u002F\n",[290,3622,3623,3625],{"class":163,"line":768},[290,3624,874],{"class":541},[290,3626,877],{"class":295},[290,3628,3629,3632],{"class":163,"line":774},[290,3630,3631],{"class":303},"  .btn-motion:focus-visible",[290,3633,450],{"class":295},[290,3635,3636,3638,3640,3642,3644,3646,3648,3650,3652],{"class":163,"line":779},[290,3637,2136],{"class":461},[290,3639,465],{"class":295},[290,3641,194],{"class":461},[290,3643,674],{"class":541},[290,3645,852],{"class":461},[290,3647,1635],{"class":461},[290,3649,484],{"class":295},[290,3651,3299],{"class":1561},[290,3653,500],{"class":295},[290,3655,3656,3658,3660,3662,3664],{"class":163,"line":784},[290,3657,2161],{"class":461},[290,3659,465],{"class":295},[290,3661,194],{"class":461},[290,3663,674],{"class":541},[290,3665,471],{"class":295},[290,3667,3668],{"class":163,"line":812},[290,3669,771],{"class":295},[290,3671,3672],{"class":163,"line":860},[290,3673,620],{"class":295},[14,3675,3676],{},[62,3677,3678],{},"Implementation Checklist:",[1393,3680,3681,3695,3703,3710],{},[1396,3682,3683,3684,3686,3687,3690,3691,3694],{},"✅ Decouple ",[18,3685,103],{}," logic from ",[18,3688,3689],{},":hover"," using ",[18,3692,3693],{},"calc()"," and custom property flags.",[1396,3696,3697,3698,2774,3700,3702],{},"✅ Use ",[18,3699,1495],{},[18,3701,1532],{}," to prevent ring bleed on mouse clicks.",[1396,3704,3705,3706,3709],{},"✅ Test with VoiceOver\u002FNVDA to ensure ",[18,3707,3708],{},"aria-live"," regions aren't flooded by rapid DOM updates.",[1396,3711,3712,3713,569,3715,569,3718,569,3721,3724],{},"✅ Validate with keyboard-only navigation (",[18,3714,1764],{},[18,3716,3717],{},"Shift+Tab",[18,3719,3720],{},"Enter",[18,3722,3723],{},"Space",").",[50,3726,3728],{"id":3727},"browser-support-cross-browser-compatibility","Browser Support & Cross-Browser Compatibility",[2250,3730,3731,3748],{},[2253,3732,3733],{},[2256,3734,3735,3738,3740,3742,3744,3746],{},[2259,3736,3737],{},"Feature",[2259,3739,2276],{},[2259,3741,2287],{},[2259,3743,2297],{},[2259,3745,2308],{},[2259,3747,2267],{},[2269,3749,3750,3771,3791,3812],{},[2256,3751,3752,3756,3759,3762,3765,3768],{},[2274,3753,3754],{},[18,3755,2584],{},[2274,3757,3758],{},"74+",[2274,3760,3761],{},"63+",[2274,3763,3764],{},"10.1+",[2274,3766,3767],{},"79+",[2274,3769,3770],{},"Full support across modern evergreen browsers",[2256,3772,3773,3777,3779,3782,3784,3786],{},[2274,3774,3775],{},[18,3776,1495],{},[2274,3778,2279],{},[2274,3780,3781],{},"85+",[2274,3783,2300],{},[2274,3785,2279],{},[2274,3787,1499,3788,3790],{},[18,3789,1532],{}," fallback for Safari \u003C 15.4",[2256,3792,3793,3798,3801,3803,3806,3809],{},[2274,3794,3795],{},[18,3796,3797],{},"will-change",[2274,3799,3800],{},"36+",[2274,3802,3800],{},[2274,3804,3805],{},"9.1+",[2274,3807,3808],{},"12+",[2274,3810,3811],{},"Use sparingly; triggers compositor promotion",[2256,3813,3814,3819,3822,3824,3827,3829],{},[2274,3815,3816],{},[18,3817,3818],{},"@media (prefers-reduced-motion)",[2274,3820,3821],{},"✅",[2274,3823,3821],{},[2274,3825,3826],{},"️",[2274,3828,3821],{},[2274,3830,3831,3832,3835],{},"Safari 10.1–12 requires ",[18,3833,3834],{},"-webkit-"," prefix for older syntax",[14,3837,3838],{},[62,3839,3840],{},"Cross-Browser Notes:",[1393,3842,3843,3853,3859],{},[1396,3844,3845,3846,69,3849,3852],{},"iOS Safari respects system-level motion toggles natively. Ensure ",[18,3847,3848],{},"viewport-fit=cover",[18,3850,3851],{},"scroll-behavior: smooth"," do not override OS preferences.",[1396,3854,3855,3856,42],{},"For older WebKit, prefix media queries: ",[18,3857,3858],{},"@media (-webkit-min-device-pixel-ratio: 0) and (prefers-reduced-motion: reduce)",[1396,3860,3861],{},"Always test on real devices; emulators cannot fully replicate vestibular sensitivity or touch-scroll physics.",[50,3863,3865],{"id":3864},"common-issues-mitigation","Common Issues & Mitigation",[2250,3867,3868,3880],{},[2253,3869,3870],{},[2256,3871,3872,3874,3877],{},[2259,3873,2338],{},[2259,3875,3876],{},"Root Cause",[2259,3878,3879],{},"Fix",[2269,3881,3882,3905,3926,3950],{},[2256,3883,3884,3890,3896],{},[2274,3885,3886,3887,3889],{},"Overriding ",[18,3888,2584],{}," with inline styles",[2274,3891,3892,3893,3895],{},"JS libraries or React ",[18,3894,1430],{}," props bypass CSS cascade",[2274,3897,1499,3898,3901,3902,3904],{},[18,3899,3900],{},"!important"," in media queries or scope JS animation libraries to check ",[18,3903,2919],{}," first",[2256,3906,3907,3915,3918],{},[2274,3908,3909,3910,569,3912,3914],{},"Animating layout properties (",[18,3911,1748],{},[18,3913,2725],{},")",[2274,3916,3917],{},"Forces synchronous reflow, causes jank & disorientation",[2274,3919,3920,3921,2351,3923],{},"Replace with ",[18,3922,2071],{},[18,3924,3925],{},"transform: translateX()",[2256,3927,3928,3934,3937],{},[2274,3929,3930,3931,3933],{},"Missing ",[18,3932,1495],{}," fallbacks during hover transitions",[2274,3935,3936],{},"Keyboard users lose visual state when motion is disabled",[2274,3938,3939,3940,3942,3943,3945,3946,3041,3948],{},"Pair ",[18,3941,3689],{}," with ",[18,3944,1495],{}," and apply static ",[18,3947,1502],{},[18,3949,1506],{},[2256,3951,3952,3955,3963],{},[2274,3953,3954],{},"Flash frequency violating WCAG 2.3.1",[2274,3956,3957,3958,3041,3960,3962],{},"Rapid ",[18,3959,76],{},[18,3961,1217],{}," toggles in loaders or alerts",[2274,3964,3965,3966,3942,3969,3972],{},"Cap flash rate at ≤3Hz, use ",[18,3967,3968],{},"@keyframes",[18,3970,3971],{},"steps()"," to control timing",[50,3974,1316],{"id":1315},[14,3976,3977,3983],{},[62,3978,3979,3980,3982],{},"Should I disable all animations when ",[18,3981,2584],{}," is active?","\nNo. WCAG 2.3.3 recommends disabling non-essential motion, but essential feedback (like button presses or form validation) should remain, ideally using opacity or color transitions instead of spatial movement.",[14,3985,3986,3989,3990,3992],{},[62,3987,3988],{},"How do I test for vestibular accessibility without a physical device?","\nUse browser dev tools to emulate ",[18,3991,2584],{},", audit with axe DevTools, and manually verify that animations under 200ms or non-transform properties don't trigger disorientation. Cross-reference with the Vestibular Disorders Association (VeDA) guidelines for motion thresholds.",[14,3994,3995,3998,3999,69,4001,4003],{},[62,3996,3997],{},"Can CSS Houdini improve animation accessibility?","\nYes. The Paint API allows custom rendering pipelines that can bypass main-thread layout thrashing, but it requires careful fallback strategies since it's not universally supported and doesn't inherently respect OS motion preferences without explicit JS\u002FCSS integration. Always pair Houdini worklets with ",[18,4000,2086],{},[18,4002,2584],{}," guards.",[47,4005],{},[50,4007,1391],{"id":1390},[1393,4009,4010,4016,4022,4027,4032],{},[1396,4011,4012,4015],{},[27,4013,4014],{"href":2517},"Vestibular-safe animation patterns"," — classifying and capping motion that can trigger discomfort.",[1396,4017,4018,4021],{},[27,4019,4020],{"href":2524},"Prefers-reduced-motion recipes"," — ready-made overrides for menus, modals, and loaders.",[1396,4023,4024,4026],{},[27,4025,1413],{"href":1412}," — the media query mechanics behind these patterns.",[1396,4028,4029,4031],{},[27,4030,1481],{"href":1480}," — the parent guide tying motion, transitions, and accessibility together.",[1396,4033,4034,4038],{},[27,4035,4037],{"href":4036},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fhow-to-use-container-queries-in-production\u002F","How to use container queries in production"," — adapting components responsively without motion, from the container-queries guide.",[1430,4040,4041],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":286,"searchDepth":330,"depth":330,"links":4043},[4044,4045,4049,4052,4055,4056,4057,4058],{"id":2650,"depth":330,"text":2651},{"id":2746,"depth":330,"text":2747,"children":4046},[4047,4048],{"id":2759,"depth":337,"text":2760},{"id":2890,"depth":337,"text":2891},{"id":3054,"depth":330,"text":3055,"children":4050},[4051],{"id":3070,"depth":337,"text":3071},{"id":3337,"depth":330,"text":3338,"children":4053},[4054],{"id":3354,"depth":337,"text":3355},{"id":3727,"depth":330,"text":3728},{"id":3864,"depth":330,"text":3865},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"WCAG 2.2-compliant CSS animation accessibility: prefers-reduced-motion implementation, vestibular considerations, and production-ready motion safety strategies.",{"seoTitle":4061,"datePublished":1447,"dateModified":1447,"faq":4062},"Accessibility in CSS Animations Guide",[4063,4066,4068],{"q":4064,"a":4065},"Should I disable all animations when prefers-reduced-motion is active?","No. WCAG 2.3.3 recommends disabling non-essential motion, but essential feedback such as button presses or form validation should remain, ideally using opacity or color transitions instead of spatial movement.",{"q":3988,"a":4067},"Use browser dev tools to emulate prefers-reduced-motion, audit with axe DevTools, and manually verify that animations under 200ms or non-transform properties do not trigger disorientation. Cross-reference Vestibular Disorders Association guidelines for motion thresholds.",{"q":3997,"a":4069},"Yes. The Paint API allows custom rendering pipelines that bypass main-thread layout thrashing, but it requires fallback strategies since it is not universally supported and does not inherently respect OS motion preferences. Pair Houdini worklets with @supports and prefers-reduced-motion guards.","\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations",{"title":2570,"description":4059},"css-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Findex","AbeHCJKr3dVy96Aa-CkzZuGBywjcAtbQexeG4AQD2Rs",{"id":4075,"title":4076,"body":4077,"description":5016,"extension":1444,"meta":5017,"navigation":333,"path":5029,"seo":5030,"stem":5031,"__hash__":5032},"content\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fprefers-reduced-motion-recipes\u002Findex.md","prefers-reduced-motion Recipes: Reduce, Don't Just Remove",{"type":7,"value":4078,"toc":5007},[4079,4082,4095,4100,4117,4121,4128,4131,4200,4204,4207,4381,4789,4792,4796,4806,4810,4813,4895,4902,4905,4925,4927,4936,4942,4963,4973,4975,5004],[10,4080,4076],{"id":4081},"prefers-reduced-motion-recipes-reduce-dont-just-remove",[14,4083,4084,4085,4088,4089,4091,4092,4094],{},"You have shipped a parallax header, an autoplaying carousel, and a dozen hover transitions, and now you need every one of them to behave for users who have asked their operating system for less motion. The narrow problem this guide solves is supplying ready-to-paste blocks for the effects that actually cause trouble — parallax, autoplay, and transitions — built around one idea: ",[62,4086,4087],{},"reduce, do not blindly remove",". This page sits under ",[27,4090,1476],{"href":1475},", and it puts the syntax from ",[27,4093,2511],{"href":1412}," to work on concrete components.",[14,4096,4097],{},[62,4098,4099],{},"Recipes covered:",[1393,4101,4102,4108,4111,4114],{},[1396,4103,4104,4105],{},"A safe global reset that does not break ",[18,4106,4107],{},"fill-mode",[1396,4109,4110],{},"Disabling parallax while keeping content visible",[1396,4112,4113],{},"Pausing autoplay and swapping to a poster",[1396,4115,4116],{},"Shortening transitions instead of deleting feedback",[50,4118,4120],{"id":4119},"the-reduce-not-remove-principle","The reduce-not-remove principle",[14,4122,4123,4124,4127],{},"The crude approach — wrap the whole site in a ",[18,4125,4126],{},"@media (prefers-reduced-motion: reduce)"," block that sets every duration to zero — is a defensible safety net, but it is rarely the best experience. A user who asked for less motion did not ask for an interface that feels broken. They asked you to stop moving things around the screen. A button can still acknowledge a click with an instant colour change; a panel can still cross-fade; a notification can still appear. What should stop is the travel, the parallax, the spin, the autoplay. Reducing rather than removing keeps the interface legible and responsive while honouring the request.",[14,4129,4130],{},"In practice this means treating the media query as a router: the same component takes the full-motion path or the reduced path, and the reduced path is a deliberately designed alternative, not an absence. The diagram shows the media query as that gate, sending one component down two designed routes.",[133,4132,140,4135,140,4138,140,4141,140,4144,140,4148,140,4152,140,4156,140,4159,140,4164,140,4168,140,4170,140,4174,140,4178,140,4182,140,4185,140,4188,140,4192,140,4197],{"viewBox":4133,"role":136,"ariaLabel":4134,"xmlns":138,"style":139},"0 0 720 300","A media query gate routes a component to full motion or reduced motion",[142,4136,4137],{},"Reduced-motion media query as a routing gate",[146,4139,4140],{},"One component passes through a prefers-reduced-motion gate and is routed to either a full-motion path or a reduced path.",[150,4142,4143],{"x":152,"y":153,"style":1781},"One component, two designed paths",[171,4145],{"x":4146,"y":4147,"width":1830,"height":1786,"rx":176,"fill":72,"stroke":167,"strokeWidth":168},"40","120",[150,4149,4151],{"x":4147,"y":4150,"style":1794},"146","component",[150,4153,4155],{"x":4147,"y":4154,"style":1799},"166",".fx",[171,4157],{"x":2601,"y":4158,"width":1822,"height":2630,"rx":176,"fill":177,"opacity":178},"118",[150,4160,4163],{"x":4161,"y":4162,"style":1794},"355","144","@media gate",[150,4165,4167],{"x":4161,"y":4166,"style":1799},"164","reduced-motion?",[163,4169],{"x1":174,"y1":1787,"x2":2601,"y2":1787,"stroke":167,"strokeWidth":168},[171,4171],{"x":2626,"y":1786,"width":2602,"height":4172,"rx":176,"fill":177,"opacity":4173},"58","0.28",[150,4175,4177],{"x":4176,"y":1793,"style":1794},"600","full motion",[150,4179,2623],{"x":4176,"y":4180,"style":4181},"104","text-anchor:middle;fill:currentColor;font:12px sans-serif;opacity:0.8",[171,4183],{"x":2626,"y":4184,"width":2602,"height":4172,"rx":176,"fill":177,"opacity":3241},"186",[150,4186,4187],{"x":4176,"y":2622,"style":1794},"reduced path",[150,4189,4191],{"x":4176,"y":4190,"style":4181},"230","fade \u002F instant, no travel",[163,4193],{"x1":4194,"y1":4195,"x2":2626,"y2":4196,"stroke":167,"strokeWidth":168},"440","140","92",[163,4198],{"x1":4194,"y1":1830,"x2":2626,"y2":4199,"stroke":167,"strokeWidth":168},"212",[50,4201,4203],{"id":4202},"complete-working-implementation","Complete working implementation",[14,4205,4206],{},"A single page that wires up all three recipes plus the global net. Each block is independent — paste the ones you need.",[281,4208,4210],{"className":283,"code":4209,"language":285,"meta":286,"style":286},"\u003Cheader class=\"parallax\">Hero\u003C\u002Fheader>\n\n\u003Cfigure class=\"autoplay\">\n  \u003Cvideo class=\"autoplay__video\" autoplay muted loop poster=\"\u002Fposter.jpg\">\n    \u003Csource src=\"\u002Floop.webm\" type=\"video\u002Fwebm\">\n  \u003C\u002Fvideo>\n  \u003Cimg class=\"autoplay__poster\" src=\"\u002Fposter.jpg\" alt=\"Product on a desk\">\n\u003C\u002Ffigure>\n\n\u003Cbutton class=\"cta\">Buy now\u003C\u002Fbutton>\n",[18,4211,4212,4233,4237,4253,4286,4311,4320,4349,4357,4361],{"__ignoreMap":286},[290,4213,4214,4216,4219,4221,4223,4226,4229,4231],{"class":163,"line":292},[290,4215,296],{"class":295},[290,4217,4218],{"class":299},"header",[290,4220,314],{"class":303},[290,4222,307],{"class":295},[290,4224,4225],{"class":310},"\"parallax\"",[290,4227,4228],{"class":295},">Hero\u003C\u002F",[290,4230,4218],{"class":299},[290,4232,327],{"class":295},[290,4234,4235],{"class":163,"line":330},[290,4236,334],{"emptyLinePlaceholder":333},[290,4238,4239,4241,4244,4246,4248,4251],{"class":163,"line":337},[290,4240,296],{"class":295},[290,4242,4243],{"class":299},"figure",[290,4245,314],{"class":303},[290,4247,307],{"class":295},[290,4249,4250],{"class":310},"\"autoplay\"",[290,4252,327],{"class":295},[290,4254,4255,4257,4260,4262,4264,4267,4270,4273,4276,4279,4281,4284],{"class":163,"line":364},[290,4256,367],{"class":295},[290,4258,4259],{"class":299},"video",[290,4261,314],{"class":303},[290,4263,307],{"class":295},[290,4265,4266],{"class":310},"\"autoplay__video\"",[290,4268,4269],{"class":303}," autoplay",[290,4271,4272],{"class":303}," muted",[290,4274,4275],{"class":303}," loop",[290,4277,4278],{"class":303}," poster",[290,4280,307],{"class":295},[290,4282,4283],{"class":310},"\"\u002Fposter.jpg\"",[290,4285,327],{"class":295},[290,4287,4288,4291,4294,4297,4299,4302,4304,4306,4309],{"class":163,"line":386},[290,4289,4290],{"class":295},"    \u003C",[290,4292,4293],{"class":299},"source",[290,4295,4296],{"class":303}," src",[290,4298,307],{"class":295},[290,4300,4301],{"class":310},"\"\u002Floop.webm\"",[290,4303,393],{"class":303},[290,4305,307],{"class":295},[290,4307,4308],{"class":310},"\"video\u002Fwebm\"",[290,4310,327],{"class":295},[290,4312,4313,4316,4318],{"class":163,"line":408},[290,4314,4315],{"class":295},"  \u003C\u002F",[290,4317,4259],{"class":299},[290,4319,327],{"class":295},[290,4321,4322,4324,4326,4328,4330,4333,4335,4337,4339,4342,4344,4347],{"class":163,"line":428},[290,4323,367],{"class":295},[290,4325,136],{"class":299},[290,4327,314],{"class":303},[290,4329,307],{"class":295},[290,4331,4332],{"class":310},"\"autoplay__poster\"",[290,4334,4296],{"class":303},[290,4336,307],{"class":295},[290,4338,4283],{"class":310},[290,4340,4341],{"class":303}," alt",[290,4343,307],{"class":295},[290,4345,4346],{"class":310},"\"Product on a desk\"",[290,4348,327],{"class":295},[290,4350,4351,4353,4355],{"class":163,"line":517},[290,4352,431],{"class":295},[290,4354,4243],{"class":299},[290,4356,327],{"class":295},[290,4358,4359],{"class":163,"line":523},[290,4360,334],{"emptyLinePlaceholder":333},[290,4362,4363,4365,4367,4369,4371,4374,4377,4379],{"class":163,"line":532},[290,4364,296],{"class":295},[290,4366,300],{"class":299},[290,4368,314],{"class":303},[290,4370,307],{"class":295},[290,4372,4373],{"class":310},"\"cta\"",[290,4375,4376],{"class":295},">Buy now\u003C\u002F",[290,4378,300],{"class":299},[290,4380,327],{"class":295},[281,4382,4384],{"className":438,"code":4383,"language":440,"meta":286,"style":286},"\u002F* ---- Recipe 0: safe global net -------------------------------------\n   Near-instant rather than truly 0s, so animation-fill-mode: forwards\n   still resolves to the final frame without a flicker. *\u002F\n@media (prefers-reduced-motion: reduce) {\n  *, *::before, *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n    scroll-behavior: auto !important;\n  }\n}\n\n\u002F* ---- Recipe 1: parallax -> static --------------------------------- *\u002F\n.parallax {\n  background-attachment: fixed;   \u002F* the parallax effect *\u002F\n  background-image: url(\"\u002Fhero.jpg\");\n}\n@media (prefers-reduced-motion: reduce) {\n  .parallax {\n    background-attachment: scroll; \u002F* image scrolls with content, no parallax *\u002F\n  }\n}\n\n\u002F* ---- Recipe 2: autoplay video -> poster --------------------------- *\u002F\n.autoplay__poster { display: none; }\n@media (prefers-reduced-motion: reduce) {\n  .autoplay__video  { display: none; }   \u002F* hide the moving video *\u002F\n  .autoplay__poster { display: block; }  \u002F* show the still frame instead *\u002F\n}\n\n\u002F* ---- Recipe 3: transition -> shortened, not deleted --------------- *\u002F\n.cta {\n  transition: background-color 0.25s ease, transform 0.25s ease;\n}\n.cta:hover { background-color: #1d49d8; transform: translateY(-2px); }\n\n@media (prefers-reduced-motion: reduce) {\n  .cta {\n    transition: background-color 0.1s ease; \u002F* keep colour feedback, drop move *\u002F\n  }\n  .cta:hover { transform: none; }           \u002F* remove the lift only *\u002F\n}\n",[18,4385,4386,4391,4396,4401,4407,4426,4440,4452,4466,4478,4482,4486,4490,4495,4502,4518,4535,4539,4545,4552,4567,4571,4575,4579,4584,4599,4605,4625,4644,4648,4652,4657,4664,4688,4692,4722,4726,4732,4740,4759,4764,4784],{"__ignoreMap":286},[290,4387,4388],{"class":163,"line":292},[290,4389,4390],{"class":455},"\u002F* ---- Recipe 0: safe global net -------------------------------------\n",[290,4392,4393],{"class":163,"line":330},[290,4394,4395],{"class":455},"   Near-instant rather than truly 0s, so animation-fill-mode: forwards\n",[290,4397,4398],{"class":163,"line":337},[290,4399,4400],{"class":455},"   still resolves to the final frame without a flicker. *\u002F\n",[290,4402,4403,4405],{"class":163,"line":364},[290,4404,874],{"class":541},[290,4406,877],{"class":295},[290,4408,4409,4411,4413,4416,4418,4420,4422,4424],{"class":163,"line":386},[290,4410,2802],{"class":299},[290,4412,569],{"class":295},[290,4414,4415],{"class":299},"*",[290,4417,2811],{"class":303},[290,4419,569],{"class":295},[290,4421,4415],{"class":299},[290,4423,2412],{"class":303},[290,4425,450],{"class":295},[290,4427,4428,4430,4432,4434,4436,4438],{"class":163,"line":408},[290,4429,2826],{"class":461},[290,4431,465],{"class":295},[290,4433,2831],{"class":461},[290,4435,542],{"class":541},[290,4437,2836],{"class":541},[290,4439,471],{"class":295},[290,4441,4442,4444,4446,4448,4450],{"class":163,"line":428},[290,4443,2843],{"class":461},[290,4445,465],{"class":295},[290,4447,468],{"class":461},[290,4449,2836],{"class":541},[290,4451,471],{"class":295},[290,4453,4454,4456,4458,4460,4462,4464],{"class":163,"line":517},[290,4455,2856],{"class":461},[290,4457,465],{"class":295},[290,4459,2831],{"class":461},[290,4461,542],{"class":541},[290,4463,2836],{"class":541},[290,4465,471],{"class":295},[290,4467,4468,4470,4472,4474,4476],{"class":163,"line":523},[290,4469,2871],{"class":461},[290,4471,465],{"class":295},[290,4473,250],{"class":461},[290,4475,2836],{"class":541},[290,4477,471],{"class":295},[290,4479,4480],{"class":163,"line":532},[290,4481,771],{"class":295},[290,4483,4484],{"class":163,"line":551},[290,4485,620],{"class":295},[290,4487,4488],{"class":163,"line":586},[290,4489,334],{"emptyLinePlaceholder":333},[290,4491,4492],{"class":163,"line":602},[290,4493,4494],{"class":455},"\u002F* ---- Recipe 1: parallax -> static --------------------------------- *\u002F\n",[290,4496,4497,4500],{"class":163,"line":617},[290,4498,4499],{"class":303},".parallax",[290,4501,450],{"class":295},[290,4503,4504,4507,4509,4512,4515],{"class":163,"line":623},[290,4505,4506],{"class":461},"  background-attachment",[290,4508,465],{"class":295},[290,4510,4511],{"class":461},"fixed",[290,4513,4514],{"class":295},";   ",[290,4516,4517],{"class":455},"\u002F* the parallax effect *\u002F\n",[290,4519,4520,4523,4525,4528,4530,4533],{"class":163,"line":628},[290,4521,4522],{"class":461},"  background-image",[290,4524,465],{"class":295},[290,4526,4527],{"class":461},"url",[290,4529,484],{"class":295},[290,4531,4532],{"class":310},"\"\u002Fhero.jpg\"",[290,4534,500],{"class":295},[290,4536,4537],{"class":163,"line":634},[290,4538,620],{"class":295},[290,4540,4541,4543],{"class":163,"line":649},[290,4542,874],{"class":541},[290,4544,877],{"class":295},[290,4546,4547,4550],{"class":163,"line":660},[290,4548,4549],{"class":303},"  .parallax",[290,4551,450],{"class":295},[290,4553,4554,4557,4559,4562,4564],{"class":163,"line":688},[290,4555,4556],{"class":461},"    background-attachment",[290,4558,465],{"class":295},[290,4560,4561],{"class":461},"scroll",[290,4563,828],{"class":295},[290,4565,4566],{"class":455},"\u002F* image scrolls with content, no parallax *\u002F\n",[290,4568,4569],{"class":163,"line":693},[290,4570,771],{"class":295},[290,4572,4573],{"class":163,"line":698},[290,4574,620],{"class":295},[290,4576,4577],{"class":163,"line":704},[290,4578,334],{"emptyLinePlaceholder":333},[290,4580,4581],{"class":163,"line":710},[290,4582,4583],{"class":455},"\u002F* ---- Recipe 2: autoplay video -> poster --------------------------- *\u002F\n",[290,4585,4586,4589,4591,4593,4595,4597],{"class":163,"line":717},[290,4587,4588],{"class":303},".autoplay__poster",[290,4590,790],{"class":295},[290,4592,59],{"class":461},[290,4594,465],{"class":295},[290,4596,72],{"class":461},[290,4598,809],{"class":295},[290,4600,4601,4603],{"class":163,"line":730},[290,4602,874],{"class":541},[290,4604,877],{"class":295},[290,4606,4607,4610,4613,4615,4617,4619,4622],{"class":163,"line":742},[290,4608,4609],{"class":303},"  .autoplay__video",[290,4611,4612],{"class":295},"  { ",[290,4614,59],{"class":461},[290,4616,465],{"class":295},[290,4618,72],{"class":461},[290,4620,4621],{"class":295},"; }   ",[290,4623,4624],{"class":455},"\u002F* hide the moving video *\u002F\n",[290,4626,4627,4630,4632,4634,4636,4638,4641],{"class":163,"line":768},[290,4628,4629],{"class":303},"  .autoplay__poster",[290,4631,790],{"class":295},[290,4633,59],{"class":461},[290,4635,465],{"class":295},[290,4637,68],{"class":461},[290,4639,4640],{"class":295},"; }  ",[290,4642,4643],{"class":455},"\u002F* show the still frame instead *\u002F\n",[290,4645,4646],{"class":163,"line":774},[290,4647,620],{"class":295},[290,4649,4650],{"class":163,"line":779},[290,4651,334],{"emptyLinePlaceholder":333},[290,4653,4654],{"class":163,"line":784},[290,4655,4656],{"class":455},"\u002F* ---- Recipe 3: transition -> shortened, not deleted --------------- *\u002F\n",[290,4658,4659,4662],{"class":163,"line":812},[290,4660,4661],{"class":303},".cta",[290,4663,450],{"class":295},[290,4665,4666,4668,4671,4673,4675,4677,4680,4682,4684,4686],{"class":163,"line":860},[290,4667,526],{"class":461},[290,4669,4670],{"class":295},": background-color ",[290,4672,1668],{"class":461},[290,4674,1886],{"class":541},[290,4676,545],{"class":461},[290,4678,4679],{"class":295},", transform ",[290,4681,1668],{"class":461},[290,4683,1886],{"class":541},[290,4685,545],{"class":461},[290,4687,471],{"class":295},[290,4689,4690],{"class":163,"line":865},[290,4691,620],{"class":295},[290,4693,4694,4697,4699,4701,4703,4706,4708,4710,4712,4714,4716,4718,4720],{"class":163,"line":871},[290,4695,4696],{"class":303},".cta:hover",[290,4698,790],{"class":295},[290,4700,2675],{"class":461},[290,4702,465],{"class":295},[290,4704,4705],{"class":461},"#1d49d8",[290,4707,828],{"class":295},[290,4709,103],{"class":461},[290,4711,465],{"class":295},[290,4713,481],{"class":461},[290,4715,484],{"class":295},[290,4717,3561],{"class":461},[290,4719,674],{"class":541},[290,4721,1122],{"class":295},[290,4723,4724],{"class":163,"line":880},[290,4725,334],{"emptyLinePlaceholder":333},[290,4727,4728,4730],{"class":163,"line":896},[290,4729,874],{"class":541},[290,4731,877],{"class":295},[290,4733,4735,4738],{"class":163,"line":4734},38,[290,4736,4737],{"class":303},"  .cta",[290,4739,450],{"class":295},[290,4741,4743,4746,4748,4750,4752,4754,4756],{"class":163,"line":4742},39,[290,4744,4745],{"class":461},"    transition",[290,4747,4670],{"class":295},[290,4749,3125],{"class":461},[290,4751,1886],{"class":541},[290,4753,545],{"class":461},[290,4755,828],{"class":295},[290,4757,4758],{"class":455},"\u002F* keep colour feedback, drop move *\u002F\n",[290,4760,4762],{"class":163,"line":4761},40,[290,4763,771],{"class":295},[290,4765,4767,4770,4772,4774,4776,4778,4781],{"class":163,"line":4766},41,[290,4768,4769],{"class":303},"  .cta:hover",[290,4771,790],{"class":295},[290,4773,103],{"class":461},[290,4775,465],{"class":295},[290,4777,72],{"class":461},[290,4779,4780],{"class":295},"; }           ",[290,4782,4783],{"class":455},"\u002F* remove the lift only *\u002F\n",[290,4785,4787],{"class":163,"line":4786},42,[290,4788,620],{"class":295},[14,4790,4791],{},"Notice that none of the reduced paths is \"nothing happens.\" Parallax becomes a normal scrolling image, the video becomes its own poster frame, and the button keeps its colour feedback while losing only the lift. That is reduce-not-remove in three concrete forms.",[50,4793,4795],{"id":4794},"the-technique-that-makes-it-work","The technique that makes it work",[14,4797,4798,4799,4801,4802,4805],{},"The autoplay recipe shows the load-bearing move: rather than trying to stop the video with CSS (which cannot pause playback), you hide the moving element and reveal a still image that was already in the markup. CSS cannot control media playback, but it can control which of two siblings is displayed, so the swap is purely a ",[18,4800,59],{}," toggle inside the media query. For true autoplay control you would add a one-line JavaScript check — ",[18,4803,4804],{},"if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) video.pause()"," — but the CSS swap alone removes the motion from the user's view. Across all three recipes the pattern is identical: define the full experience as the default, then override only the motion-bearing properties inside the query.",[50,4807,4809],{"id":4808},"variation-a-custom-property-speed-dial","Variation: a custom-property speed dial",[14,4811,4812],{},"For a design system, expose a single duration token and let the query dim it, so every component that reads the token reduces at once without per-component overrides.",[281,4814,4816],{"className":438,"code":4815,"language":440,"meta":286,"style":286},":root { --motion: 1; }                 \u002F* 1 = full speed *\u002F\n@media (prefers-reduced-motion: reduce) {\n  :root { --motion: 0.01; }            \u002F* near-instant everywhere *\u002F\n}\n.fx { transition-duration: calc(250ms * var(--motion)); }\n",[18,4817,4818,4837,4843,4861,4865],{"__ignoreMap":286},[290,4819,4820,4822,4824,4827,4829,4831,4834],{"class":163,"line":292},[290,4821,1554],{"class":303},[290,4823,790],{"class":295},[290,4825,4826],{"class":1561},"--motion",[290,4828,465],{"class":295},[290,4830,468],{"class":461},[290,4832,4833],{"class":295},"; }                 ",[290,4835,4836],{"class":455},"\u002F* 1 = full speed *\u002F\n",[290,4838,4839,4841],{"class":163,"line":330},[290,4840,874],{"class":541},[290,4842,877],{"class":295},[290,4844,4845,4847,4849,4851,4853,4855,4858],{"class":163,"line":337},[290,4846,3430],{"class":303},[290,4848,790],{"class":295},[290,4850,4826],{"class":1561},[290,4852,465],{"class":295},[290,4854,2831],{"class":461},[290,4856,4857],{"class":295},"; }            ",[290,4859,4860],{"class":455},"\u002F* near-instant everywhere *\u002F\n",[290,4862,4863],{"class":163,"line":364},[290,4864,620],{"class":295},[290,4866,4867,4869,4871,4874,4876,4878,4880,4882,4884,4886,4888,4890,4892],{"class":163,"line":386},[290,4868,4155],{"class":303},[290,4870,790],{"class":295},[290,4872,4873],{"class":461},"transition-duration",[290,4875,465],{"class":295},[290,4877,3556],{"class":461},[290,4879,484],{"class":295},[290,4881,3112],{"class":461},[290,4883,542],{"class":541},[290,4885,3566],{"class":541},[290,4887,1635],{"class":461},[290,4889,484],{"class":295},[290,4891,4826],{"class":1561},[290,4893,4894],{"class":295},")); }\n",[14,4896,4897,4898,42],{},"This pairs naturally with token-driven timing from ",[27,4899,4901],{"href":4900},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Ffluid-spacing-tokens-driving-transition-durations\u002F","Fluid Spacing Tokens Driving Transition Durations",[50,4903,4904],{"id":2247},"Browser support",[14,4906,4907,4909,4910,69,4913,4915,4916,4918,4919,4921,4922,4924],{},[18,4908,2584],{}," is supported in Chrome 74+, Edge 79+, Firefox 63+, and Safari 10.1+. ",[18,4911,4912],{},"background-attachment: fixed",[18,4914,59],{}," toggles have universal support, and the ",[18,4917,2919],{}," API for the optional JavaScript pause is supported everywhere evergreen. Unsupporting browsers ignore the ",[18,4920,874],{}," blocks and render the full-motion defaults, so no ",[18,4923,2086],{}," guard is needed.",[50,4926,1316],{"id":1315},[14,4928,4929,4935],{},[62,4930,4931,4932,4934],{},"Should ",[18,4933,2584],{}," remove all animation?","\nNot always. The reduce-not-remove principle says to cut motion that implies movement while keeping useful feedback like fades or instant state changes. Full removal is fine as a safe default, but a thoughtful reduction is usually better UX.",[14,4937,4938,4941],{},[62,4939,4940],{},"How do I handle autoplaying media under reduced motion?","\nPause autoplay and offer a manual control. For looping background video or animated GIFs, swap to a static poster image inside the reduce block, and never auto-restart playback.",[14,4943,4944,4947,4948,69,4951,80,4953,3942,4955,4958,4959,4962],{},[62,4945,4946],{},"What is a safe global reset for reduced motion?","\nA universal selector block that sets ",[18,4949,4950],{},"animation-duration",[18,4952,4873],{},[18,4954,2773],{},[18,4956,4957],{},"animation-iteration-count: 1",", marked important. It neutralizes runaway motion site-wide while leaving a near-instant state change so ",[18,4960,4961],{},"fill-mode: forwards"," still applies.",[14,4964,4965,4968,4969,4972],{},[62,4966,4967],{},"Can I detect reduced motion in JavaScript too?","\nYes. Use ",[18,4970,4971],{},"window.matchMedia('(prefers-reduced-motion: reduce)').matches"," to branch logic, and listen for changes so you respond if the user toggles the preference while the page is open.",[50,4974,1391],{"id":1390},[1393,4976,4977,4982,4987,4992,4997],{},[1396,4978,4979,4981],{},[27,4980,1476],{"href":1475}," — the parent guide on accessible motion.",[1396,4983,4984,4986],{},[27,4985,2511],{"href":1412}," — the underlying media-query syntax and cascade.",[1396,4988,4989,4991],{},[27,4990,2518],{"href":2517}," — which motion to avoid before you ever need a reduction.",[1396,4993,4994,4996],{},[27,4995,4901],{"href":4900}," — token-driven timing that dims cleanly under reduced motion.",[1396,4998,4999,5003],{},[27,5000,5002],{"href":5001},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-typography-without-javascript\u002F","Fluid Typography Without JavaScript"," — responsive scaling that needs no animated zoom.",[1430,5005,5006],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":286,"searchDepth":330,"depth":330,"links":5008},[5009,5010,5011,5012,5013,5014,5015],{"id":4119,"depth":330,"text":4120},{"id":4202,"depth":330,"text":4203},{"id":4794,"depth":330,"text":4795},{"id":4808,"depth":330,"text":4809},{"id":2247,"depth":330,"text":4904},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Copy-paste prefers-reduced-motion recipes for parallax, autoplay, and transitions, built on the reduce-not-remove principle for accessible, honest motion.",{"seoTitle":5018,"datePublished":1447,"dateModified":1447,"faq":5019},"prefers-reduced-motion: Copy-Paste Recipes",[5020,5023,5025,5027],{"q":5021,"a":5022},"Should prefers-reduced-motion remove all animation?","Not always. The reduce-not-remove principle says to cut motion that implies movement while keeping useful feedback like fades or instant state changes. Full removal is fine as a safe default, but a thoughtful reduction is usually better UX.",{"q":4940,"a":5024},"Pause autoplay and offer a manual control. For looping background video or animated GIFs, swap to a static poster image inside the reduce block, and never auto-restart playback.",{"q":4946,"a":5026},"A universal selector block that sets animation-duration and transition-duration to 0.01ms with animation-iteration-count 1, marked important. It neutralizes runaway motion site-wide while leaving a near-instant state change so fill-mode forwards still applies.",{"q":4967,"a":5028},"Yes. Use window.matchMedia('(prefers-reduced-motion: reduce)').matches to branch logic, and listen for changes so you respond if the user toggles the preference while the page is open.","\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fprefers-reduced-motion-recipes",{"title":4076,"description":5016},"css-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fprefers-reduced-motion-recipes\u002Findex","RHekeYJI5tlqtVi6G8mYFlYCAxnFPswxFkvAjHviuZw",{"id":5034,"title":5035,"body":5036,"description":5798,"extension":1444,"meta":5799,"navigation":333,"path":5810,"seo":5811,"stem":5812,"__hash__":5813},"content\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Freducing-motion-preferences-in-css\u002Findex.md","Implementing prefers-reduced-motion in CSS: A Developer Reference",{"type":7,"value":5037,"toc":5783},[5038,5041,5050,5055,5069,5076,5085,5097,5100,5165,5169,5190,5194,5197,5290,5294,5297,5370,5374,5377,5506,5510,5513,5568,5572,5578,5603,5605,5642,5648,5652,5708,5710,5725,5745,5754,5756,5780],[10,5039,5035],{"id":5040},"implementing-prefers-reduced-motion-in-css-a-developer-reference",[14,5042,5043,5044,5046,5047,5049],{},"When building modern interfaces, respecting user system settings is non-negotiable for accessibility. This page is part of the ",[27,5045,1476],{"href":1475}," guide and provides a precise, fallback-ready implementation of the ",[18,5048,2584],{}," media query. We cover exact syntax, performance implications, and how to gracefully degrade complex micro-interactions without breaking layout or UX.",[14,5051,5052],{},[62,5053,5054],{},"Key implementation goals:",[1393,5056,5057,5060,5063,5066],{},[1396,5058,5059],{},"System-level preference detection via CSS media queries",[1396,5061,5062],{},"Graceful degradation of keyframes and transitions",[1396,5064,5065],{},"Performance optimization by disabling GPU-heavy transforms",[1396,5067,5068],{},"Seamless integration with existing component architectures",[50,5070,5072,5073,5075],{"id":5071},"understanding-the-prefers-reduced-motion-media-query","Understanding the ",[18,5074,2584],{}," Media Query",[14,5077,1517,5078,5080,5081,5084],{},[18,5079,2584],{}," media query acts as a direct bridge between OS-level accessibility toggles and your stylesheet. It evaluates to ",[18,5082,5083],{},"true"," when a user explicitly requests minimized motion in their system preferences (macOS, Windows, iOS, Android). The query supports two primary values:",[1393,5086,5087,5092],{},[1396,5088,5089,5091],{},[18,5090,2623],{},": Default state. Standard animations and transitions render normally.",[1396,5093,5094,5096],{},[18,5095,2627],{},": User has requested minimized motion. CSS rules inside this block activate.",[14,5098,5099],{},"This is a boolean-like evaluation handled entirely by the browser’s rendering engine. It requires zero JavaScript for baseline implementation, making it highly reliable for progressive enhancement and critical rendering path optimization.",[133,5101,140,5104,140,5107,140,5110,140,5113,140,5116,140,5121,140,5125,140,5128,140,5131,140,5134,140,5136,140,5141,140,5145,140,5147,140,5150,140,5152,140,5156,140,5161],{"viewBox":5102,"role":136,"ariaLabel":5103,"xmlns":138,"style":139},"0 0 720 230","Decision flow from the OS reduce-motion toggle through the media query to two CSS outcomes",[142,5105,5106],{},"prefers-reduced-motion decision flow",[146,5108,5109],{},"The OS toggle drives the media query, which branches to full motion when no-preference and to instant or minimal motion when reduce.",[150,5111,5112],{"x":152,"y":153,"style":1781},"Where the preference comes from",[171,5114],{"x":2598,"y":5115,"width":1822,"height":1788,"rx":176,"fill":177,"opacity":178,"stroke":167,"strokeWidth":1789},"90",[150,5117,5120],{"x":5118,"y":5119,"style":1794},"115","114","OS motion toggle",[150,5122,5124],{"x":5118,"y":5123,"style":1808},"132","macOS \u002F iOS \u002F Win",[171,5126],{"x":5127,"y":5115,"width":1822,"height":1788,"rx":176,"fill":72,"stroke":167,"strokeWidth":1789},"275",[150,5129,5130],{"x":152,"y":5119,"style":183},"@media query",[150,5132,5133],{"x":152,"y":5123,"style":1808},"evaluated by engine",[163,5135],{"x1":174,"y1":4158,"x2":5127,"y2":4158,"stroke":167,"strokeWidth":1824},[171,5137],{"x":5138,"y":5139,"width":5140,"height":1831,"rx":176,"fill":72,"stroke":167,"strokeWidth":1789},"520","48","175",[150,5142,2623],{"x":5143,"y":5144,"style":183},"607","70",[150,5146,4177],{"x":5143,"y":1823,"style":1808},[171,5148],{"x":5138,"y":5149,"width":5140,"height":1831,"rx":176,"fill":177,"opacity":1813,"stroke":167,"strokeWidth":1789},"138",[150,5151,2627],{"x":5143,"y":1830,"style":183},[150,5153,5155],{"x":5143,"y":5154,"style":4181},"178","instant \u002F minimal",[163,5157],{"x1":5158,"y1":5159,"x2":5138,"y2":5160,"stroke":167,"strokeWidth":1824},"445","110","73",[163,5162],{"x1":5158,"y1":5163,"x2":5138,"y2":5164,"stroke":167,"strokeWidth":1824},"126","163",[50,5166,5168],{"id":5167},"implementation-patterns-syntax","Implementation Patterns & Syntax",[14,5170,5171,5172,5175,5176,69,5179,5181,5182,5184,5185,5187,5188,42],{},"To properly handle ",[62,5173,5174],{},"reducing motion preferences in CSS",", you must explicitly override ",[18,5177,5178],{},"animation",[18,5180,887],{}," properties. Relying on implicit browser behavior will fail across different component states. Below are production-ready patterns that integrate cleanly into ",[27,5183,1481],{"href":1480}," workflows. For copy-paste overrides organized by component type, see the ",[27,5186,2781],{"href":2524},"; for the design principles behind which motion to cut entirely, see ",[27,5189,2657],{"href":2517},[2757,5191,5193],{"id":5192},"global-motion-override-pattern","Global Motion Override Pattern",[14,5195,5196],{},"A safe, high-cascade block that neutralizes motion across the entire DOM when the preference is active.",[281,5198,5200],{"className":438,"code":5199,"language":440,"meta":286,"style":286},"@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    animation-duration: 0.01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: 0.01ms !important;\n    scroll-behavior: auto !important;\n  }\n}\n",[18,5201,5202,5208,5214,5222,5230,5244,5256,5270,5282,5286],{"__ignoreMap":286},[290,5203,5204,5206],{"class":163,"line":292},[290,5205,874],{"class":541},[290,5207,877],{"class":295},[290,5209,5210,5212],{"class":163,"line":330},[290,5211,2802],{"class":299},[290,5213,548],{"class":295},[290,5215,5216,5218,5220],{"class":163,"line":337},[290,5217,2802],{"class":299},[290,5219,2811],{"class":303},[290,5221,548],{"class":295},[290,5223,5224,5226,5228],{"class":163,"line":364},[290,5225,2802],{"class":299},[290,5227,2412],{"class":303},[290,5229,450],{"class":295},[290,5231,5232,5234,5236,5238,5240,5242],{"class":163,"line":386},[290,5233,2826],{"class":461},[290,5235,465],{"class":295},[290,5237,2831],{"class":461},[290,5239,542],{"class":541},[290,5241,2836],{"class":541},[290,5243,471],{"class":295},[290,5245,5246,5248,5250,5252,5254],{"class":163,"line":408},[290,5247,2843],{"class":461},[290,5249,465],{"class":295},[290,5251,468],{"class":461},[290,5253,2836],{"class":541},[290,5255,471],{"class":295},[290,5257,5258,5260,5262,5264,5266,5268],{"class":163,"line":428},[290,5259,2856],{"class":461},[290,5261,465],{"class":295},[290,5263,2831],{"class":461},[290,5265,542],{"class":541},[290,5267,2836],{"class":541},[290,5269,471],{"class":295},[290,5271,5272,5274,5276,5278,5280],{"class":163,"line":517},[290,5273,2871],{"class":461},[290,5275,465],{"class":295},[290,5277,250],{"class":461},[290,5279,2836],{"class":541},[290,5281,471],{"class":295},[290,5283,5284],{"class":163,"line":523},[290,5285,771],{"class":295},[290,5287,5288],{"class":163,"line":532},[290,5289,620],{"class":295},[2757,5291,5293],{"id":5292},"component-specific-transition-override","Component-Specific Transition Override",[14,5295,5296],{},"For interactive UI elements relying on hover\u002Ffocus states, strip motion while preserving state changes.",[281,5298,5300],{"className":438,"code":5299,"language":440,"meta":286,"style":286},"@media (prefers-reduced-motion: reduce) {\n  .ui-button {\n    transition: none;\n    transform: none;\n  }\n  .ui-button:hover {\n    background-color: var(--color-hover);\n  }\n}\n",[18,5301,5302,5308,5315,5325,5335,5339,5346,5362,5366],{"__ignoreMap":286},[290,5303,5304,5306],{"class":163,"line":292},[290,5305,874],{"class":541},[290,5307,877],{"class":295},[290,5309,5310,5313],{"class":163,"line":330},[290,5311,5312],{"class":303},"  .ui-button",[290,5314,450],{"class":295},[290,5316,5317,5319,5321,5323],{"class":163,"line":337},[290,5318,4745],{"class":461},[290,5320,465],{"class":295},[290,5322,72],{"class":461},[290,5324,471],{"class":295},[290,5326,5327,5329,5331,5333],{"class":163,"line":364},[290,5328,745],{"class":461},[290,5330,465],{"class":295},[290,5332,72],{"class":461},[290,5334,471],{"class":295},[290,5336,5337],{"class":163,"line":386},[290,5338,771],{"class":295},[290,5340,5341,5344],{"class":163,"line":408},[290,5342,5343],{"class":303},"  .ui-button:hover",[290,5345,450],{"class":295},[290,5347,5348,5351,5353,5355,5357,5360],{"class":163,"line":428},[290,5349,5350],{"class":461},"    background-color",[290,5352,465],{"class":295},[290,5354,1622],{"class":461},[290,5356,484],{"class":295},[290,5358,5359],{"class":1561},"--color-hover",[290,5361,500],{"class":295},[290,5363,5364],{"class":163,"line":517},[290,5365,771],{"class":295},[290,5367,5368],{"class":163,"line":523},[290,5369,620],{"class":295},[2757,5371,5373],{"id":5372},"css-custom-property-toggle","CSS Custom Property Toggle",[14,5375,5376],{},"Dynamically swap animation values without duplicating selectors. This is the cleanest approach for scalable design systems.",[281,5378,5380],{"className":438,"code":5379,"language":440,"meta":286,"style":286},":root {\n  --motion-duration: 300ms;\n  --motion-easing: ease-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  :root {\n    --motion-duration: 0.01ms;\n    --motion-easing: linear;\n  }\n}\n\n.animated-element {\n  transition: transform var(--motion-duration) var(--motion-easing);\n}\n",[18,5381,5382,5388,5402,5414,5418,5422,5428,5434,5447,5459,5463,5467,5471,5478,5502],{"__ignoreMap":286},[290,5383,5384,5386],{"class":163,"line":292},[290,5385,1554],{"class":303},[290,5387,450],{"class":295},[290,5389,5390,5393,5395,5398,5400],{"class":163,"line":330},[290,5391,5392],{"class":1561},"  --motion-duration",[290,5394,465],{"class":295},[290,5396,5397],{"class":461},"300",[290,5399,542],{"class":541},[290,5401,471],{"class":295},[290,5403,5404,5407,5409,5412],{"class":163,"line":337},[290,5405,5406],{"class":1561},"  --motion-easing",[290,5408,465],{"class":295},[290,5410,5411],{"class":461},"ease-out",[290,5413,471],{"class":295},[290,5415,5416],{"class":163,"line":364},[290,5417,620],{"class":295},[290,5419,5420],{"class":163,"line":386},[290,5421,334],{"emptyLinePlaceholder":333},[290,5423,5424,5426],{"class":163,"line":408},[290,5425,874],{"class":541},[290,5427,877],{"class":295},[290,5429,5430,5432],{"class":163,"line":428},[290,5431,3430],{"class":303},[290,5433,450],{"class":295},[290,5435,5436,5439,5441,5443,5445],{"class":163,"line":517},[290,5437,5438],{"class":1561},"    --motion-duration",[290,5440,465],{"class":295},[290,5442,2831],{"class":461},[290,5444,542],{"class":541},[290,5446,471],{"class":295},[290,5448,5449,5452,5454,5457],{"class":163,"line":523},[290,5450,5451],{"class":1561},"    --motion-easing",[290,5453,465],{"class":295},[290,5455,5456],{"class":461},"linear",[290,5458,471],{"class":295},[290,5460,5461],{"class":163,"line":532},[290,5462,771],{"class":295},[290,5464,5465],{"class":163,"line":551},[290,5466,620],{"class":295},[290,5468,5469],{"class":163,"line":586},[290,5470,334],{"emptyLinePlaceholder":333},[290,5472,5473,5476],{"class":163,"line":602},[290,5474,5475],{"class":303},".animated-element",[290,5477,450],{"class":295},[290,5479,5480,5482,5484,5486,5488,5491,5493,5495,5497,5500],{"class":163,"line":617},[290,5481,526],{"class":461},[290,5483,1880],{"class":295},[290,5485,1622],{"class":461},[290,5487,484],{"class":295},[290,5489,5490],{"class":1561},"--motion-duration",[290,5492,490],{"class":295},[290,5494,1622],{"class":461},[290,5496,484],{"class":295},[290,5498,5499],{"class":1561},"--motion-easing",[290,5501,500],{"class":295},[290,5503,5504],{"class":163,"line":623},[290,5505,620],{"class":295},[50,5507,5509],{"id":5508},"debugging-fallback-strategies","Debugging & Fallback Strategies",[14,5511,5512],{},"Implementing these overrides often triggers cascade conflicts. Follow these debugging steps:",[3017,5514,5515,5527,5546,5559],{},[1396,5516,5517,5520,5521,5523,5524,5526],{},[62,5518,5519],{},"Specificity Wars:"," Utility frameworks often inject inline or high-specificity classes. Scope your ",[18,5522,874],{}," overrides at the end of your stylesheet or use component-level selectors to avoid ",[18,5525,3900],{}," bloat.",[1396,5528,5529,1529,5532,5535,5536,5538,5539,5541,5542,5545],{},[18,5530,5531],{},"animation: none",[18,5533,5534],{},"animation-duration: 0.01ms",": Use ",[18,5537,5531],{}," to halt keyframe execution entirely. Use ",[18,5540,2773],{}," when components depend on ",[18,5543,5544],{},"animation-fill-mode: forwards"," to maintain the final rendered state without a visual flicker or layout jump.",[1396,5547,5548,5551,5552,5554,5555,5558],{},[62,5549,5550],{},"DevTools Emulation:"," Do not rely on manual OS toggling during development. In Chrome\u002FFirefox DevTools, open the ",[62,5553,3024],{}," panel and force ",[18,5556,5557],{},"prefers-reduced-motion: reduce",". Verify that hover\u002Ffocus states remain accessible without motion.",[1396,5560,5561,5564,5565,5567],{},[62,5562,5563],{},"Legacy Fallbacks:"," Browsers that do not support the media query will ignore the ",[18,5566,874],{}," block and render default animations. This is safe progressive enhancement. No polyfills are required.",[50,5569,5571],{"id":5570},"performance-gpu-considerations","Performance & GPU Considerations",[14,5573,5574,5575,5577],{},"Disabling motion isn't just an accessibility requirement; it directly impacts rendering performance. When ",[18,5576,5557],{}," is active:",[1393,5579,5580,5586,5597],{},[1396,5581,5582,5585],{},[62,5583,5584],{},"Prevents Forced Compositing:"," Animated layers that would normally promote to the GPU compositor remain on the main thread, reducing memory overhead.",[1396,5587,5588,5591,5592,69,5594,5596],{},[62,5589,5590],{},"Eliminates Layout Thrashing:"," By stripping ",[18,5593,4873],{},[18,5595,4950],{},", the browser skips intermediate paint and layout recalculations, directly improving Interaction to Next Paint (INP) and Cumulative Layout Shift (CLS) scores.",[1396,5598,5599,5602],{},[62,5600,5601],{},"Battery & Thermal Efficiency:"," Continuous transform animations trigger repeated compositor ticks. Neutralizing them reduces CPU\u002FGPU wake cycles, extending battery life on mobile devices.",[50,5604,2248],{"id":2247},[2250,5606,5607,5616],{},[2253,5608,5609],{},[2256,5610,5611,5613],{},[2259,5612,2261],{},[2259,5614,5615],{},"Minimum Version",[2269,5617,5618,5624,5630,5636],{},[2256,5619,5620,5622],{},[2274,5621,2276],{},[2274,5623,3758],{},[2256,5625,5626,5628],{},[2274,5627,2287],{},[2274,5629,3761],{},[2256,5631,5632,5634],{},[2274,5633,2297],{},[2274,5635,3764],{},[2256,5637,5638,5640],{},[2274,5639,2308],{},[2274,5641,3767],{},[14,5643,5644,5647],{},[86,5645,5646],{},"Note:"," iOS Safari and Android Chrome support varies slightly with OS version. Legacy browsers safely ignore the query. Always test with DevTools emulation.",[50,5649,5651],{"id":5650},"common-issues-solutions","Common Issues & Solutions",[2250,5653,5654,5662],{},[2253,5655,5656],{},[2256,5657,5658,5660],{},[2259,5659,2338],{},[2259,5661,2341],{},[2269,5663,5664,5672,5697],{},[2256,5665,5666,5669],{},[2274,5667,5668],{},"Animations still trigger despite media query",[2274,5670,5671],{},"Check CSS specificity. Framework utility classes often require higher cascade priority or scoped overrides. Ensure property names match exactly.",[2256,5673,5674,5677],{},[2274,5675,5676],{},"Layout shifts when motion is disabled",[2274,5678,1499,5679,2351,5682,2774,5684,5686,5687,5689,5690,569,5692,569,5694,5696],{},[18,5680,5681],{},"visibility: hidden",[18,5683,907],{},[18,5685,20],{}," if space must be preserved. Animate ",[18,5688,103],{}," instead of layout properties (",[18,5691,2725],{},[18,5693,2728],{},[18,5695,2731],{},") to avoid reflow.",[2256,5698,5699,5702],{},[2274,5700,5701],{},"JavaScript-driven animations ignore CSS preference",[2274,5703,5704,5705,5707],{},"Query the preference in JS: ",[18,5706,4971],{},". Conditionally skip animation logic or apply static classes.",[50,5709,1316],{"id":1315},[14,5711,5712,5718,5719,5721,5722,5724],{},[62,5713,5714,5715,5717],{},"Does ",[18,5716,2584],{}," disable all animations automatically?","\nNo. The media query only evaluates to ",[18,5720,5083],{}," when the OS setting is active. You must explicitly write CSS rules inside the ",[18,5723,874],{}," block to override or modify animations.",[14,5726,5727,5735,5736,5738,5739,5741,5742,5744],{},[62,5728,2432,5729,2351,5731,5734],{},[18,5730,5531],{},[18,5732,5733],{},"animation-duration: 0s","?","\nUse ",[18,5737,5531],{}," to prevent keyframe execution entirely. Use ",[18,5740,5534],{}," if your component relies on ",[18,5743,5544],{}," to maintain the final state without visual flicker.",[14,5746,5747,5750,5751,5753],{},[62,5748,5749],{},"How do I test this locally without changing OS settings?","\nUse Chrome or Firefox DevTools > Elements > CSS inspector to toggle ",[18,5752,2584],{},", or enable emulation in the Rendering panel. This allows real-time debugging of fallback states.",[50,5755,1391],{"id":1390},[1393,5757,5758,5763,5768,5773],{},[1396,5759,5760,5762],{},[27,5761,1476],{"href":1475}," — the parent guide for accessible motion and interaction design.",[1396,5764,5765,5767],{},[27,5766,2518],{"href":2517}," — which animations to remove or soften for motion-sensitive users.",[1396,5769,5770,5772],{},[27,5771,2525],{"href":2524}," — component-by-component overrides built on this media query.",[1396,5774,5775,5779],{},[27,5776,5778],{"href":5777},"\u002Fmastering-container-queries-responsive-layouts\u002F","Container Queries Guide"," — the responsive-layout area, where the same preference-driven approach gates motion in adaptive components.",[1430,5781,5782],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":286,"searchDepth":330,"depth":330,"links":5784},[5785,5787,5792,5793,5794,5795,5796,5797],{"id":5071,"depth":330,"text":5786},"Understanding the prefers-reduced-motion Media Query",{"id":5167,"depth":330,"text":5168,"children":5788},[5789,5790,5791],{"id":5192,"depth":337,"text":5193},{"id":5292,"depth":337,"text":5293},{"id":5372,"depth":337,"text":5373},{"id":5508,"depth":330,"text":5509},{"id":5570,"depth":330,"text":5571},{"id":2247,"depth":330,"text":2248},{"id":5650,"depth":330,"text":5651},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Implement prefers-reduced-motion in CSS: syntax reference, performance-safe fallback strategies, and accessible UI animation approaches.",{"seoTitle":5800,"datePublished":1447,"dateModified":1447,"faq":5801},"prefers-reduced-motion in CSS: A Reference",[5802,5805,5808],{"q":5803,"a":5804},"Does prefers-reduced-motion disable all animations automatically?","No. The media query only evaluates to true when the OS setting is active. You must explicitly write CSS rules inside the @media block to override or modify animations.",{"q":5806,"a":5807},"Should I use animation: none or animation-duration: 0s?","Use animation: none to prevent keyframe execution entirely. Use animation-duration: 0.01ms if your component relies on animation-fill-mode: forwards to maintain its final state without a visual flicker.",{"q":5749,"a":5809},"Use Chrome or Firefox DevTools to toggle prefers-reduced-motion from the Rendering panel, or via the CSS inspector. This lets you debug fallback states in real time without changing system preferences.","\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Freducing-motion-preferences-in-css",{"title":5035,"description":5798},"css-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Freducing-motion-preferences-in-css\u002Findex","HZoL3bpFa0aMjog2ictJ8aGslD9oLfqzjJn5cBk8vfg",{"id":5815,"title":5816,"body":5817,"description":6551,"extension":1444,"meta":6552,"navigation":333,"path":6565,"seo":6566,"stem":6567,"__hash__":6568},"content\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fvestibular-safe-animation-patterns\u002Findex.md","Vestibular-Safe Animation Patterns: Motion That Does Not Make People Sick",{"type":7,"value":5818,"toc":6542},[5819,5822,5831,5836,5855,5859,5862,5869,5925,5927,5933,5988,6221,6224,6333,6335,6353,6357,6360,6454,6457,6459,6475,6477,6483,6492,6504,6510,6512,6539],[10,5820,5816],{"id":5821},"vestibular-safe-animation-patterns-motion-that-does-not-make-people-sick",[14,5823,5824,5825,5827,5828,5830],{},"Roughly one in three adults experiences some form of vestibular dysfunction in their lifetime, and for many of them a parallax hero section or a full-screen zoom transition is not delightful — it is nauseating. The narrow problem this guide solves is choosing animation that conveys change and polish without triggering the dizziness, disorientation, and nausea that large screen motion can induce, and gating anything questionable behind a preference. This page sits under ",[27,5826,1476],{"href":1475},", and it complements the mechanics of ",[27,5829,2511],{"href":1412}," by focusing on which motion is safe in the first place.",[14,5832,5833],{},[62,5834,5835],{},"The principles in brief:",[1393,5837,5838,5841,5846,5849],{},[1396,5839,5840],{},"Large movement implies self-motion and can trigger symptoms",[1396,5842,5843,5845],{},[18,5844,76],{}," and small translations are the safe defaults",[1396,5847,5848],{},"Avoid parallax, big zoom\u002Fscale, and spin as primary effects",[1396,5850,5851,5852,5854],{},"Always provide a ",[18,5853,2584],{}," reduction",[50,5856,5858],{"id":5857},"why-large-motion-is-the-problem","Why large motion is the problem",[14,5860,5861],{},"The vestibular system in the inner ear senses motion and balance. When the eyes report large movement that the body did not perform — a background sliding at a different speed than the foreground, an element rushing toward the viewer, a section spinning into place — the brain receives a mismatch between visual and physical signals. That mismatch is the same conflict behind motion sickness, and for people with vestibular disorders it can be provoked by far smaller stimuli than it would in others. Symptoms range from mild unease to migraine, vertigo, and nausea that linger long after the page is closed.",[14,5863,5864,5865,5868],{},"The design takeaway is not \"no animation.\" It is that ",[62,5866,5867],{},"scale and distance are the risk factors",", not animation itself. A 4px lift on hover communicates interactivity without implying you are moving through space. A parallax layer that travels hundreds of pixels as you scroll does imply exactly that. The diagram below contrasts an unsafe large transform with a safe fade so the difference in visual displacement is concrete.",[133,5870,140,5872,140,5875,140,5878,140,5881,140,5885,140,5889,140,5895,140,5898,140,5902,140,5907,140,5912,140,5917,140,5919,140,5921],{"viewBox":4133,"role":136,"ariaLabel":5871,"xmlns":138,"style":139},"A large scaling and sliding animation versus a safe opacity fade",[142,5873,5874],{},"Unsafe large transform versus safe fade",[146,5876,5877],{},"The left panel shows an element scaling and sliding a long distance; the right panel shows the same element fading in place.",[150,5879,5880],{"x":152,"y":153,"style":1781},"Large transform vs fade in place",[150,5882,5884],{"x":2602,"y":4172,"style":5883},"text-anchor:middle;fill:currentColor;font:600 14px sans-serif","Risky",[150,5886,5888],{"x":5887,"y":4172,"style":5883},"540","Safe",[171,5890],{"x":5891,"y":5892,"width":5893,"height":174,"rx":5894,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},"32","72","296","12",[171,5896],{"x":5897,"y":5892,"width":5893,"height":174,"rx":5894,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},"392",[171,5899],{"x":5900,"y":174,"width":1786,"height":4146,"rx":5901,"fill":177,"opacity":178},"52","6",[171,5903],{"x":5904,"y":165,"width":4196,"height":5905,"rx":5901,"fill":177,"opacity":5906},"226","62","0.32",[163,5908],{"x1":2614,"y1":538,"x2":5904,"y2":5909,"stroke":177,"strokeWidth":194,"strokeDashArray":5910},"135",[5901,5911],"5",[150,5913,5916],{"x":2602,"y":5914,"style":5915},"262","text-anchor:middle;fill:currentColor;font:12px sans-serif;opacity:0.75","scale + long travel",[171,5918],{"x":5887,"y":5909,"width":4196,"height":5905,"rx":5901,"fill":177,"opacity":3241},[171,5920],{"x":5887,"y":5909,"width":4196,"height":5905,"rx":5901,"fill":177,"opacity":5906},[150,5922,5924],{"x":5923,"y":5914,"style":5915},"586","opacity 0 to 1, no move",[50,5926,4203],{"id":4202},[14,5928,5929,5930,5932],{},"This is a content-reveal pattern — the kind people reach for on scroll or load. The default is a gentle fade with a tiny 8px rise; there is no large slide, no zoom, no spin. Under ",[18,5931,5557],{}," it collapses to a plain fade, and the rise is removed entirely.",[281,5934,5936],{"className":283,"code":5935,"language":285,"meta":286,"style":286},"\u003Csection class=\"reveal\">\n  \u003Ch2>Section heading\u003C\u002Fh2>\n  \u003Cp>Body content that appears without flinging itself across the screen.\u003C\u002Fp>\n\u003C\u002Fsection>\n",[18,5937,5938,5954,5967,5980],{"__ignoreMap":286},[290,5939,5940,5942,5945,5947,5949,5952],{"class":163,"line":292},[290,5941,296],{"class":295},[290,5943,5944],{"class":299},"section",[290,5946,314],{"class":303},[290,5948,307],{"class":295},[290,5950,5951],{"class":310},"\"reveal\"",[290,5953,327],{"class":295},[290,5955,5956,5958,5960,5963,5965],{"class":163,"line":330},[290,5957,367],{"class":295},[290,5959,50],{"class":299},[290,5961,5962],{"class":295},">Section heading\u003C\u002F",[290,5964,50],{"class":299},[290,5966,327],{"class":295},[290,5968,5969,5971,5973,5976,5978],{"class":163,"line":337},[290,5970,367],{"class":295},[290,5972,14],{"class":299},[290,5974,5975],{"class":295},">Body content that appears without flinging itself across the screen.\u003C\u002F",[290,5977,14],{"class":299},[290,5979,327],{"class":295},[290,5981,5982,5984,5986],{"class":163,"line":364},[290,5983,431],{"class":295},[290,5985,5944],{"class":299},[290,5987,327],{"class":295},[281,5989,5991],{"className":438,"code":5990,"language":440,"meta":286,"style":286},".reveal {\n  \u002F* Safe defaults: fade with a barely-there rise (8px, not 80px). *\u002F\n  opacity: 0;\n  transform: translateY(8px);\n  animation: gentle-in 0.45s ease-out forwards;\n}\n\n@keyframes gentle-in {\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n\u002F* Reduce, do not necessarily remove: keep the fade, drop the movement.\n   Some users tolerate opacity changes that they cannot tolerate motion. *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .reveal {\n    transform: none;          \u002F* no translation at all *\u002F\n    animation: fade-only 0.3s ease-out forwards;\n  }\n}\n\n@keyframes fade-only {\n  from { opacity: 0; }\n  to   { opacity: 1; }\n}\n",[18,5992,5993,6000,6005,6015,6031,6051,6055,6059,6068,6075,6085,6099,6103,6107,6111,6116,6121,6127,6134,6148,6166,6170,6174,6178,6187,6202,6217],{"__ignoreMap":286},[290,5994,5995,5998],{"class":163,"line":292},[290,5996,5997],{"class":303},".reveal",[290,5999,450],{"class":295},[290,6001,6002],{"class":163,"line":330},[290,6003,6004],{"class":455},"  \u002F* Safe defaults: fade with a barely-there rise (8px, not 80px). *\u002F\n",[290,6006,6007,6009,6011,6013],{"class":163,"line":337},[290,6008,462],{"class":461},[290,6010,465],{"class":295},[290,6012,487],{"class":461},[290,6014,471],{"class":295},[290,6016,6017,6019,6021,6023,6025,6027,6029],{"class":163,"line":364},[290,6018,476],{"class":461},[290,6020,465],{"class":295},[290,6022,481],{"class":461},[290,6024,484],{"class":295},[290,6026,176],{"class":461},[290,6028,674],{"class":541},[290,6030,500],{"class":295},[290,6032,6033,6036,6039,6042,6044,6046,6049],{"class":163,"line":386},[290,6034,6035],{"class":461},"  animation",[290,6037,6038],{"class":295},": gentle-in ",[290,6040,6041],{"class":461},"0.45",[290,6043,1886],{"class":541},[290,6045,1889],{"class":461},[290,6047,6048],{"class":461}," forwards",[290,6050,471],{"class":295},[290,6052,6053],{"class":163,"line":408},[290,6054,620],{"class":295},[290,6056,6057],{"class":163,"line":428},[290,6058,334],{"emptyLinePlaceholder":333},[290,6060,6061,6063,6066],{"class":163,"line":517},[290,6062,3968],{"class":541},[290,6064,6065],{"class":1561}," gentle-in",[290,6067,450],{"class":295},[290,6069,6070,6073],{"class":163,"line":523},[290,6071,6072],{"class":303},"  to",[290,6074,450],{"class":295},[290,6076,6077,6079,6081,6083],{"class":163,"line":532},[290,6078,733],{"class":461},[290,6080,465],{"class":295},[290,6082,468],{"class":461},[290,6084,471],{"class":295},[290,6086,6087,6089,6091,6093,6095,6097],{"class":163,"line":551},[290,6088,745],{"class":461},[290,6090,465],{"class":295},[290,6092,481],{"class":461},[290,6094,484],{"class":295},[290,6096,487],{"class":461},[290,6098,500],{"class":295},[290,6100,6101],{"class":163,"line":586},[290,6102,771],{"class":295},[290,6104,6105],{"class":163,"line":602},[290,6106,620],{"class":295},[290,6108,6109],{"class":163,"line":617},[290,6110,334],{"emptyLinePlaceholder":333},[290,6112,6113],{"class":163,"line":623},[290,6114,6115],{"class":455},"\u002F* Reduce, do not necessarily remove: keep the fade, drop the movement.\n",[290,6117,6118],{"class":163,"line":628},[290,6119,6120],{"class":455},"   Some users tolerate opacity changes that they cannot tolerate motion. *\u002F\n",[290,6122,6123,6125],{"class":163,"line":634},[290,6124,874],{"class":541},[290,6126,877],{"class":295},[290,6128,6129,6132],{"class":163,"line":649},[290,6130,6131],{"class":303},"  .reveal",[290,6133,450],{"class":295},[290,6135,6136,6138,6140,6142,6145],{"class":163,"line":660},[290,6137,745],{"class":461},[290,6139,465],{"class":295},[290,6141,72],{"class":461},[290,6143,6144],{"class":295},";          ",[290,6146,6147],{"class":455},"\u002F* no translation at all *\u002F\n",[290,6149,6150,6153,6156,6158,6160,6162,6164],{"class":163,"line":688},[290,6151,6152],{"class":461},"    animation",[290,6154,6155],{"class":295},": fade-only ",[290,6157,199],{"class":461},[290,6159,1886],{"class":541},[290,6161,1889],{"class":461},[290,6163,6048],{"class":461},[290,6165,471],{"class":295},[290,6167,6168],{"class":163,"line":693},[290,6169,771],{"class":295},[290,6171,6172],{"class":163,"line":698},[290,6173,620],{"class":295},[290,6175,6176],{"class":163,"line":704},[290,6177,334],{"emptyLinePlaceholder":333},[290,6179,6180,6182,6185],{"class":163,"line":710},[290,6181,3968],{"class":541},[290,6183,6184],{"class":1561}," fade-only",[290,6186,450],{"class":295},[290,6188,6189,6192,6194,6196,6198,6200],{"class":163,"line":717},[290,6190,6191],{"class":303},"  from",[290,6193,790],{"class":295},[290,6195,76],{"class":461},[290,6197,465],{"class":295},[290,6199,487],{"class":461},[290,6201,809],{"class":295},[290,6203,6204,6206,6209,6211,6213,6215],{"class":163,"line":730},[290,6205,6072],{"class":303},[290,6207,6208],{"class":295},"   { ",[290,6210,76],{"class":461},[290,6212,465],{"class":295},[290,6214,468],{"class":461},[290,6216,809],{"class":295},[290,6218,6219],{"class":163,"line":742},[290,6220,620],{"class":295},[14,6222,6223],{},"Contrast that with what to avoid. The following is a textbook vestibular trigger and is shown only to be recognised and refused:",[281,6225,6227],{"className":438,"code":6226,"language":440,"meta":286,"style":286},"\u002F* DON'T: large parallax + zoom. Travels far and scales hard. *\u002F\n.hero__bg {\n  animation: parallax-zoom 1.2s ease-out;\n}\n@keyframes parallax-zoom {\n  from { transform: translateY(-200px) scale(1.6); }  \u002F* huge displacement *\u002F\n  to   { transform: translateY(0) scale(1); }\n}\n",[18,6228,6229,6234,6241,6256,6260,6269,6303,6329],{"__ignoreMap":286},[290,6230,6231],{"class":163,"line":292},[290,6232,6233],{"class":455},"\u002F* DON'T: large parallax + zoom. Travels far and scales hard. *\u002F\n",[290,6235,6236,6239],{"class":163,"line":330},[290,6237,6238],{"class":303},".hero__bg",[290,6240,450],{"class":295},[290,6242,6243,6245,6248,6250,6252,6254],{"class":163,"line":337},[290,6244,6035],{"class":461},[290,6246,6247],{"class":295},": parallax-zoom ",[290,6249,1789],{"class":461},[290,6251,1886],{"class":541},[290,6253,1889],{"class":461},[290,6255,471],{"class":295},[290,6257,6258],{"class":163,"line":364},[290,6259,620],{"class":295},[290,6261,6262,6264,6267],{"class":163,"line":386},[290,6263,3968],{"class":541},[290,6265,6266],{"class":1561}," parallax-zoom",[290,6268,450],{"class":295},[290,6270,6271,6273,6275,6277,6279,6281,6283,6286,6288,6290,6292,6294,6297,6300],{"class":163,"line":408},[290,6272,6191],{"class":303},[290,6274,790],{"class":295},[290,6276,103],{"class":461},[290,6278,465],{"class":295},[290,6280,481],{"class":461},[290,6282,484],{"class":295},[290,6284,6285],{"class":461},"-200",[290,6287,674],{"class":541},[290,6289,490],{"class":295},[290,6291,493],{"class":461},[290,6293,484],{"class":295},[290,6295,6296],{"class":461},"1.6",[290,6298,6299],{"class":295},"); }  ",[290,6301,6302],{"class":455},"\u002F* huge displacement *\u002F\n",[290,6304,6305,6307,6309,6311,6313,6315,6317,6319,6321,6323,6325,6327],{"class":163,"line":428},[290,6306,6072],{"class":303},[290,6308,6208],{"class":295},[290,6310,103],{"class":461},[290,6312,465],{"class":295},[290,6314,481],{"class":461},[290,6316,484],{"class":295},[290,6318,487],{"class":461},[290,6320,490],{"class":295},[290,6322,493],{"class":461},[290,6324,484],{"class":295},[290,6326,468],{"class":461},[290,6328,1122],{"class":295},[290,6330,6331],{"class":163,"line":517},[290,6332,620],{"class":295},[50,6334,4795],{"id":4794},[14,6336,6337,6338,6340,6341,6343,6344,6346,6347,6349,6350,6352],{},"The safe pattern leans on a property that the vestibular system does not read as motion: ",[18,6339,76],{},". A fade changes how present something is, not where it is, so it carries none of the self-motion signal that triggers symptoms. The optional 8px ",[18,6342,481],{}," is small enough to register as a settle rather than travel, and it is the first thing removed under a reduced-motion preference. The discipline is to treat ",[18,6345,103],{}," distance and ",[18,6348,493],{}," magnitude as a budget — keep travel in single-digit-to-low-double-digit pixels and scale within a couple of percent — and to reach for ",[18,6351,76],{}," whenever the goal is simply \"this is new.\"",[50,6354,6356],{"id":6355},"variation-cross-fading-between-states-without-movement","Variation: cross-fading between states without movement",[14,6358,6359],{},"When you must transition between two visual states (a tab panel, a theme swap), cross-fade rather than slide. No element crosses the viewport, so nothing implies motion.",[281,6361,6363],{"className":438,"code":6362,"language":440,"meta":286,"style":286},".panel { opacity: 0; transition: opacity 0.25s ease; }\n.panel[data-active=\"true\"] { opacity: 1; }\n\n@media (prefers-reduced-motion: reduce) {\n  .panel { transition: opacity 0.15s ease; } \u002F* shorten, keep the fade *\u002F\n}\n",[18,6364,6365,6393,6417,6421,6427,6450],{"__ignoreMap":286},[290,6366,6367,6370,6372,6374,6376,6378,6380,6382,6385,6387,6389,6391],{"class":163,"line":292},[290,6368,6369],{"class":303},".panel",[290,6371,790],{"class":295},[290,6373,76],{"class":461},[290,6375,465],{"class":295},[290,6377,487],{"class":461},[290,6379,828],{"class":295},[290,6381,887],{"class":461},[290,6383,6384],{"class":295},": opacity ",[290,6386,1668],{"class":461},[290,6388,1886],{"class":541},[290,6390,545],{"class":461},[290,6392,809],{"class":295},[290,6394,6395,6397,6399,6402,6404,6407,6409,6411,6413,6415],{"class":163,"line":330},[290,6396,6369],{"class":303},[290,6398,1140],{"class":295},[290,6400,6401],{"class":303},"data-active",[290,6403,307],{"class":541},[290,6405,6406],{"class":310},"\"true\"",[290,6408,1145],{"class":295},[290,6410,76],{"class":461},[290,6412,465],{"class":295},[290,6414,468],{"class":461},[290,6416,809],{"class":295},[290,6418,6419],{"class":163,"line":337},[290,6420,334],{"emptyLinePlaceholder":333},[290,6422,6423,6425],{"class":163,"line":364},[290,6424,874],{"class":541},[290,6426,877],{"class":295},[290,6428,6429,6432,6434,6436,6438,6440,6442,6444,6447],{"class":163,"line":386},[290,6430,6431],{"class":303},"  .panel",[290,6433,790],{"class":295},[290,6435,887],{"class":461},[290,6437,6384],{"class":295},[290,6439,1883],{"class":461},[290,6441,1886],{"class":541},[290,6443,545],{"class":461},[290,6445,6446],{"class":295},"; } ",[290,6448,6449],{"class":455},"\u002F* shorten, keep the fade *\u002F\n",[290,6451,6452],{"class":163,"line":408},[290,6453,620],{"class":295},[14,6455,6456],{},"If you genuinely need a directional hint (e.g. a carousel), keep the move tiny and pair it with a fade so the opacity does most of the perceptual work while the few pixels of travel only suggest direction.",[50,6458,4904],{"id":2247},[14,6460,6461,6463,6464,69,6466,6468,6469,6471,6472,6474],{},[18,6462,2584],{}," is supported in Chrome 74+, Edge 79+, Firefox 63+, and Safari 10.1+, so the reduction path is universally available on evergreen browsers. ",[18,6465,76],{},[18,6467,103],{}," animations have no relevant caveats. Because unsupporting browsers simply ignore the ",[18,6470,874],{}," block and render the (already gentle) default, no ",[18,6473,2086],{}," fallback is required.",[50,6476,1316],{"id":1315},[14,6478,6479,6482],{},[62,6480,6481],{},"What kinds of motion trigger vestibular symptoms?","\nLarge-scale movement across the screen is the main offender: parallax scrolling, big zoom or scale effects, spinning, and sudden directional shifts. These mimic real-world motion and can cause dizziness, nausea, or disorientation.",[14,6484,6485,6491],{},[62,6486,6487,6488,6490],{},"Is ",[18,6489,76],{}," safe for people with vestibular disorders?","\nGenerally yes. A fade changes how visible something is without moving it across the visual field, so it does not imply self-motion. Cross-fades and gentle color shifts are among the safest animated transitions.",[14,6493,6494,6500,6501,6503],{},[62,6495,6496,6497,6499],{},"Do I still need ",[18,6498,2584],{}," if my animations are small?","\nYes. Small and safe are not the same as no motion. Some users need motion removed entirely, including subtle fades, so always provide a ",[18,6502,2584],{}," path that reduces or stops animation.",[14,6505,6506,6509],{},[62,6507,6508],{},"How large is too large for a transform animation?","\nThere is no exact pixel limit, but movement that spans a large fraction of the viewport, scales an element well beyond its resting size, or rotates noticeably is risky. Prefer travel of a few pixels and scale changes within a few percent.",[50,6511,1391],{"id":1390},[1393,6513,6514,6518,6523,6528,6534],{},[1396,6515,6516,4981],{},[27,6517,1476],{"href":1475},[1396,6519,6520,6522],{},[27,6521,2511],{"href":1412}," — the syntax and cascade mechanics behind the reduction.",[1396,6524,6525,6527],{},[27,6526,2525],{"href":2524}," — copy-paste reductions for common effects.",[1396,6529,6530,6533],{},[27,6531,6532],{"href":3349},"Creating Accessible Focus Indicators"," — non-motion feedback that stays available when motion is off.",[1396,6535,6536,6538],{},[27,6537,5002],{"href":5001}," — scaling layout calmly without animated zoom.",[1430,6540,6541],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":286,"searchDepth":330,"depth":330,"links":6543},[6544,6545,6546,6547,6548,6549,6550],{"id":5857,"depth":330,"text":5858},{"id":4202,"depth":330,"text":4203},{"id":4794,"depth":330,"text":4795},{"id":6355,"depth":330,"text":6356},{"id":2247,"depth":330,"text":4904},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Design vestibular-disorder-safe CSS motion: avoid large parallax, zoom, and spin, prefer opacity and small moves, and gate everything with prefers-reduced-motion.",{"seoTitle":6553,"datePublished":1447,"dateModified":1447,"faq":6554},"Vestibular-Safe CSS Animation Patterns",[6555,6557,6560,6563],{"q":6481,"a":6556},"Large-scale movement across the screen is the main offender: parallax scrolling, big zoom or scale effects, spinning, and sudden directional shifts. These mimic real-world motion and can cause dizziness, nausea, or disorientation.",{"q":6558,"a":6559},"Is opacity safe for people with vestibular disorders?","Generally yes. A fade changes how visible something is without moving it across the visual field, so it does not imply self-motion. Cross-fades and gentle color shifts are among the safest animated transitions.",{"q":6561,"a":6562},"Do I still need prefers-reduced-motion if my animations are small?","Yes. Small and safe are not the same as no motion. Some users need motion removed entirely, including subtle fades, so always provide a prefers-reduced-motion path that reduces or stops animation.",{"q":6508,"a":6564},"There is no exact pixel limit, but movement that spans a large fraction of the viewport, scales an element well beyond its resting size, or rotates noticeably is risky. Prefer travel of a few pixels and scale changes within a few percent.","\u002Fcss-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fvestibular-safe-animation-patterns",{"title":5816,"description":6551},"css-only-micro-interactions-animations\u002Faccessibility-in-css-animations\u002Fvestibular-safe-animation-patterns\u002Findex","qPB3LvsrDOdNiftl0FrV1lJHaUFIZuwIYBytHBK67Xw",{"id":6570,"title":6571,"body":6572,"description":8792,"extension":1444,"meta":8793,"navigation":333,"path":8805,"seo":8806,"stem":8807,"__hash__":8808},"content\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Findex.md","CSS Animation vs the Web Animations API: A Decision Guide",{"type":7,"value":6573,"toc":8773},[6574,6577,6593,6598,6612,6742,6744,6748,6751,6794,6797,6799,6803,6835,6859,6862,6864,6868,6871,7079,7095,7097,7101,7105,7108,7236,7242,7248,7273,7388,7391,7395,7398,7524,7535,7539,7545,7675,7685,7687,7691,7701,7787,7854,8286,8296,8298,8302,8330,8353,8355,8359,8427,8429,8433,8526,8543,8545,8549,8661,8663,8665,8678,8698,8710,8729,8731,8733,8770],[10,6575,6571],{"id":6576},"css-animation-vs-the-web-animations-api-a-decision-guide",[14,6578,6579,6580,6583,6584,6586,6587,6589,6590,6592],{},"Choosing between declarative CSS and the imperative Web Animations API (WAAPI, exposed through ",[18,6581,6582],{},"element.animate()",") is one of the recurring decisions in motion work, and it sits at the centre of the broader ",[27,6585,1481],{"href":1480}," guide. Both run on the same underlying timing model defined by the Web Animations specification, so the question is rarely \"which is faster\" and almost always \"which gives me the control I need without paying for control I do not\". This guide maps the decision across the axes that actually matter in production: where the animated values come from, how sequences are coordinated, whether you must interrupt or reverse motion, and where each approach runs in the rendering pipeline. The foundations build directly on ",[27,6588,30],{"href":29}," and the declarative timeline mapping covered in ",[27,6591,1420],{"href":1419},"; WAAPI is best understood as the imperative counterpart to those declarative tools, not a replacement for them.",[14,6594,6595],{},[62,6596,6597],{},"What this guide establishes:",[1393,6599,6600,6603,6606,6609],{},[1396,6601,6602],{},"A repeatable rule for choosing declarative CSS vs imperative WAAPI",[1396,6604,6605],{},"How dynamic, runtime-computed values force an imperative approach",[1396,6607,6608],{},"Sequencing, cancellation, and reversal differences between the two",[1396,6610,6611],{},"Why both can be GPU-composited and what that means for \"performance\"",[133,6613,140,6616,140,6619,140,6622,140,6625,140,6628,140,6632,6635,6639,6643,6648,6651,6655,6658,6660,6663,6665,6667,6671,6674,6676,6678,6680,6683,6685,6688,6690,6693,6697,6699,6702,6704,140,6706,140,6709,140,6713,140,6716,140,6719,140,6723,140,6727,140,6731,140,6734,6738],{"viewBox":6614,"role":136,"ariaLabel":6615,"xmlns":138,"style":139},"0 0 720 380","Decision matrix comparing CSS animations and the Web Animations API across five capability axes",[142,6617,6618],{},"CSS vs Web Animations API decision matrix",[146,6620,6621],{},"A matrix scoring CSS animations against the Web Animations API across dynamic values, sequencing, runtime control, authoring cost, and compositing.",[150,6623,6624],{"x":152,"y":2598,"style":154},"Pick by capability, not by speed",[150,6626,6627],{"x":5397,"y":2630,"style":5883},"CSS",[150,6629,6631],{"x":6630,"y":2630,"style":5883},"470","WAAPI",[163,6633],{"x1":158,"y1":6634,"x2":5887,"y2":6634,"stroke":167,"strokeWidth":468,"opacity":169},"74",[150,6636,6638],{"x":153,"y":4180,"style":6637},"text-anchor:start;fill:currentColor;font:13px sans-serif","\nAuthor-time values\n",[171,6640],{"x":6641,"y":4196,"width":6642,"height":1785,"rx":249,"fill":177,"opacity":1813},"252","96",[150,6644,6647],{"x":5397,"y":6645,"style":6646},"106","text-anchor:middle;fill:currentColor;font:600 12px sans-serif","\nstrong\n",[171,6649],{"x":6650,"y":4196,"width":6642,"height":1785,"rx":249,"fill":177,"opacity":1883},"422",[150,6652,6654],{"x":6630,"y":6645,"style":6653},"text-anchor:middle;fill:currentColor;font:12px sans-serif","\nok\n",[150,6656,6657],{"x":153,"y":4162,"style":6637},"\nRuntime values\n",[171,6659],{"x":6641,"y":5123,"width":6642,"height":1785,"rx":249,"fill":72,"stroke":167,"strokeWidth":468,"opacity":169},[150,6661,6662],{"x":5397,"y":4150,"style":6653},"\nweak\n",[171,6664],{"x":6650,"y":5123,"width":6642,"height":1785,"rx":249,"fill":177,"opacity":1813},[150,6666,6647],{"x":6630,"y":4150,"style":6646},[150,6668,6670],{"x":153,"y":6669,"style":6637},"184","\nInterrupt \u002F reverse\n",[171,6672],{"x":6641,"y":6673,"width":6642,"height":1785,"rx":249,"fill":72,"stroke":167,"strokeWidth":468,"opacity":169},"172",[150,6675,6662],{"x":5397,"y":4184,"style":6653},[171,6677],{"x":6650,"y":6673,"width":6642,"height":1785,"rx":249,"fill":177,"opacity":1813},[150,6679,6647],{"x":6630,"y":4184,"style":6646},[150,6681,6682],{"x":153,"y":191,"style":6637},"\nAuthoring cost\n",[171,6684],{"x":6641,"y":4199,"width":6642,"height":1785,"rx":249,"fill":177,"opacity":1813},[150,6686,6687],{"x":5397,"y":5904,"style":6646},"\nlow\n",[171,6689],{"x":6650,"y":4199,"width":6642,"height":1785,"rx":249,"fill":72,"stroke":167,"strokeWidth":468,"opacity":169},[150,6691,6692],{"x":6630,"y":5904,"style":6653},"\nhigher\n",[150,6694,6696],{"x":153,"y":6695,"style":6637},"264","\nGPU compositing\n",[171,6698],{"x":6641,"y":6641,"width":6642,"height":1785,"rx":249,"fill":177,"opacity":1813},[150,6700,6701],{"x":5397,"y":220,"style":6646},"\nyes\n",[171,6703],{"x":6650,"y":6641,"width":6642,"height":1785,"rx":249,"fill":177,"opacity":1813},[150,6705,6701],{"x":6630,"y":220,"style":6646},[171,6707],{"x":2619,"y":4196,"width":6708,"height":2602,"rx":836,"fill":177,"opacity":3241},"136",[150,6710,6712],{"x":6711,"y":4158,"style":2606},"628","Rule",[150,6714,6715],{"x":6711,"y":4150,"style":6653},"Default to",[150,6717,6718],{"x":6711,"y":4166,"style":6653},"CSS.",[150,6720,6722],{"x":6711,"y":6721,"style":6653},"196","Reach for",[150,6724,6726],{"x":6711,"y":6725,"style":6653},"214","WAAPI when",[150,6728,6730],{"x":6711,"y":6729,"style":6653},"232","values or",[150,6732,6733],{"x":6711,"y":3112,"style":6653},"control move",[150,6735,6737],{"x":6711,"y":6736,"style":6653},"268","to runtime.",[150,6739,6741],{"x":152,"y":6740,"style":5915},"312","\nBoth share one timing model; the split is control, not throughput.\n",[47,6743],{},[50,6745,6747],{"id":6746},"prerequisites","Prerequisites",[14,6749,6750],{},"This guide assumes you are comfortable with the following before deciding between the two approaches:",[1393,6752,6753,6760,6765,6780,6783],{},[1396,6754,1517,6755,69,6757,6759],{},[18,6756,887],{},[18,6758,5178],{}," shorthands and their longhand properties.",[1396,6761,6762,6764],{},[18,6763,3968],{}," syntax and how keyframe offsets map to a timeline.",[1396,6766,6767,6768,569,6771,569,6774,6776,6777,42],{},"Timing functions (",[18,6769,6770],{},"ease",[18,6772,6773],{},"cubic-bezier()",[18,6775,3971],{},") and ",[18,6778,6779],{},"animation-fill-mode",[1396,6781,6782],{},"Basic DOM scripting: selecting an element and calling a method on it.",[1396,6784,6785,6786,569,6788,569,6790,6793],{},"Which properties composite (",[18,6787,103],{},[18,6789,76],{},[18,6791,6792],{},"filter",") versus which trigger layout.",[14,6795,6796],{},"If any of those feel shaky, work through the transitions and keyframes guides linked above first, because the WAAPI uses identical concepts expressed as JavaScript objects.",[47,6798],{},[50,6800,6802],{"id":6801},"core-concept-one-model-two-authoring-surfaces","Core concept: one model, two authoring surfaces",[14,6804,6805,6806,6808,6809,6812,6813,6815,6816,6818,6819,6822,6823,6826,6827,6830,6831,6834],{},"The single most important fact is that CSS animations, CSS transitions, and ",[18,6807,6582],{}," are all defined against the ",[62,6810,6811],{},"Web Animations model",". The Web Animations specification describes a timeline, an animation that maps a time to an effect, and a keyframe effect that interpolates property values. CSS ",[18,6814,3968],{}," and the ",[18,6817,5178],{}," properties are a declarative ",[86,6820,6821],{},"serialisation"," of that model; the WAAPI is an ",[86,6824,6825],{},"imperative API"," over the very same machinery. A CSS animation you wrote in a stylesheet appears as an ",[18,6828,6829],{},"Animation"," object in JavaScript — ",[18,6832,6833],{},"element.getAnimations()"," returns it, and you can pause or cancel it. They are not parallel systems competing for the engine; they are two doors into one room.",[14,6836,6837,6838,6841,6842,6844,6845,6847,6848,6851,6852,6815,6855,6858],{},"That has a direct consequence for how you decide. Because the engine is shared, ",[62,6839,6840],{},"neither approach is meaningfully faster than the other for the same effect",". A ",[18,6843,103],{}," animation declared in CSS and the identical animation created with ",[18,6846,6582],{}," are both eligible to run on the compositor thread, off the main thread, with no per-frame JavaScript. The genuine difference is ",[86,6849,6850],{},"who computes the values and who holds the controls",". CSS holds the values at author time in the stylesheet and hands control to the cascade and pseudo-classes. WAAPI lets JavaScript compute the values at runtime and keep a handle on the running animation. The decision is therefore about the ",[86,6853,6854],{},"source of values",[86,6856,6857],{},"need for control",", never about raw throughput.",[14,6860,6861],{},"The corollary: prefer the smallest tool that expresses the effect. If the keyframes are static and the trigger is a state, CSS wins on simplicity, resilience, and the fact that it keeps working when scripts fail to load. The moment a value can only be known at runtime, or you must steer the animation while it plays, the declarative surface stops being expressive enough and you cross over to the imperative one.",[47,6863],{},[50,6865,6867],{"id":6866},"syntax-and-parameters","Syntax and parameters",[14,6869,6870],{},"The two surfaces accept the same conceptual inputs. This table maps the CSS token to its WAAPI counterpart so you can read one as the other.",[2250,6872,6873,6889],{},[2253,6874,6875],{},[2256,6876,6877,6880,6883,6886],{},[2259,6878,6879],{},"Concept (CSS token)",[2259,6881,6882],{},"Accepted values",[2259,6884,6885],{},"Default",[2259,6887,6888],{},"WAAPI equivalent",[2269,6890,6891,6913,6946,6974,6994,7021,7051],{},[2256,6892,6893,6898,6901,6904],{},[2274,6894,6895],{},[18,6896,6897],{},"@keyframes name { … }",[2274,6899,6900],{},"offset blocks with property values",[2274,6902,6903],{},"—",[2274,6905,6906,6909,6910],{},[18,6907,6908],{},"keyframes"," array: ",[18,6911,6912],{},"[{ opacity: 0 }, { opacity: 1 }]",[2256,6914,6915,6919,6931,6936],{},[2274,6916,6917],{},[18,6918,4950],{},[2274,6920,6921,6924,6925,569,6928,3914],{},[18,6922,6923],{},"\u003Ctime>"," (e.g. ",[18,6926,6927],{},"0.4s",[18,6929,6930],{},"400ms",[2274,6932,6933],{},[18,6934,6935],{},"0s",[2274,6937,6938,6941,6942,6945],{},[18,6939,6940],{},"duration"," in ",[18,6943,6944],{},"options"," (ms)",[2256,6947,6948,6953,6964,6968],{},[2274,6949,6950],{},[18,6951,6952],{},"animation-timing-function",[2274,6954,6955,6957,6958,6957,6960,6957,6962],{},[18,6956,6770],{}," | ",[18,6959,5456],{},[18,6961,6773],{},[18,6963,3971],{},[2274,6965,6966],{},[18,6967,6770],{},[2274,6969,6970,6973],{},[18,6971,6972],{},"easing"," string",[2256,6975,6976,6981,6985,6989],{},[2274,6977,6978],{},[18,6979,6980],{},"animation-delay",[2274,6982,6983],{},[18,6984,6923],{},[2274,6986,6987],{},[18,6988,6935],{},[2274,6990,6991,6945],{},[18,6992,6993],{},"delay",[2256,6995,6996,7001,7009,7013],{},[2274,6997,6998],{},[18,6999,7000],{},"animation-iteration-count",[2274,7002,7003,6957,7006],{},[18,7004,7005],{},"\u003Cnumber>",[18,7007,7008],{},"infinite",[2274,7010,7011],{},[18,7012,468],{},[2274,7014,7015,3595,7018,3914],{},[18,7016,7017],{},"iterations",[18,7019,7020],{},"Infinity",[2256,7022,7023,7028,7042,7046],{},[2274,7024,7025],{},[18,7026,7027],{},"animation-direction",[2274,7029,7030,6957,7033,6957,7036,6957,7039],{},[18,7031,7032],{},"normal",[18,7034,7035],{},"reverse",[18,7037,7038],{},"alternate",[18,7040,7041],{},"alternate-reverse",[2274,7043,7044],{},[18,7045,7032],{},[2274,7047,7048],{},[18,7049,7050],{},"direction",[2256,7052,7053,7057,7070,7074],{},[2274,7054,7055],{},[18,7056,6779],{},[2274,7058,7059,6957,7061,6957,7064,6957,7067],{},[18,7060,72],{},[18,7062,7063],{},"forwards",[18,7065,7066],{},"backwards",[18,7068,7069],{},"both",[2274,7071,7072],{},[18,7073,72],{},[2274,7075,7076],{},[18,7077,7078],{},"fill",[14,7080,7081,7082,7085,7086,7088,7089,7091,7092,7094],{},"A ",[18,7083,7084],{},"cubic-bezier(0.2, 0.8, 0.2, 1)"," curve written in CSS is the literal string you pass as ",[18,7087,6972],{}," in WAAPI, and a duration of ",[18,7090,6927],{}," becomes ",[18,7093,2618],{},". Because the vocabularies line up, porting a tested CSS animation to JavaScript is mechanical, which is exactly why you should only do it when you need the imperative powers below.",[47,7096],{},[50,7098,7100],{"id":7099},"step-by-step-the-same-effect-both-ways","Step-by-step: the same effect, both ways",[2757,7102,7104],{"id":7103},"step-1-express-the-static-case-in-css","Step 1 — Express the static case in CSS",[14,7106,7107],{},"When the target value is known up front, CSS is the whole solution. A fade-and-rise on entry needs no script.",[281,7109,7111],{"className":438,"code":7110,"language":440,"meta":286,"style":286},".toast {\n  opacity: 0;\n  transform: translateY(8px);\n  animation: rise 0.32s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;\n}\n\n@keyframes rise {\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n",[18,7112,7113,7120,7130,7146,7181,7185,7189,7198,7204,7214,7228,7232],{"__ignoreMap":286},[290,7114,7115,7118],{"class":163,"line":292},[290,7116,7117],{"class":303},".toast",[290,7119,450],{"class":295},[290,7121,7122,7124,7126,7128],{"class":163,"line":330},[290,7123,462],{"class":461},[290,7125,465],{"class":295},[290,7127,487],{"class":461},[290,7129,471],{"class":295},[290,7131,7132,7134,7136,7138,7140,7142,7144],{"class":163,"line":337},[290,7133,476],{"class":461},[290,7135,465],{"class":295},[290,7137,481],{"class":461},[290,7139,484],{"class":295},[290,7141,176],{"class":461},[290,7143,674],{"class":541},[290,7145,500],{"class":295},[290,7147,7148,7150,7153,7155,7157,7159,7161,7163,7165,7167,7169,7171,7173,7175,7177,7179],{"class":163,"line":364},[290,7149,6035],{"class":461},[290,7151,7152],{"class":295},": rise ",[290,7154,5906],{"class":461},[290,7156,1886],{"class":541},[290,7158,561],{"class":461},[290,7160,484],{"class":295},[290,7162,566],{"class":461},[290,7164,569],{"class":295},[290,7166,572],{"class":461},[290,7168,569],{"class":295},[290,7170,566],{"class":461},[290,7172,569],{"class":295},[290,7174,468],{"class":461},[290,7176,490],{"class":295},[290,7178,7063],{"class":461},[290,7180,471],{"class":295},[290,7182,7183],{"class":163,"line":386},[290,7184,620],{"class":295},[290,7186,7187],{"class":163,"line":408},[290,7188,334],{"emptyLinePlaceholder":333},[290,7190,7191,7193,7196],{"class":163,"line":428},[290,7192,3968],{"class":541},[290,7194,7195],{"class":1561}," rise",[290,7197,450],{"class":295},[290,7199,7200,7202],{"class":163,"line":517},[290,7201,6072],{"class":303},[290,7203,450],{"class":295},[290,7205,7206,7208,7210,7212],{"class":163,"line":523},[290,7207,733],{"class":461},[290,7209,465],{"class":295},[290,7211,468],{"class":461},[290,7213,471],{"class":295},[290,7215,7216,7218,7220,7222,7224,7226],{"class":163,"line":532},[290,7217,745],{"class":461},[290,7219,465],{"class":295},[290,7221,481],{"class":461},[290,7223,484],{"class":295},[290,7225,487],{"class":461},[290,7227,500],{"class":295},[290,7229,7230],{"class":163,"line":551},[290,7231,771],{"class":295},[290,7233,7234],{"class":163,"line":586},[290,7235,620],{"class":295},[2757,7237,7239,7240],{"id":7238},"step-2-recreate-it-with-elementanimate","Step 2 — Recreate it with ",[18,7241,6582],{},[14,7243,7244,7245,7247],{},"The identical motion in WAAPI returns an ",[18,7246,6829],{}," object. Note the matching durations and easing.",[281,7249,7251],{"className":283,"code":7250,"language":285,"meta":286,"style":286},"\u003Cdiv id=\"toast\">Saved\u003C\u002Fdiv>\n",[18,7252,7253],{"__ignoreMap":286},[290,7254,7255,7257,7259,7261,7263,7266,7269,7271],{"class":163,"line":292},[290,7256,296],{"class":295},[290,7258,342],{"class":299},[290,7260,345],{"class":303},[290,7262,307],{"class":295},[290,7264,7265],{"class":310},"\"toast\"",[290,7267,7268],{"class":295},">Saved\u003C\u002F",[290,7270,342],{"class":299},[290,7272,327],{"class":295},[281,7274,7276],{"className":2904,"code":7275,"language":2906,"meta":286,"style":286},"const toast = document.getElementById(\"toast\");\n\nconst anim = toast.animate(\n  [\n    { opacity: 0, transform: \"translateY(8px)\" },\n    { opacity: 1, transform: \"translateY(0)\" },\n  ],\n  { duration: 320, easing: \"cubic-bezier(0.2, 0.8, 0.2, 1)\", fill: \"forwards\" }\n);\n",[18,7277,7278,7301,7305,7323,7328,7344,7357,7362,7384],{"__ignoreMap":286},[290,7279,7280,7283,7286,7289,7292,7295,7297,7299],{"class":163,"line":292},[290,7281,7282],{"class":541},"const",[290,7284,7285],{"class":461}," toast",[290,7287,7288],{"class":541}," =",[290,7290,7291],{"class":295}," document.",[290,7293,7294],{"class":303},"getElementById",[290,7296,484],{"class":295},[290,7298,7265],{"class":310},[290,7300,500],{"class":295},[290,7302,7303],{"class":163,"line":330},[290,7304,334],{"emptyLinePlaceholder":333},[290,7306,7307,7309,7312,7314,7317,7320],{"class":163,"line":337},[290,7308,7282],{"class":541},[290,7310,7311],{"class":461}," anim",[290,7313,7288],{"class":541},[290,7315,7316],{"class":295}," toast.",[290,7318,7319],{"class":303},"animate",[290,7321,7322],{"class":295},"(\n",[290,7324,7325],{"class":163,"line":364},[290,7326,7327],{"class":295},"  [\n",[290,7329,7330,7333,7335,7338,7341],{"class":163,"line":386},[290,7331,7332],{"class":295},"    { opacity: ",[290,7334,487],{"class":461},[290,7336,7337],{"class":295},", transform: ",[290,7339,7340],{"class":310},"\"translateY(8px)\"",[290,7342,7343],{"class":295}," },\n",[290,7345,7346,7348,7350,7352,7355],{"class":163,"line":408},[290,7347,7332],{"class":295},[290,7349,468],{"class":461},[290,7351,7337],{"class":295},[290,7353,7354],{"class":310},"\"translateY(0)\"",[290,7356,7343],{"class":295},[290,7358,7359],{"class":163,"line":428},[290,7360,7361],{"class":295},"  ],\n",[290,7363,7364,7367,7369,7372,7375,7378,7381],{"class":163,"line":517},[290,7365,7366],{"class":295},"  { duration: ",[290,7368,2613],{"class":461},[290,7370,7371],{"class":295},", easing: ",[290,7373,7374],{"class":310},"\"cubic-bezier(0.2, 0.8, 0.2, 1)\"",[290,7376,7377],{"class":295},", fill: ",[290,7379,7380],{"class":310},"\"forwards\"",[290,7382,7383],{"class":295}," }\n",[290,7385,7386],{"class":163,"line":523},[290,7387,500],{"class":295},[14,7389,7390],{},"At this point the two are equivalent and the CSS version is preferable: less code, no script dependency. WAAPI earns its place only when you add the next step.",[2757,7392,7394],{"id":7393},"step-3-inject-a-runtime-value-css-cannot-know","Step 3 — Inject a runtime value CSS cannot know",[14,7396,7397],{},"Suppose the toast must rise from wherever a button sits, a distance only measurable at runtime. CSS cannot read layout geometry; WAAPI can interpolate the measured number.",[281,7399,7401],{"className":2904,"code":7400,"language":2906,"meta":286,"style":286},"const button = document.querySelector(\".trigger\");\nconst start = button.getBoundingClientRect().top - toast.getBoundingClientRect().top;\n\ntoast.animate(\n  [\n    { opacity: 0, transform: `translateY(${start}px)` },\n    { opacity: 1, transform: \"translateY(0)\" },\n  ],\n  { duration: 320, easing: \"cubic-bezier(0.2, 0.8, 0.2, 1)\", fill: \"forwards\" }\n);\n",[18,7402,7403,7424,7452,7456,7465,7469,7488,7500,7504,7520],{"__ignoreMap":286},[290,7404,7405,7407,7410,7412,7414,7417,7419,7422],{"class":163,"line":292},[290,7406,7282],{"class":541},[290,7408,7409],{"class":461}," button",[290,7411,7288],{"class":541},[290,7413,7291],{"class":295},[290,7415,7416],{"class":303},"querySelector",[290,7418,484],{"class":295},[290,7420,7421],{"class":310},"\".trigger\"",[290,7423,500],{"class":295},[290,7425,7426,7428,7431,7433,7436,7439,7442,7445,7447,7449],{"class":163,"line":330},[290,7427,7282],{"class":541},[290,7429,7430],{"class":461}," start",[290,7432,7288],{"class":541},[290,7434,7435],{"class":295}," button.",[290,7437,7438],{"class":303},"getBoundingClientRect",[290,7440,7441],{"class":295},"().top ",[290,7443,7444],{"class":541},"-",[290,7446,7316],{"class":295},[290,7448,7438],{"class":303},[290,7450,7451],{"class":295},"().top;\n",[290,7453,7454],{"class":163,"line":337},[290,7455,334],{"emptyLinePlaceholder":333},[290,7457,7458,7461,7463],{"class":163,"line":364},[290,7459,7460],{"class":295},"toast.",[290,7462,7319],{"class":303},[290,7464,7322],{"class":295},[290,7466,7467],{"class":163,"line":386},[290,7468,7327],{"class":295},[290,7470,7471,7473,7475,7477,7480,7483,7486],{"class":163,"line":408},[290,7472,7332],{"class":295},[290,7474,487],{"class":461},[290,7476,7337],{"class":295},[290,7478,7479],{"class":310},"`translateY(${",[290,7481,7482],{"class":295},"start",[290,7484,7485],{"class":310},"}px)`",[290,7487,7343],{"class":295},[290,7489,7490,7492,7494,7496,7498],{"class":163,"line":428},[290,7491,7332],{"class":295},[290,7493,468],{"class":461},[290,7495,7337],{"class":295},[290,7497,7354],{"class":310},[290,7499,7343],{"class":295},[290,7501,7502],{"class":163,"line":517},[290,7503,7361],{"class":295},[290,7505,7506,7508,7510,7512,7514,7516,7518],{"class":163,"line":523},[290,7507,7366],{"class":295},[290,7509,2613],{"class":461},[290,7511,7371],{"class":295},[290,7513,7374],{"class":310},[290,7515,7377],{"class":295},[290,7517,7380],{"class":310},[290,7519,7383],{"class":295},[290,7521,7522],{"class":163,"line":532},[290,7523,500],{"class":295},[14,7525,7526,7527,7530,7531,7534],{},"This is the dividing line: the start offset is a measured number, so the declarative surface can no longer express it without manually writing inline styles per element. (CSS custom properties can carry a ",[86,7528,7529],{},"value you already have",", but they cannot run ",[18,7532,7533],{},"getBoundingClientRect()",".)",[2757,7536,7538],{"id":7537},"step-4-take-control-of-the-running-animation","Step 4 — Take control of the running animation",[14,7540,7541,7542,7544],{},"The second power CSS lacks is mid-flight control. With the ",[18,7543,6829],{}," handle you can pause, set time, reverse, and await completion.",[281,7546,7548],{"className":2904,"code":7547,"language":2906,"meta":286,"style":286},"const a = toast.animate(keyframes, { duration: 320, fill: \"forwards\" });\n\n\u002F\u002F Pause and scrub\na.pause();\na.currentTime = 160; \u002F\u002F jump to the midpoint\n\n\u002F\u002F Reverse from the current position without a jump\na.reverse();\n\n\u002F\u002F Cancel and remove all effect\na.cancel();\n\n\u002F\u002F React to completion (a real promise, not an event listener)\na.finished.then(() => toast.remove());\n",[18,7549,7550,7575,7579,7584,7595,7610,7614,7619,7627,7631,7636,7645,7649,7654],{"__ignoreMap":286},[290,7551,7552,7554,7557,7559,7561,7563,7566,7568,7570,7572],{"class":163,"line":292},[290,7553,7282],{"class":541},[290,7555,7556],{"class":461}," a",[290,7558,7288],{"class":541},[290,7560,7316],{"class":295},[290,7562,7319],{"class":303},[290,7564,7565],{"class":295},"(keyframes, { duration: ",[290,7567,2613],{"class":461},[290,7569,7377],{"class":295},[290,7571,7380],{"class":310},[290,7573,7574],{"class":295}," });\n",[290,7576,7577],{"class":163,"line":330},[290,7578,334],{"emptyLinePlaceholder":333},[290,7580,7581],{"class":163,"line":337},[290,7582,7583],{"class":455},"\u002F\u002F Pause and scrub\n",[290,7585,7586,7589,7592],{"class":163,"line":364},[290,7587,7588],{"class":295},"a.",[290,7590,7591],{"class":303},"pause",[290,7593,7594],{"class":295},"();\n",[290,7596,7597,7600,7602,7605,7607],{"class":163,"line":386},[290,7598,7599],{"class":295},"a.currentTime ",[290,7601,307],{"class":541},[290,7603,7604],{"class":461}," 160",[290,7606,828],{"class":295},[290,7608,7609],{"class":455},"\u002F\u002F jump to the midpoint\n",[290,7611,7612],{"class":163,"line":408},[290,7613,334],{"emptyLinePlaceholder":333},[290,7615,7616],{"class":163,"line":428},[290,7617,7618],{"class":455},"\u002F\u002F Reverse from the current position without a jump\n",[290,7620,7621,7623,7625],{"class":163,"line":517},[290,7622,7588],{"class":295},[290,7624,7035],{"class":303},[290,7626,7594],{"class":295},[290,7628,7629],{"class":163,"line":523},[290,7630,334],{"emptyLinePlaceholder":333},[290,7632,7633],{"class":163,"line":532},[290,7634,7635],{"class":455},"\u002F\u002F Cancel and remove all effect\n",[290,7637,7638,7640,7643],{"class":163,"line":551},[290,7639,7588],{"class":295},[290,7641,7642],{"class":303},"cancel",[290,7644,7594],{"class":295},[290,7646,7647],{"class":163,"line":586},[290,7648,334],{"emptyLinePlaceholder":333},[290,7650,7651],{"class":163,"line":602},[290,7652,7653],{"class":455},"\u002F\u002F React to completion (a real promise, not an event listener)\n",[290,7655,7656,7659,7662,7665,7667,7669,7672],{"class":163,"line":617},[290,7657,7658],{"class":295},"a.finished.",[290,7660,7661],{"class":303},"then",[290,7663,7664],{"class":295},"(() ",[290,7666,2988],{"class":541},[290,7668,7316],{"class":295},[290,7670,7671],{"class":303},"remove",[290,7673,7674],{"class":295},"());\n",[14,7676,7677,7678,7681,7682,7684],{},"A CSS animation toggled by class swaps cannot reverse from its current position; it restarts or snaps. The ",[18,7679,7680],{},"finished"," promise also replaces brittle ",[18,7683,2766],{}," listeners, which fire per-iteration and per-property and are easy to mis-handle.",[47,7686],{},[50,7688,7690],{"id":7689},"annotated-production-example-an-interruptible-accordion","Annotated production example: an interruptible accordion",[14,7692,7693,7694,7696,7697,7700],{},"A common real case that exposes the boundary is an accordion whose panels can be clicked again ",[86,7695,952],{}," the previous animation finishes. CSS height transitions cannot smoothly reverse mid-flight; WAAPI can, by reading the live ",[18,7698,7699],{},"currentTime",". The markup and resting styles stay declarative; only the interruption logic is imperative.",[281,7702,7704],{"className":283,"code":7703,"language":285,"meta":286,"style":286},"\u003Cbutton class=\"acc__head\" aria-expanded=\"false\" aria-controls=\"p1\">Details\u003C\u002Fbutton>\n\u003Cdiv class=\"acc__panel\" id=\"p1\" hidden>\n  \u003Cp>Panel content that animates open and closed.\u003C\u002Fp>\n\u003C\u002Fdiv>\n",[18,7705,7706,7742,7766,7779],{"__ignoreMap":286},[290,7707,7708,7710,7712,7714,7716,7719,7722,7724,7727,7730,7732,7735,7738,7740],{"class":163,"line":292},[290,7709,296],{"class":295},[290,7711,300],{"class":299},[290,7713,314],{"class":303},[290,7715,307],{"class":295},[290,7717,7718],{"class":310},"\"acc__head\"",[290,7720,7721],{"class":303}," aria-expanded",[290,7723,307],{"class":295},[290,7725,7726],{"class":310},"\"false\"",[290,7728,7729],{"class":303}," aria-controls",[290,7731,307],{"class":295},[290,7733,7734],{"class":310},"\"p1\"",[290,7736,7737],{"class":295},">Details\u003C\u002F",[290,7739,300],{"class":299},[290,7741,327],{"class":295},[290,7743,7744,7746,7748,7750,7752,7755,7757,7759,7761,7764],{"class":163,"line":330},[290,7745,296],{"class":295},[290,7747,342],{"class":299},[290,7749,314],{"class":303},[290,7751,307],{"class":295},[290,7753,7754],{"class":310},"\"acc__panel\"",[290,7756,345],{"class":303},[290,7758,307],{"class":295},[290,7760,7734],{"class":310},[290,7762,7763],{"class":303}," hidden",[290,7765,327],{"class":295},[290,7767,7768,7770,7772,7775,7777],{"class":163,"line":337},[290,7769,367],{"class":295},[290,7771,14],{"class":299},[290,7773,7774],{"class":295},">Panel content that animates open and closed.\u003C\u002F",[290,7776,14],{"class":299},[290,7778,327],{"class":295},[290,7780,7781,7783,7785],{"class":163,"line":364},[290,7782,431],{"class":295},[290,7784,342],{"class":299},[290,7786,327],{"class":295},[281,7788,7790],{"className":438,"code":7789,"language":440,"meta":286,"style":286},".acc__panel {\n  overflow: hidden;\n  \u002F* Resting presentation stays in CSS; JS only drives the height tween. *\u002F\n}\n\n@media (prefers-reduced-motion: reduce) {\n  \u002F* Honour the OS setting: the script below checks this too. *\u002F\n  .acc__panel { transition: none; }\n}\n",[18,7791,7792,7799,7811,7816,7820,7824,7830,7835,7850],{"__ignoreMap":286},[290,7793,7794,7797],{"class":163,"line":292},[290,7795,7796],{"class":303},".acc__panel",[290,7798,450],{"class":295},[290,7800,7801,7804,7806,7809],{"class":163,"line":330},[290,7802,7803],{"class":461},"  overflow",[290,7805,465],{"class":295},[290,7807,7808],{"class":461},"hidden",[290,7810,471],{"class":295},[290,7812,7813],{"class":163,"line":337},[290,7814,7815],{"class":455},"  \u002F* Resting presentation stays in CSS; JS only drives the height tween. *\u002F\n",[290,7817,7818],{"class":163,"line":364},[290,7819,620],{"class":295},[290,7821,7822],{"class":163,"line":386},[290,7823,334],{"emptyLinePlaceholder":333},[290,7825,7826,7828],{"class":163,"line":408},[290,7827,874],{"class":541},[290,7829,877],{"class":295},[290,7831,7832],{"class":163,"line":428},[290,7833,7834],{"class":455},"  \u002F* Honour the OS setting: the script below checks this too. *\u002F\n",[290,7836,7837,7840,7842,7844,7846,7848],{"class":163,"line":517},[290,7838,7839],{"class":303},"  .acc__panel",[290,7841,790],{"class":295},[290,7843,887],{"class":461},[290,7845,465],{"class":295},[290,7847,72],{"class":461},[290,7849,809],{"class":295},[290,7851,7852],{"class":163,"line":523},[290,7853,620],{"class":295},[281,7855,7857],{"className":2904,"code":7856,"language":2906,"meta":286,"style":286},"const head = document.querySelector(\".acc__head\");\nconst panel = document.getElementById(\"p1\");\nlet current = null; \u002F\u002F the in-flight Animation, if any\n\nconst reduce = matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\nfunction toggle() {\n  const open = head.getAttribute(\"aria-expanded\") === \"true\";\n  head.setAttribute(\"aria-expanded\", String(!open));\n\n  \u002F\u002F Measure the real content height at runtime — CSS cannot do this.\n  panel.hidden = false;\n  const full = panel.scrollHeight;\n  const from = open ? full : 0;\n  const to = open ? 0 : full;\n\n  \u002F\u002F If an animation is already running, start from where it is so the\n  \u002F\u002F motion never jumps — this is the capability CSS lacks.\n  if (current) current.cancel();\n\n  current = panel.animate(\n    [{ height: `${from}px` }, { height: `${to}px` }],\n    {\n      duration: reduce ? 0 : 280,\n      easing: \"cubic-bezier(0.2, 0.8, 0.2, 1)\",\n      fill: \"forwards\",\n    }\n  );\n\n  current.finished.then(() => {\n    if (to === 0) panel.hidden = true; \u002F\u002F remove from a11y tree when closed\n    current = null;\n  });\n}\n\nhead.addEventListener(\"click\", toggle);\n",[18,7858,7859,7879,7898,7916,7920,7939,7943,7954,7985,8010,8014,8019,8031,8043,8066,8087,8091,8096,8101,8113,8117,8131,8157,8162,8178,8187,8196,8201,8206,8210,8223,8248,8259,8263,8267,8271],{"__ignoreMap":286},[290,7860,7861,7863,7866,7868,7870,7872,7874,7877],{"class":163,"line":292},[290,7862,7282],{"class":541},[290,7864,7865],{"class":461}," head",[290,7867,7288],{"class":541},[290,7869,7291],{"class":295},[290,7871,7416],{"class":303},[290,7873,484],{"class":295},[290,7875,7876],{"class":310},"\".acc__head\"",[290,7878,500],{"class":295},[290,7880,7881,7883,7886,7888,7890,7892,7894,7896],{"class":163,"line":330},[290,7882,7282],{"class":541},[290,7884,7885],{"class":461}," panel",[290,7887,7288],{"class":541},[290,7889,7291],{"class":295},[290,7891,7294],{"class":303},[290,7893,484],{"class":295},[290,7895,7734],{"class":310},[290,7897,500],{"class":295},[290,7899,7900,7903,7906,7908,7911,7913],{"class":163,"line":337},[290,7901,7902],{"class":541},"let",[290,7904,7905],{"class":295}," current ",[290,7907,307],{"class":541},[290,7909,7910],{"class":461}," null",[290,7912,828],{"class":295},[290,7914,7915],{"class":455},"\u002F\u002F the in-flight Animation, if any\n",[290,7917,7918],{"class":163,"line":364},[290,7919,334],{"emptyLinePlaceholder":333},[290,7921,7922,7924,7927,7929,7932,7934,7936],{"class":163,"line":386},[290,7923,7282],{"class":541},[290,7925,7926],{"class":461}," reduce",[290,7928,7288],{"class":541},[290,7930,7931],{"class":303}," matchMedia",[290,7933,484],{"class":295},[290,7935,2924],{"class":310},[290,7937,7938],{"class":295},").matches;\n",[290,7940,7941],{"class":163,"line":408},[290,7942,334],{"emptyLinePlaceholder":333},[290,7944,7945,7948,7951],{"class":163,"line":428},[290,7946,7947],{"class":541},"function",[290,7949,7950],{"class":303}," toggle",[290,7952,7953],{"class":295},"() {\n",[290,7955,7956,7959,7962,7964,7967,7970,7972,7975,7977,7980,7983],{"class":163,"line":517},[290,7957,7958],{"class":541},"  const",[290,7960,7961],{"class":461}," open",[290,7963,7288],{"class":541},[290,7965,7966],{"class":295}," head.",[290,7968,7969],{"class":303},"getAttribute",[290,7971,484],{"class":295},[290,7973,7974],{"class":310},"\"aria-expanded\"",[290,7976,490],{"class":295},[290,7978,7979],{"class":541},"===",[290,7981,7982],{"class":310}," \"true\"",[290,7984,471],{"class":295},[290,7986,7987,7990,7993,7995,7997,7999,8002,8004,8007],{"class":163,"line":523},[290,7988,7989],{"class":295},"  head.",[290,7991,7992],{"class":303},"setAttribute",[290,7994,484],{"class":295},[290,7996,7974],{"class":310},[290,7998,569],{"class":295},[290,8000,8001],{"class":303},"String",[290,8003,484],{"class":295},[290,8005,8006],{"class":541},"!",[290,8008,8009],{"class":295},"open));\n",[290,8011,8012],{"class":163,"line":532},[290,8013,334],{"emptyLinePlaceholder":333},[290,8015,8016],{"class":163,"line":551},[290,8017,8018],{"class":455},"  \u002F\u002F Measure the real content height at runtime — CSS cannot do this.\n",[290,8020,8021,8024,8026,8029],{"class":163,"line":586},[290,8022,8023],{"class":295},"  panel.hidden ",[290,8025,307],{"class":541},[290,8027,8028],{"class":461}," false",[290,8030,471],{"class":295},[290,8032,8033,8035,8038,8040],{"class":163,"line":602},[290,8034,7958],{"class":541},[290,8036,8037],{"class":461}," full",[290,8039,7288],{"class":541},[290,8041,8042],{"class":295}," panel.scrollHeight;\n",[290,8044,8045,8047,8050,8052,8055,8057,8060,8062,8064],{"class":163,"line":617},[290,8046,7958],{"class":541},[290,8048,8049],{"class":461}," from",[290,8051,7288],{"class":541},[290,8053,8054],{"class":295}," open ",[290,8056,5734],{"class":541},[290,8058,8059],{"class":295}," full ",[290,8061,723],{"class":541},[290,8063,1198],{"class":461},[290,8065,471],{"class":295},[290,8067,8068,8070,8073,8075,8077,8079,8081,8084],{"class":163,"line":623},[290,8069,7958],{"class":541},[290,8071,8072],{"class":461}," to",[290,8074,7288],{"class":541},[290,8076,8054],{"class":295},[290,8078,5734],{"class":541},[290,8080,1198],{"class":461},[290,8082,8083],{"class":541}," :",[290,8085,8086],{"class":295}," full;\n",[290,8088,8089],{"class":163,"line":628},[290,8090,334],{"emptyLinePlaceholder":333},[290,8092,8093],{"class":163,"line":634},[290,8094,8095],{"class":455},"  \u002F\u002F If an animation is already running, start from where it is so the\n",[290,8097,8098],{"class":163,"line":649},[290,8099,8100],{"class":455},"  \u002F\u002F motion never jumps — this is the capability CSS lacks.\n",[290,8102,8103,8106,8109,8111],{"class":163,"line":660},[290,8104,8105],{"class":541},"  if",[290,8107,8108],{"class":295}," (current) current.",[290,8110,7642],{"class":303},[290,8112,7594],{"class":295},[290,8114,8115],{"class":163,"line":688},[290,8116,334],{"emptyLinePlaceholder":333},[290,8118,8119,8122,8124,8127,8129],{"class":163,"line":693},[290,8120,8121],{"class":295},"  current ",[290,8123,307],{"class":541},[290,8125,8126],{"class":295}," panel.",[290,8128,7319],{"class":303},[290,8130,7322],{"class":295},[290,8132,8133,8136,8139,8141,8144,8147,8149,8152,8154],{"class":163,"line":698},[290,8134,8135],{"class":295},"    [{ height: ",[290,8137,8138],{"class":310},"`${",[290,8140,88],{"class":295},[290,8142,8143],{"class":310},"}px`",[290,8145,8146],{"class":295}," }, { height: ",[290,8148,8138],{"class":310},[290,8150,8151],{"class":295},"to",[290,8153,8143],{"class":310},[290,8155,8156],{"class":295}," }],\n",[290,8158,8159],{"class":163,"line":704},[290,8160,8161],{"class":295},"    {\n",[290,8163,8164,8167,8169,8171,8173,8176],{"class":163,"line":710},[290,8165,8166],{"class":295},"      duration: reduce ",[290,8168,5734],{"class":541},[290,8170,1198],{"class":461},[290,8172,8083],{"class":541},[290,8174,8175],{"class":461}," 280",[290,8177,548],{"class":295},[290,8179,8180,8183,8185],{"class":163,"line":717},[290,8181,8182],{"class":295},"      easing: ",[290,8184,7374],{"class":310},[290,8186,548],{"class":295},[290,8188,8189,8192,8194],{"class":163,"line":730},[290,8190,8191],{"class":295},"      fill: ",[290,8193,7380],{"class":310},[290,8195,548],{"class":295},[290,8197,8198],{"class":163,"line":742},[290,8199,8200],{"class":295},"    }\n",[290,8202,8203],{"class":163,"line":768},[290,8204,8205],{"class":295},"  );\n",[290,8207,8208],{"class":163,"line":774},[290,8209,334],{"emptyLinePlaceholder":333},[290,8211,8212,8215,8217,8219,8221],{"class":163,"line":779},[290,8213,8214],{"class":295},"  current.finished.",[290,8216,7661],{"class":303},[290,8218,7664],{"class":295},[290,8220,2988],{"class":541},[290,8222,450],{"class":295},[290,8224,8225,8228,8231,8233,8235,8238,8240,8243,8245],{"class":163,"line":784},[290,8226,8227],{"class":541},"    if",[290,8229,8230],{"class":295}," (to ",[290,8232,7979],{"class":541},[290,8234,1198],{"class":461},[290,8236,8237],{"class":295},") panel.hidden ",[290,8239,307],{"class":541},[290,8241,8242],{"class":461}," true",[290,8244,828],{"class":295},[290,8246,8247],{"class":455},"\u002F\u002F remove from a11y tree when closed\n",[290,8249,8250,8253,8255,8257],{"class":163,"line":812},[290,8251,8252],{"class":295},"    current ",[290,8254,307],{"class":541},[290,8256,7910],{"class":461},[290,8258,471],{"class":295},[290,8260,8261],{"class":163,"line":860},[290,8262,3010],{"class":295},[290,8264,8265],{"class":163,"line":865},[290,8266,620],{"class":295},[290,8268,8269],{"class":163,"line":871},[290,8270,334],{"emptyLinePlaceholder":333},[290,8272,8273,8276,8278,8280,8283],{"class":163,"line":880},[290,8274,8275],{"class":295},"head.",[290,8277,2972],{"class":303},[290,8279,484],{"class":295},[290,8281,8282],{"class":310},"\"click\"",[290,8284,8285],{"class":295},", toggle);\n",[14,8287,8288,8289,8292,8293,8295],{},"Everything that ",[86,8290,8291],{},"can"," stay in CSS — focus rings, colours, spacing, the reduced-motion guard — does. The script is confined to the one thing CSS cannot express: re-targeting an animation from its live position. That separation keeps the component resilient if the script fails: the panel still opens because ",[18,8294,7808],{}," is toggled and the resting styles render.",[47,8297],{},[50,8299,8301],{"id":8300},"performance-and-accessibility-notes","Performance and accessibility notes",[14,8303,8304,8305,8308,8309,8311,8312,2351,8314,8316,8317,8319,8320,8322,8323,8326,8327,8329],{},"On performance, the headline is that ",[62,8306,8307],{},"WAAPI is not a performance technique",". An ",[18,8310,6582],{}," call that animates ",[18,8313,103],{},[18,8315,76],{}," is composited on the same thread as the equivalent CSS animation, so both can hit 60fps without touching the main thread between frames. Animating ",[18,8318,2722],{},", as the accordion does, is a layout-triggering property in either approach and is the genuine cost there — the WAAPI does not make ",[18,8321,2722],{}," cheap; it only makes the ",[86,8324,8325],{},"interruption"," possible. If you find yourself reaching for WAAPI hoping for speed, you have the wrong motivation; reach for it for control. Keep ",[18,8328,2075],{}," scoped to active states and drop it afterwards, exactly as you would for CSS, since the rules for compositor promotion are identical.",[14,8331,8332,8333,8335,8336,8338,8339,8342,8343,8345,8346,8349,8350,8352],{},"On accessibility, both surfaces must honour ",[18,8334,2584],{},". In CSS you guard with ",[18,8337,4126],{},"; in WAAPI you read ",[18,8340,8341],{},"matchMedia(\"(prefers-reduced-motion: reduce)\").matches"," and collapse the duration to ",[18,8344,487],{},", as the example does. A subtle WAAPI advantage is that you can ",[86,8347,8348],{},"listen"," to the media query changing and re-plan in-flight animations, whereas CSS re-evaluates only on the next state change. Either way, the WCAG 2.3.3 guidance applies: motion triggered by interaction should be suppressible, and large translation or parallax should be replaced with a simple opacity change under reduced-motion preference. When you script motion, also restore the accessibility tree state (the accordion sets ",[18,8351,7808],{}," on close) so screen readers are not left announcing a visually collapsed panel.",[47,8354],{},[50,8356,8358],{"id":8357},"devtools-debugging-workflow","DevTools debugging workflow",[3017,8360,8361,8373,8406,8418],{},[1396,8362,8363,8366,8367,8369,8370,8372],{},[62,8364,8365],{},"Animations panel"," (Chrome\u002FEdge DevTools → More tools → Animations): record an interaction. Both CSS and WAAPI animations appear on the same timeline with draggable scrubbers. This is the fastest way to confirm a WAAPI animation registered at all — if nothing shows up, your ",[18,8368,6908],{}," array or ",[18,8371,6940],{}," is malformed.",[1396,8374,8375,8380,8381,8384,8385,8387,8388,569,8391,8393,8394,8397,8398,8401,8402,8405],{},[62,8376,8377,8379],{},[18,8378,6833],{}," in the Console",": select the node, run ",[18,8382,8383],{},"$0.getAnimations()",", and inspect each ",[18,8386,6829],{},"'s ",[18,8389,8390],{},"playState",[18,8392,7699],{},", and ",[18,8395,8396],{},"effect.getTiming()",". This reveals leftover animations that were never ",[18,8399,8400],{},"cancel()","-ed and are silently holding ",[18,8403,8404],{},"fill: forwards"," styles.",[1396,8407,8408,8411,8412,8414,8415,8417],{},[62,8409,8410],{},"Performance panel",": record a trace and look for purple \"Layout\" \u002F green \"Paint\" bars during the animation. Their presence means a non-composited property (like the accordion's ",[18,8413,2722],{},") is animating — expected there, a bug if you intended a ",[18,8416,103],{},"-only effect.",[1396,8419,8420,8423,8424,8426],{},[62,8421,8422],{},"Layers \u002F paint flashing",": toggle layer borders to confirm the animated element is promoted to its own compositor layer. The result is identical whether the animation came from CSS or ",[18,8425,6582],{},", which is the visual proof that \"WAAPI is faster\" is a myth.",[47,8428],{},[50,8430,8432],{"id":8431},"browser-compatibility","Browser compatibility",[2250,8434,8435,8448],{},[2253,8436,8437],{},[2256,8438,8439,8441,8444,8446],{},[2259,8440,3737],{},[2259,8442,8443],{},"Chrome \u002F Edge",[2259,8445,2297],{},[2259,8447,2287],{},[2269,8449,8450,8466,8482,8496,8510],{},[2256,8451,8452,8457,8460,8463],{},[2274,8453,8454,8455],{},"CSS animations \u002F ",[18,8456,3968],{},[2274,8458,8459],{},"43+",[2274,8461,8462],{},"9+",[2274,8464,8465],{},"16+",[2256,8467,8468,8473,8476,8479],{},[2274,8469,8470,8472],{},[18,8471,6582],{}," (core WAAPI)",[2274,8474,8475],{},"84+",[2274,8477,8478],{},"13.1+",[2274,8480,8481],{},"75+",[2256,8483,8484,8490,8492,8494],{},[2274,8485,8486,8489],{},[18,8487,8488],{},"Animation.finished"," promise",[2274,8491,8475],{},[2274,8493,8478],{},[2274,8495,8481],{},[2256,8497,8498,8503,8505,8508],{},[2274,8499,8500],{},[18,8501,8502],{},"getAnimations()",[2274,8504,8475],{},[2274,8506,8507],{},"14+",[2274,8509,8481],{},[2256,8511,8512,8520,8522,8524],{},[2274,8513,8514,8515,3041,8517,8519],{},"Composited ",[18,8516,103],{},[18,8518,76],{}," via WAAPI",[2274,8521,8475],{},[2274,8523,8478],{},[2274,8525,8481],{},[14,8527,8528,8529,569,8531,569,8533,8535,8536,8538,8539,8542],{},"Core WAAPI (",[18,8530,6582],{},[18,8532,8488],{},[18,8534,8502],{},") has been broadly interoperable across Chrome\u002FEdge 84+, Safari 13.1+ (14+ for ",[18,8537,8502],{},"), and Firefox 75+ since roughly 2020, so it is safe to use without polyfills in evergreen targets. Newer specialised pieces such as ",[18,8540,8541],{},"ScrollTimeline"," lag this baseline and need feature detection; treat them separately from the stable core API.",[47,8544],{},[50,8546,8548],{"id":8547},"common-pitfalls","Common pitfalls",[2250,8550,8551,8564],{},[2253,8552,8553],{},[2256,8554,8555,8558,8561],{},[2259,8556,8557],{},"Pitfall",[2259,8559,8560],{},"Cause",[2259,8562,8563],{},"Resolution",[2269,8565,8566,8591,8602,8621,8640],{},[2256,8567,8568,8571,8577],{},[2274,8569,8570],{},"WAAPI styles \"stick\" after the animation",[2274,8572,8573,8576],{},[18,8574,8575],{},"fill: \"forwards\""," leaves the animation holding final values indefinitely",[2274,8578,8579,8580,8583,8584,8587,8588,42],{},"Call ",[18,8581,8582],{},"anim.commitStyles()"," then ",[18,8585,8586],{},"anim.cancel()",", or set the resting value in CSS and use ",[18,8589,8590],{},"fill: \"none\"",[2256,8592,8593,8596,8599],{},[2274,8594,8595],{},"Reaching for WAAPI expecting speed",[2274,8597,8598],{},"Belief that JS animation is faster than CSS",[2274,8600,8601],{},"Both share the compositor; choose WAAPI for control over dynamic values, not throughput.",[2256,8603,8604,8607,8610],{},[2274,8605,8606],{},"Class-toggle reversal jumps",[2274,8608,8609],{},"CSS animations restart instead of reversing from the live position",[2274,8611,1499,8612,8614,8615,8618,8619,42],{},[18,8613,6582],{}," and call ",[18,8616,8617],{},"reverse()",", which retimes from ",[18,8620,7699],{},[2256,8622,8623,8626,8632],{},[2274,8624,8625],{},"Orphaned animations leak styles",[2274,8627,8628,8629,8631],{},"Created animations never ",[18,8630,8400],{},"-ed on re-trigger",[2274,8633,8634,8635,69,8637,8639],{},"Track the active ",[18,8636,6829],{},[18,8638,8400],{}," it before starting a new one.",[2256,8641,8642,8645,8650],{},[2274,8643,8644],{},"Reduced-motion ignored in JS path",[2274,8646,8647,8648],{},"Only the CSS path guarded ",[18,8649,2584],{},[2274,8651,8652,8653,8656,8657,80,8659,42],{},"Read ",[18,8654,8655],{},"matchMedia(...).matches"," in script and collapse ",[18,8658,6940],{},[18,8660,487],{},[47,8662],{},[50,8664,1316],{"id":1315},[14,8666,8667,8670,8671,569,8673,1745,8675,8677],{},[62,8668,8669],{},"Is the Web Animations API faster than CSS animations?","\nNo. Both run on the same animation engine and can be composited off the main thread when they animate ",[18,8672,103],{},[18,8674,76],{},[18,8676,6792],{},". WAAPI is not inherently faster; it just gives you imperative control. Choose it for dynamic values and runtime control, not for speed.",[14,8679,8680,8683,8684,569,8687,569,8689,8691,8692,8694,8695,8697],{},[62,8681,8682],{},"Can I cancel or reverse a CSS animation mid-flight?","\nNot cleanly with CSS alone. CSS animations restart or jump when you toggle classes. The Web Animations API exposes ",[18,8685,8686],{},"pause()",[18,8688,8617],{},[18,8690,8400],{},", and a settable ",[18,8693,7699],{},", so use ",[18,8696,6582],{}," when you need to interrupt motion smoothly.",[14,8699,8700,8703,8704,2351,8706,8709],{},[62,8701,8702],{},"When should I keep an animation CSS-only?","\nKeep it CSS-only when the values are known at author time, the trigger is a state or pseudo-class such as ",[18,8705,3689],{},[18,8707,8708],{},":checked",", and you do not need to interrupt, reverse, or measure progress. This covers most hover effects, loaders, and state transitions.",[14,8711,8712,8717,8718,8720,8721,8723,8724,8726,8727,42],{},[62,8713,8714,8715,5734],{},"Does the Web Animations API replace ",[18,8716,3968],{},"\nNo. ",[18,8719,6582],{}," accepts the same keyframe and timing concepts and produces ",[18,8722,6829],{}," objects you can control in JavaScript, but ",[18,8725,3968],{}," remains the declarative authoring format. They share one model, so you can mix them and read CSS animations back via ",[18,8728,8502],{},[47,8730],{},[50,8732,1391],{"id":1390},[1393,8734,8735,8742,8747,8752,8759,8765],{},[1396,8736,8737,8741],{},[27,8738,8740],{"href":8739},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Fwhen-to-use-javascript-animation\u002F","When to Use JavaScript for Animation"," — the concrete cases that force an imperative approach.",[1396,8743,8744,8746],{},[27,8745,30],{"href":29}," — the declarative baseline this guide compares against.",[1396,8748,8749,8751],{},[27,8750,1420],{"href":1419}," — declarative timeline mapping that WAAPI mirrors imperatively.",[1396,8753,8754,8758],{},[27,8755,8757],{"href":8756},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Foptimizing-css-animations-for-60fps\u002F","Optimizing CSS Animations for 60fps"," — why compositing, not API choice, determines smoothness.",[1396,8760,8761,8764],{},[27,8762,8763],{"href":4036},"How to Use Container Queries in Production"," — pairing responsive component sizing with state-driven motion.",[1396,8766,8767,8769],{},[27,8768,1481],{"href":1480}," — the parent guide tying these techniques together.",[1430,8771,8772],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":286,"searchDepth":330,"depth":330,"links":8774},[8775,8776,8777,8778,8785,8786,8787,8788,8789,8790,8791],{"id":6746,"depth":330,"text":6747},{"id":6801,"depth":330,"text":6802},{"id":6866,"depth":330,"text":6867},{"id":7099,"depth":330,"text":7100,"children":8779},[8780,8781,8783,8784],{"id":7103,"depth":337,"text":7104},{"id":7238,"depth":337,"text":8782},"Step 2 — Recreate it with element.animate()",{"id":7393,"depth":337,"text":7394},{"id":7537,"depth":337,"text":7538},{"id":7689,"depth":330,"text":7690},{"id":8300,"depth":330,"text":8301},{"id":8357,"depth":330,"text":8358},{"id":8431,"depth":330,"text":8432},{"id":8547,"depth":330,"text":8548},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Decide between CSS animations and the Web Animations API: declarative vs imperative control, dynamic values, sequencing, cancellation, and performance trade-offs.",{"seoTitle":8794,"datePublished":1447,"dateModified":1447,"faq":8795},"CSS Animation vs Web Animations API",[8796,8798,8800,8802],{"q":8669,"a":8797},"No. Both run on the same animation engine and can be composited off the main thread when they animate transform, opacity, or filter. WAAPI is not inherently faster; it just gives you imperative control. Choose it for dynamic values and runtime control, not for speed.",{"q":8682,"a":8799},"Not cleanly with CSS alone. CSS animations restart or jump when you toggle classes. The Web Animations API exposes pause(), reverse(), cancel(), and a settable currentTime, so use element.animate() when you need to interrupt motion smoothly.",{"q":8702,"a":8801},"Keep it CSS-only when the values are known at author time, the trigger is a state or pseudo-class such as :hover or :checked, and you do not need to interrupt, reverse, or measure progress. This covers most hover effects, loaders, and state transitions.",{"q":8803,"a":8804},"Does the Web Animations API replace @keyframes?","No. element.animate() accepts the same keyframe and timing concepts and produces Animation objects you can control in JavaScript, but @keyframes remains the declarative authoring format. They share one model, so you can mix them and read CSS animations back via getAnimations().","\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api",{"title":6571,"description":8792},"css-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Findex","P909HbxTdiodXq78VgaCLq6pCu3NXNJtumUyW7s4W64",{"id":8810,"title":8811,"body":8812,"description":10119,"extension":1444,"meta":10120,"navigation":333,"path":10130,"seo":10131,"stem":10132,"__hash__":10133},"content\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Fwhen-to-use-javascript-animation\u002Findex.md","When to Use JavaScript Instead of CSS for Animation",{"type":7,"value":8813,"toc":10110},[8814,8817,8828,8835,8837,8841,8858,8864,8952,8954,8958,8973,9836,9838,9842,9880,9882,9886,9919,9927,9994,10000,10002,10004,10028,10030,10032,10041,10047,10061,10073,10075,10077,10107],[10,8815,8811],{"id":8816},"when-to-use-javascript-instead-of-css-for-animation",[14,8818,8819,8820,8824,8825],{},"CSS handles the overwhelming majority of interface motion, but a handful of scenarios genuinely require JavaScript or the Web Animations API. This page is the practical companion to ",[27,8821,8823],{"href":8822},"\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002F","CSS Animation vs the Web Animations API",": rather than comparing the two surfaces in the abstract, it walks the concrete cases where the declarative approach runs out of expressiveness — and, just as importantly, the CSS-first alternative to try first in each one. The narrow question is: ",[86,8826,8827],{},"given a specific effect, can CSS do it, and if not, exactly which capability is missing?",[14,8829,8830,8831,8834],{},"The four cases that recur in production are layout transitions where positions are only known at runtime (FLIP), scroll-driven effects beyond what ",[18,8832,8833],{},"animation-timeline"," can express, physics-based motion with velocity, and interrupting or reversing an animation from its live position. Everything else — hover feedback, loaders, state changes, entrance effects — should stay declarative for resilience and lower cost.",[47,8836],{},[50,8838,8840],{"id":8839},"approach-rationale-prefer-css-escalate-deliberately","Approach rationale: prefer CSS, escalate deliberately",[14,8842,8843,8844,8846,8847,8850,8851,8853,8854,8857],{},"The default is CSS because declarative motion keeps working when scripts fail, costs no per-frame JavaScript, and is trivially guarded by ",[18,8845,2584],{},". You only escalate to JavaScript when a value or a control cannot be expressed in a stylesheet. There are exactly two triggers: the animated value is ",[86,8848,8849],{},"computed at runtime"," (geometry, velocity, scroll math that ",[18,8852,8833],{}," cannot map), or you must ",[86,8855,8856],{},"steer the animation while it runs"," (interrupt, reverse from the current position, scrub). If neither trigger is present, JavaScript adds fragility for no gain.",[14,8859,8860,8861,8863],{},"Accessibility is part of the rationale, not an afterthought. Scripted motion must read ",[18,8862,2584],{}," and collapse or skip the animation, and FLIP transitions in particular should degrade to an instant state change because animating a moving element across the viewport is exactly the kind of large motion the preference is meant to suppress. The flowchart below is the decision in one glance.",[133,8865,140,8868,140,8871,140,8874,140,8877,140,8880,140,8884,140,8886,140,8889,140,8891,140,8895,140,8898,140,8902,140,8905,140,8910,140,8915,140,8917,140,8919,140,8923,140,8927,140,8930,140,8932,140,8936,140,8940,140,8942,140,8945,140,8948],{"viewBox":8866,"role":136,"ariaLabel":8867,"xmlns":138,"style":139},"0 0 720 400","Flowchart deciding whether CSS can handle an animation or JavaScript is required",[142,8869,8870],{},"Can CSS do it? decision flowchart",[146,8872,8873],{},"A flowchart that routes an animation requirement to CSS or to JavaScript based on whether values are runtime-computed or motion must be interrupted.",[150,8875,8876],{"x":152,"y":153,"style":154},"Can CSS do it?",[171,8878],{"x":2601,"y":5139,"width":2602,"height":8879,"rx":836,"fill":177,"opacity":178},"44",[150,8881,8883],{"x":152,"y":8882,"style":2606},"75","Static, known values?",[163,8885],{"x1":152,"y1":4196,"x2":152,"y2":4158,"stroke":167,"strokeWidth":168},[150,8887,8888],{"x":538,"y":2614,"style":4181},"yes",[171,8890],{"x":3112,"y":4158,"width":538,"height":4146,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},[150,8892,8894],{"x":152,"y":8893,"style":2606},"143","Use CSS transition \u002F keyframes",[163,8896],{"x1":8897,"y1":5144,"x2":4176,"y2":5144,"stroke":167,"strokeWidth":168},"450",[150,8899,8901],{"x":8900,"y":5905,"style":4181},"525","no",[171,8903],{"x":5887,"y":4158,"width":8904,"height":8879,"rx":836,"fill":177,"opacity":178},"156",[150,8906,8909],{"x":8907,"y":8908,"style":2606},"618","139","Scroll-linked?",[150,8911,8914],{"x":8907,"y":8912,"style":8913},"155","text-anchor:middle;fill:currentColor;font:11px sans-serif;opacity:0.8","use animation-timeline",[163,8916],{"x1":4176,"y1":5144,"x2":4176,"y2":4158,"stroke":167,"strokeWidth":168},[171,8918],{"x":3112,"y":174,"width":538,"height":8879,"rx":836,"fill":177,"opacity":178},[150,8920,8922],{"x":152,"y":8921,"style":2606},"221","Runtime geometry?",[150,8924,8926],{"x":152,"y":8925,"style":8913},"237","FLIP",[163,8928],{"x1":152,"y1":8929,"x2":152,"y2":174,"stroke":167,"strokeWidth":168},"158",[171,8931],{"x":3112,"y":6736,"width":538,"height":8879,"rx":836,"fill":177,"opacity":178},[150,8933,8935],{"x":152,"y":8934,"style":2606},"289","Velocity \u002F interrupt?",[150,8937,8939],{"x":152,"y":8938,"style":8913},"305","physics, reverse mid-flight",[163,8941],{"x1":152,"y1":217,"x2":152,"y2":6736,"stroke":167,"strokeWidth":168},[163,8943],{"x1":152,"y1":6740,"x2":152,"y2":8944,"stroke":167,"strokeWidth":168},"338",[171,8946],{"x":8947,"y":8944,"width":8947,"height":8879,"rx":836,"fill":72,"stroke":177,"strokeWidth":194},"240",[150,8949,8951],{"x":152,"y":8950,"style":5883},"365","Reach for JS \u002F WAAPI",[47,8953],{},[50,8955,8957],{"id":8956},"complete-working-implementation-a-flip-layout-transition","Complete working implementation: a FLIP layout transition",[14,8959,8960,8961,8963,8964,8966,8967,8970,8971,42],{},"The canonical case JavaScript exists for is ",[62,8962,8926],{}," — First, Last, Invert, Play. When an element changes position because the layout reflowed (a card moving as a list reorders, a thumbnail expanding into a hero), CSS cannot animate it: the start and end coordinates are only knowable after the browser computes layout. FLIP measures both, applies an inverting ",[18,8965,103],{}," so the element ",[86,8968,8969],{},"looks"," unmoved, then animates that transform to zero. The result is a smooth, composited move using only ",[18,8972,103],{},[281,8974,8976],{"className":283,"code":8975,"language":285,"meta":286,"style":286},"\u003C!DOCTYPE html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\" \u002F>\n\u003Cstyle>\n  .grid { display: flex; flex-wrap: wrap; gap: 12px; }\n  .card {\n    width: 120px; height: 80px; border-radius: 10px;\n    background: #7aa2ff; color: #fff; display: grid; place-items: center;\n    \u002F* No transition here: FLIP drives motion via the Web Animations API. *\u002F\n  }\n  .grid.compact .card { width: 80px; height: 80px; }\n  button { margin-bottom: 16px; }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cbutton id=\"shuffle\">Reorder\u003C\u002Fbutton>\n  \u003Cdiv class=\"grid\" id=\"grid\">\n    \u003Cdiv class=\"card\">1\u003C\u002Fdiv>\u003Cdiv class=\"card\">2\u003C\u002Fdiv>\n    \u003Cdiv class=\"card\">3\u003C\u002Fdiv>\u003Cdiv class=\"card\">4\u003C\u002Fdiv>\n    \u003Cdiv class=\"card\">5\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\n  \u003Cscript>\n    const grid = document.getElementById(\"grid\");\n    const reduce = matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\n    document.getElementById(\"shuffle\").addEventListener(\"click\", () => {\n      const cards = [...grid.children];\n\n      \u002F\u002F FIRST: record each card's current position.\n      const first = new Map(cards.map((el) => [el, el.getBoundingClientRect()]));\n\n      \u002F\u002F Mutate the DOM: reverse the order (the \"layout change\").\n      cards.reverse().forEach((el) => grid.appendChild(el));\n\n      cards.forEach((el) => {\n        \u002F\u002F LAST: read the new position after the reflow.\n        const last = el.getBoundingClientRect();\n        const f = first.get(el);\n\n        \u002F\u002F INVERT: compute the delta and apply it as a transform so the\n        \u002F\u002F card visually stays where it was. These deltas are the runtime\n        \u002F\u002F values CSS alone cannot know.\n        const dx = f.left - last.left;\n        const dy = f.top - last.top;\n\n        \u002F\u002F PLAY: animate the inverting transform back to zero. Honour the\n        \u002F\u002F reduced-motion preference by snapping instantly.\n        el.animate(\n          [\n            { transform: `translate(${dx}px, ${dy}px)` },\n            { transform: \"translate(0, 0)\" },\n          ],\n          {\n            duration: reduce ? 0 : 320,\n            easing: \"cubic-bezier(0.2, 0.8, 0.2, 1)\",\n          }\n        );\n      });\n    });\n  \u003C\u002Fscript>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,8977,8978,8991,9007,9016,9034,9042,9079,9086,9120,9160,9165,9169,9199,9217,9225,9233,9242,9262,9283,9319,9353,9372,9380,9384,9393,9413,9429,9433,9459,9478,9482,9487,9525,9529,9534,9564,9568,9584,9589,9606,9624,9628,9633,9639,9645,9663,9681,9686,9692,9698,9708,9714,9736,9746,9752,9758,9775,9785,9791,9797,9803,9809,9818,9827],{"__ignoreMap":286},[290,8979,8980,8983,8986,8989],{"class":163,"line":292},[290,8981,8982],{"class":295},"\u003C!",[290,8984,8985],{"class":299},"DOCTYPE",[290,8987,8988],{"class":303}," html",[290,8990,327],{"class":295},[290,8992,8993,8995,8997,9000,9002,9005],{"class":163,"line":330},[290,8994,296],{"class":295},[290,8996,285],{"class":299},[290,8998,8999],{"class":303}," lang",[290,9001,307],{"class":295},[290,9003,9004],{"class":310},"\"en\"",[290,9006,327],{"class":295},[290,9008,9009,9011,9014],{"class":163,"line":337},[290,9010,296],{"class":295},[290,9012,9013],{"class":299},"head",[290,9015,327],{"class":295},[290,9017,9018,9020,9023,9026,9028,9031],{"class":163,"line":364},[290,9019,296],{"class":295},[290,9021,9022],{"class":299},"meta",[290,9024,9025],{"class":303}," charset",[290,9027,307],{"class":295},[290,9029,9030],{"class":310},"\"utf-8\"",[290,9032,9033],{"class":295}," \u002F>\n",[290,9035,9036,9038,9040],{"class":163,"line":386},[290,9037,296],{"class":295},[290,9039,1430],{"class":299},[290,9041,327],{"class":295},[290,9043,9044,9047,9049,9051,9053,9056,9058,9061,9063,9066,9068,9071,9073,9075,9077],{"class":163,"line":408},[290,9045,9046],{"class":303},"  .grid",[290,9048,790],{"class":295},[290,9050,59],{"class":461},[290,9052,465],{"class":295},[290,9054,9055],{"class":461},"flex",[290,9057,828],{"class":295},[290,9059,9060],{"class":461},"flex-wrap",[290,9062,465],{"class":295},[290,9064,9065],{"class":461},"wrap",[290,9067,828],{"class":295},[290,9069,9070],{"class":461},"gap",[290,9072,465],{"class":295},[290,9074,5894],{"class":461},[290,9076,674],{"class":541},[290,9078,809],{"class":295},[290,9080,9081,9084],{"class":163,"line":428},[290,9082,9083],{"class":303},"  .card",[290,9085,450],{"class":295},[290,9087,9088,9091,9093,9095,9097,9099,9101,9103,9106,9108,9110,9112,9114,9116,9118],{"class":163,"line":517},[290,9089,9090],{"class":461},"    width",[290,9092,465],{"class":295},[290,9094,4147],{"class":461},[290,9096,674],{"class":541},[290,9098,828],{"class":295},[290,9100,2722],{"class":461},[290,9102,465],{"class":295},[290,9104,9105],{"class":461},"80",[290,9107,674],{"class":541},[290,9109,828],{"class":295},[290,9111,831],{"class":461},[290,9113,465],{"class":295},[290,9115,836],{"class":461},[290,9117,674],{"class":541},[290,9119,471],{"class":295},[290,9121,9122,9125,9127,9129,9131,9134,9136,9139,9141,9143,9145,9148,9150,9153,9155,9158],{"class":163,"line":523},[290,9123,9124],{"class":461},"    background",[290,9126,465],{"class":295},[290,9128,177],{"class":461},[290,9130,828],{"class":295},[290,9132,9133],{"class":461},"color",[290,9135,465],{"class":295},[290,9137,9138],{"class":461},"#fff",[290,9140,828],{"class":295},[290,9142,59],{"class":461},[290,9144,465],{"class":295},[290,9146,9147],{"class":461},"grid",[290,9149,828],{"class":295},[290,9151,9152],{"class":461},"place-items",[290,9154,465],{"class":295},[290,9156,9157],{"class":461},"center",[290,9159,471],{"class":295},[290,9161,9162],{"class":163,"line":532},[290,9163,9164],{"class":455},"    \u002F* No transition here: FLIP drives motion via the Web Animations API. *\u002F\n",[290,9166,9167],{"class":163,"line":551},[290,9168,771],{"class":295},[290,9170,9171,9174,9177,9179,9181,9183,9185,9187,9189,9191,9193,9195,9197],{"class":163,"line":586},[290,9172,9173],{"class":303},"  .grid.compact",[290,9175,9176],{"class":303}," .card",[290,9178,790],{"class":295},[290,9180,1748],{"class":461},[290,9182,465],{"class":295},[290,9184,9105],{"class":461},[290,9186,674],{"class":541},[290,9188,828],{"class":295},[290,9190,2722],{"class":461},[290,9192,465],{"class":295},[290,9194,9105],{"class":461},[290,9196,674],{"class":541},[290,9198,809],{"class":295},[290,9200,9201,9203,9205,9208,9210,9213,9215],{"class":163,"line":602},[290,9202,2127],{"class":299},[290,9204,790],{"class":295},[290,9206,9207],{"class":461},"margin-bottom",[290,9209,465],{"class":295},[290,9211,9212],{"class":461},"16",[290,9214,674],{"class":541},[290,9216,809],{"class":295},[290,9218,9219,9221,9223],{"class":163,"line":617},[290,9220,431],{"class":295},[290,9222,1430],{"class":299},[290,9224,327],{"class":295},[290,9226,9227,9229,9231],{"class":163,"line":623},[290,9228,431],{"class":295},[290,9230,9013],{"class":299},[290,9232,327],{"class":295},[290,9234,9235,9237,9240],{"class":163,"line":628},[290,9236,296],{"class":295},[290,9238,9239],{"class":299},"body",[290,9241,327],{"class":295},[290,9243,9244,9246,9248,9250,9252,9255,9258,9260],{"class":163,"line":634},[290,9245,367],{"class":295},[290,9247,300],{"class":299},[290,9249,345],{"class":303},[290,9251,307],{"class":295},[290,9253,9254],{"class":310},"\"shuffle\"",[290,9256,9257],{"class":295},">Reorder\u003C\u002F",[290,9259,300],{"class":299},[290,9261,327],{"class":295},[290,9263,9264,9266,9268,9270,9272,9275,9277,9279,9281],{"class":163,"line":649},[290,9265,367],{"class":295},[290,9267,342],{"class":299},[290,9269,314],{"class":303},[290,9271,307],{"class":295},[290,9273,9274],{"class":310},"\"grid\"",[290,9276,345],{"class":303},[290,9278,307],{"class":295},[290,9280,9274],{"class":310},[290,9282,327],{"class":295},[290,9284,9285,9287,9289,9291,9293,9296,9299,9301,9304,9306,9308,9310,9312,9315,9317],{"class":163,"line":660},[290,9286,4290],{"class":295},[290,9288,342],{"class":299},[290,9290,314],{"class":303},[290,9292,307],{"class":295},[290,9294,9295],{"class":310},"\"card\"",[290,9297,9298],{"class":295},">1\u003C\u002F",[290,9300,342],{"class":299},[290,9302,9303],{"class":295},">\u003C",[290,9305,342],{"class":299},[290,9307,314],{"class":303},[290,9309,307],{"class":295},[290,9311,9295],{"class":310},[290,9313,9314],{"class":295},">2\u003C\u002F",[290,9316,342],{"class":299},[290,9318,327],{"class":295},[290,9320,9321,9323,9325,9327,9329,9331,9334,9336,9338,9340,9342,9344,9346,9349,9351],{"class":163,"line":688},[290,9322,4290],{"class":295},[290,9324,342],{"class":299},[290,9326,314],{"class":303},[290,9328,307],{"class":295},[290,9330,9295],{"class":310},[290,9332,9333],{"class":295},">3\u003C\u002F",[290,9335,342],{"class":299},[290,9337,9303],{"class":295},[290,9339,342],{"class":299},[290,9341,314],{"class":303},[290,9343,307],{"class":295},[290,9345,9295],{"class":310},[290,9347,9348],{"class":295},">4\u003C\u002F",[290,9350,342],{"class":299},[290,9352,327],{"class":295},[290,9354,9355,9357,9359,9361,9363,9365,9368,9370],{"class":163,"line":693},[290,9356,4290],{"class":295},[290,9358,342],{"class":299},[290,9360,314],{"class":303},[290,9362,307],{"class":295},[290,9364,9295],{"class":310},[290,9366,9367],{"class":295},">5\u003C\u002F",[290,9369,342],{"class":299},[290,9371,327],{"class":295},[290,9373,9374,9376,9378],{"class":163,"line":698},[290,9375,4315],{"class":295},[290,9377,342],{"class":299},[290,9379,327],{"class":295},[290,9381,9382],{"class":163,"line":704},[290,9383,334],{"emptyLinePlaceholder":333},[290,9385,9386,9388,9391],{"class":163,"line":710},[290,9387,367],{"class":295},[290,9389,9390],{"class":299},"script",[290,9392,327],{"class":295},[290,9394,9395,9398,9401,9403,9405,9407,9409,9411],{"class":163,"line":717},[290,9396,9397],{"class":541},"    const",[290,9399,9400],{"class":461}," grid",[290,9402,7288],{"class":541},[290,9404,7291],{"class":295},[290,9406,7294],{"class":303},[290,9408,484],{"class":295},[290,9410,9274],{"class":310},[290,9412,500],{"class":295},[290,9414,9415,9417,9419,9421,9423,9425,9427],{"class":163,"line":730},[290,9416,9397],{"class":541},[290,9418,7926],{"class":461},[290,9420,7288],{"class":541},[290,9422,7931],{"class":303},[290,9424,484],{"class":295},[290,9426,2924],{"class":310},[290,9428,7938],{"class":295},[290,9430,9431],{"class":163,"line":742},[290,9432,334],{"emptyLinePlaceholder":333},[290,9434,9435,9438,9440,9442,9444,9446,9448,9450,9452,9455,9457],{"class":163,"line":768},[290,9436,9437],{"class":295},"    document.",[290,9439,7294],{"class":303},[290,9441,484],{"class":295},[290,9443,9254],{"class":310},[290,9445,3724],{"class":295},[290,9447,2972],{"class":303},[290,9449,484],{"class":295},[290,9451,8282],{"class":310},[290,9453,9454],{"class":295},", () ",[290,9456,2988],{"class":541},[290,9458,450],{"class":295},[290,9460,9461,9464,9467,9469,9472,9475],{"class":163,"line":774},[290,9462,9463],{"class":541},"      const",[290,9465,9466],{"class":461}," cards",[290,9468,7288],{"class":541},[290,9470,9471],{"class":295}," [",[290,9473,9474],{"class":541},"...",[290,9476,9477],{"class":295},"grid.children];\n",[290,9479,9480],{"class":163,"line":779},[290,9481,334],{"emptyLinePlaceholder":333},[290,9483,9484],{"class":163,"line":784},[290,9485,9486],{"class":455},"      \u002F\u002F FIRST: record each card's current position.\n",[290,9488,9489,9491,9493,9495,9498,9501,9504,9507,9510,9513,9515,9517,9520,9522],{"class":163,"line":812},[290,9490,9463],{"class":541},[290,9492,3904],{"class":461},[290,9494,7288],{"class":541},[290,9496,9497],{"class":541}," new",[290,9499,9500],{"class":303}," Map",[290,9502,9503],{"class":295},"(cards.",[290,9505,9506],{"class":303},"map",[290,9508,9509],{"class":295},"((",[290,9511,9512],{"class":1561},"el",[290,9514,490],{"class":295},[290,9516,2988],{"class":541},[290,9518,9519],{"class":295}," [el, el.",[290,9521,7438],{"class":303},[290,9523,9524],{"class":295},"()]));\n",[290,9526,9527],{"class":163,"line":860},[290,9528,334],{"emptyLinePlaceholder":333},[290,9530,9531],{"class":163,"line":865},[290,9532,9533],{"class":455},"      \u002F\u002F Mutate the DOM: reverse the order (the \"layout change\").\n",[290,9535,9536,9539,9541,9544,9547,9549,9551,9553,9555,9558,9561],{"class":163,"line":871},[290,9537,9538],{"class":295},"      cards.",[290,9540,7035],{"class":303},[290,9542,9543],{"class":295},"().",[290,9545,9546],{"class":303},"forEach",[290,9548,9509],{"class":295},[290,9550,9512],{"class":1561},[290,9552,490],{"class":295},[290,9554,2988],{"class":541},[290,9556,9557],{"class":295}," grid.",[290,9559,9560],{"class":303},"appendChild",[290,9562,9563],{"class":295},"(el));\n",[290,9565,9566],{"class":163,"line":880},[290,9567,334],{"emptyLinePlaceholder":333},[290,9569,9570,9572,9574,9576,9578,9580,9582],{"class":163,"line":896},[290,9571,9538],{"class":295},[290,9573,9546],{"class":303},[290,9575,9509],{"class":295},[290,9577,9512],{"class":1561},[290,9579,490],{"class":295},[290,9581,2988],{"class":541},[290,9583,450],{"class":295},[290,9585,9586],{"class":163,"line":4734},[290,9587,9588],{"class":455},"        \u002F\u002F LAST: read the new position after the reflow.\n",[290,9590,9591,9594,9597,9599,9602,9604],{"class":163,"line":4742},[290,9592,9593],{"class":541},"        const",[290,9595,9596],{"class":461}," last",[290,9598,7288],{"class":541},[290,9600,9601],{"class":295}," el.",[290,9603,7438],{"class":303},[290,9605,7594],{"class":295},[290,9607,9608,9610,9613,9615,9618,9621],{"class":163,"line":4761},[290,9609,9593],{"class":541},[290,9611,9612],{"class":461}," f",[290,9614,7288],{"class":541},[290,9616,9617],{"class":295}," first.",[290,9619,9620],{"class":303},"get",[290,9622,9623],{"class":295},"(el);\n",[290,9625,9626],{"class":163,"line":4766},[290,9627,334],{"emptyLinePlaceholder":333},[290,9629,9630],{"class":163,"line":4786},[290,9631,9632],{"class":455},"        \u002F\u002F INVERT: compute the delta and apply it as a transform so the\n",[290,9634,9636],{"class":163,"line":9635},43,[290,9637,9638],{"class":455},"        \u002F\u002F card visually stays where it was. These deltas are the runtime\n",[290,9640,9642],{"class":163,"line":9641},44,[290,9643,9644],{"class":455},"        \u002F\u002F values CSS alone cannot know.\n",[290,9646,9648,9650,9653,9655,9658,9660],{"class":163,"line":9647},45,[290,9649,9593],{"class":541},[290,9651,9652],{"class":461}," dx",[290,9654,7288],{"class":541},[290,9656,9657],{"class":295}," f.left ",[290,9659,7444],{"class":541},[290,9661,9662],{"class":295}," last.left;\n",[290,9664,9666,9668,9671,9673,9676,9678],{"class":163,"line":9665},46,[290,9667,9593],{"class":541},[290,9669,9670],{"class":461}," dy",[290,9672,7288],{"class":541},[290,9674,9675],{"class":295}," f.top ",[290,9677,7444],{"class":541},[290,9679,9680],{"class":295}," last.top;\n",[290,9682,9684],{"class":163,"line":9683},47,[290,9685,334],{"emptyLinePlaceholder":333},[290,9687,9689],{"class":163,"line":9688},48,[290,9690,9691],{"class":455},"        \u002F\u002F PLAY: animate the inverting transform back to zero. Honour the\n",[290,9693,9695],{"class":163,"line":9694},49,[290,9696,9697],{"class":455},"        \u002F\u002F reduced-motion preference by snapping instantly.\n",[290,9699,9701,9704,9706],{"class":163,"line":9700},50,[290,9702,9703],{"class":295},"        el.",[290,9705,7319],{"class":303},[290,9707,7322],{"class":295},[290,9709,9711],{"class":163,"line":9710},51,[290,9712,9713],{"class":295},"          [\n",[290,9715,9717,9720,9723,9726,9729,9732,9734],{"class":163,"line":9716},52,[290,9718,9719],{"class":295},"            { transform: ",[290,9721,9722],{"class":310},"`translate(${",[290,9724,9725],{"class":295},"dx",[290,9727,9728],{"class":310},"}px, ${",[290,9730,9731],{"class":295},"dy",[290,9733,7485],{"class":310},[290,9735,7343],{"class":295},[290,9737,9739,9741,9744],{"class":163,"line":9738},53,[290,9740,9719],{"class":295},[290,9742,9743],{"class":310},"\"translate(0, 0)\"",[290,9745,7343],{"class":295},[290,9747,9749],{"class":163,"line":9748},54,[290,9750,9751],{"class":295},"          ],\n",[290,9753,9755],{"class":163,"line":9754},55,[290,9756,9757],{"class":295},"          {\n",[290,9759,9761,9764,9766,9768,9770,9773],{"class":163,"line":9760},56,[290,9762,9763],{"class":295},"            duration: reduce ",[290,9765,5734],{"class":541},[290,9767,1198],{"class":461},[290,9769,8083],{"class":541},[290,9771,9772],{"class":461}," 320",[290,9774,548],{"class":295},[290,9776,9778,9781,9783],{"class":163,"line":9777},57,[290,9779,9780],{"class":295},"            easing: ",[290,9782,7374],{"class":310},[290,9784,548],{"class":295},[290,9786,9788],{"class":163,"line":9787},58,[290,9789,9790],{"class":295},"          }\n",[290,9792,9794],{"class":163,"line":9793},59,[290,9795,9796],{"class":295},"        );\n",[290,9798,9800],{"class":163,"line":9799},60,[290,9801,9802],{"class":295},"      });\n",[290,9804,9806],{"class":163,"line":9805},61,[290,9807,9808],{"class":295},"    });\n",[290,9810,9812,9814,9816],{"class":163,"line":9811},62,[290,9813,4315],{"class":295},[290,9815,9390],{"class":299},[290,9817,327],{"class":295},[290,9819,9821,9823,9825],{"class":163,"line":9820},63,[290,9822,431],{"class":295},[290,9824,9239],{"class":299},[290,9826,327],{"class":295},[290,9828,9830,9832,9834],{"class":163,"line":9829},64,[290,9831,431],{"class":295},[290,9833,285],{"class":299},[290,9835,327],{"class":295},[47,9837],{},[50,9839,9841],{"id":9840},"key-technique-callout-invert-then-animate-to-zero","Key technique callout: invert, then animate to zero",[14,9843,9844,9845,9848,9849,9852,9853,3041,9855,9857,9858,9861,9862,9864,9865,9868,9869,9871,9872,9874,9875,69,9877,9879],{},"The single idea that makes FLIP work is the ",[62,9846,9847],{},"Invert"," step. After the DOM mutates and the browser has already painted the element at its new position, you apply ",[18,9850,9851],{},"transform: translate(dx, dy)"," where ",[18,9854,9725],{},[18,9856,9731],{}," are the ",[86,9859,9860],{},"old position minus the new position",". Because ",[18,9863,103],{}," is a visual-only, composited property, the element appears to occupy its original spot without affecting layout. You then animate that transform to ",[18,9866,9867],{},"translate(0, 0)",", and the eye reads a smooth glide from old to new. The animation is pure ",[18,9870,103],{},", so it composites at 60fps; the only main-thread cost is the two ",[18,9873,7533],{}," reads, taken in a tight First\u002FLast pair to avoid layout thrashing. CSS cannot participate because ",[18,9876,9725],{},[18,9878,9731],{}," do not exist until layout runs.",[47,9881],{},[50,9883,9885],{"id":9884},"variation-or-extension-reduced-motion-and-the-css-first-alternative","Variation or extension: reduced-motion and the CSS-first alternative",[14,9887,9888,9889,9892,9893,69,9896,9899,9900,9902,9903,9906,9907,9909,9910,9913,9914,9916,9917,42],{},"For the other three cases, always test the CSS-first option before scripting. ",[62,9890,9891],{},"Scroll-linked"," progress bars and reveal-on-scroll effects are now declarative via ",[18,9894,9895],{},"animation-timeline: scroll()",[18,9897,9898],{},"view()",", which the ",[27,9901,1420],{"href":1419}," guide builds on — only escalate to a scroll listener when the math is arbitrary. ",[62,9904,9905],{},"Physics"," often reads convincingly as a tuned ",[18,9908,6773],{}," overshoot; reserve a spring library for velocity-driven motion like a flung draggable. ",[62,9911,9912],{},"Interrupt\u002Freverse"," is the WAAPI's ",[18,9915,8617],{}," and settable ",[18,9918,7699],{},[14,9920,9921,9922,80,9924,9926],{},"The reduced-motion guard already shown collapses ",[18,9923,6940],{},[18,9925,487],{},", but you can go further and skip the animation entirely so no transform is ever applied:",[281,9928,9930],{"className":2904,"code":9929,"language":2906,"meta":286,"style":286},"if (matchMedia(\"(prefers-reduced-motion: reduce)\").matches) {\n  cards.reverse().forEach((el) => grid.appendChild(el)); \u002F\u002F just reorder, no FLIP\n} else {\n  \u002F\u002F ...run the First\u002FLast\u002FInvert\u002FPlay sequence...\n}\n",[18,9931,9932,9946,9975,9985,9990],{"__ignoreMap":286},[290,9933,9934,9936,9938,9940,9942,9944],{"class":163,"line":292},[290,9935,2913],{"class":541},[290,9937,3595],{"class":295},[290,9939,2919],{"class":303},[290,9941,484],{"class":295},[290,9943,2924],{"class":310},[290,9945,2927],{"class":295},[290,9947,9948,9951,9953,9955,9957,9959,9961,9963,9965,9967,9969,9972],{"class":163,"line":330},[290,9949,9950],{"class":295},"  cards.",[290,9952,7035],{"class":303},[290,9954,9543],{"class":295},[290,9956,9546],{"class":303},[290,9958,9509],{"class":295},[290,9960,9512],{"class":1561},[290,9962,490],{"class":295},[290,9964,2988],{"class":541},[290,9966,9557],{"class":295},[290,9968,9560],{"class":303},[290,9970,9971],{"class":295},"(el)); ",[290,9973,9974],{"class":455},"\u002F\u002F just reorder, no FLIP\n",[290,9976,9977,9980,9983],{"class":163,"line":337},[290,9978,9979],{"class":295},"} ",[290,9981,9982],{"class":541},"else",[290,9984,450],{"class":295},[290,9986,9987],{"class":163,"line":364},[290,9988,9989],{"class":455},"  \u002F\u002F ...run the First\u002FLast\u002FInvert\u002FPlay sequence...\n",[290,9991,9992],{"class":163,"line":386},[290,9993,620],{"class":295},[14,9995,9996,9997,9999],{},"This mirrors the CSS pattern of wrapping motion in ",[18,9998,4126],{}," — the instant state change remains correct and usable.",[47,10001],{},[50,10003,1299],{"id":1298},[14,10005,10006,10007,10009,10010,10012,10013,3942,10015,3041,10018,10020,10021,10024,10025,10027],{},"The Web Animations API used here (",[18,10008,6582],{}," returning an ",[18,10011,6829],{},") is interoperable in Chrome\u002FEdge 84+, Safari 13.1+, and Firefox 75+, so FLIP needs no polyfill on evergreen targets. The declarative scroll alternative, ",[18,10014,8833],{},[18,10016,10017],{},"scroll()",[18,10019,9898],{},", shipped in Chrome\u002FEdge 115+ and Firefox 114+ (behind broader rollout), with Safari support arriving later; gate it with ",[18,10022,10023],{},"@supports (animation-timeline: scroll())"," and fall back to a static state. ",[18,10026,7533],{}," is universal.",[47,10029],{},[50,10031,1316],{"id":1315},[14,10033,10034,10037,10038,10040],{},[62,10035,10036],{},"When is JavaScript genuinely required for an animation?","\nJavaScript is required when the values depend on measured layout (FLIP), when motion must continue with physics like spring or inertia, when you must interrupt and reverse from a live position, or for scroll effects that cannot be expressed with ",[18,10039,8833],{},". Static, state-triggered motion should stay in CSS.",[14,10042,10043,10046],{},[62,10044,10045],{},"What is the FLIP technique?","\nFLIP stands for First, Last, Invert, Play. You measure an element's start and end geometry, apply an inverting transform so it appears unmoved, then animate the transform to zero. It needs JavaScript because the start and end positions are only known at runtime.",[14,10048,10049,10052,10053,10055,10056,69,10058,10060],{},[62,10050,10051],{},"Can CSS handle scroll-linked animation without JavaScript?","\nOften yes. The ",[18,10054,8833],{}," property with ",[18,10057,10017],{},[18,10059,9898],{}," covers progress bars and reveal-on-scroll effects declaratively. Reach for JavaScript only when the effect needs arbitrary scroll math or must coordinate with non-scroll state.",[14,10062,10063,10066,10067,10069,10070,10072],{},[62,10064,10065],{},"Should I use a physics library for spring animations?","\nOnly when a single CSS ",[18,10068,6773],{}," curve cannot fake the feel convincingly. Many springs read fine as a tuned ",[18,10071,6773],{}," or a steps-free overshoot keyframe. Use a spring library when motion must respond to variable velocity, such as a flung, draggable element.",[47,10074],{},[50,10076,1391],{"id":1390},[1393,10078,10079,10084,10089,10096,10102],{},[1396,10080,10081,10083],{},[27,10082,8823],{"href":8822}," — the decision guide this page extends.",[1396,10085,10086,10088],{},[27,10087,1420],{"href":1419}," — declarative keyframes, including scroll-driven motion.",[1396,10090,10091,10093,10094,42],{},[27,10092,8757],{"href":8756}," — why FLIP stays smooth using only ",[18,10095,103],{},[1396,10097,10098,10101],{},[27,10099,10100],{"href":1426},"Building Responsive Cards with Container Queries"," — laying out the cards a FLIP transition then animates.",[1396,10103,10104,10106],{},[27,10105,1481],{"href":1480}," — the parent guide for declarative motion.",[1430,10108,10109],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":286,"searchDepth":330,"depth":330,"links":10111},[10112,10113,10114,10115,10116,10117,10118],{"id":8839,"depth":330,"text":8840},{"id":8956,"depth":330,"text":8957},{"id":9840,"depth":330,"text":9841},{"id":9884,"depth":330,"text":9885},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Concrete cases where JavaScript or the Web Animations API beats CSS: FLIP layout transitions, scroll-linked effects, physics, and interrupting motion mid-flight.",{"seoTitle":8740,"datePublished":1447,"dateModified":1447,"faq":10121},[10122,10124,10126,10128],{"q":10036,"a":10123},"JavaScript is required when the values depend on measured layout (FLIP), when motion must continue with physics like spring or inertia, when you must interrupt and reverse from a live position, or for scroll effects that cannot be expressed with animation-timeline. Static, state-triggered motion should stay in CSS.",{"q":10045,"a":10125},"FLIP stands for First, Last, Invert, Play. You measure an element's start and end geometry, apply an inverting transform so it appears unmoved, then animate the transform to zero. It needs JavaScript because the start and end positions are only known at runtime.",{"q":10051,"a":10127},"Often yes. The animation-timeline property with scroll() and view() covers progress bars and reveal-on-scroll effects declaratively. Reach for JavaScript only when the effect needs arbitrary scroll math or must coordinate with non-scroll state.",{"q":10065,"a":10129},"Only when a single CSS cubic-bezier curve cannot fake the feel convincingly. Many springs read fine as a tuned cubic-bezier or a steps-free overshoot keyframe. Use a spring library when motion must respond to variable velocity, such as a flung, draggable element.","\u002Fcss-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Fwhen-to-use-javascript-animation",{"title":8811,"description":10119},"css-only-micro-interactions-animations\u002Fcss-animation-vs-web-animations-api\u002Fwhen-to-use-javascript-animation\u002Findex","_VXinDEGstQudoRptxoqWvnJA9qhNMmDf0MzCA_2l10",{"id":10135,"title":10136,"body":10137,"description":11264,"extension":1444,"meta":11265,"navigation":333,"path":11276,"seo":11277,"stem":11278,"__hash__":11279},"content\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fanimating-custom-properties-with-at-property\u002Findex.md","Animating Custom Properties with @property: Typed Variables That Interpolate",{"type":7,"value":10138,"toc":11254},[10139,10142,10160,10163,10165,10169,10183,10215,10217,10221,10228,10288,10290,10294,10304,10335,10792,10812,10814,10818,10860,10862,10866,10869,11126,11131,11133,11135,11155,11157,11159,11180,11197,11209,11217,11219,11221,11251],[10,10140,10136],{"id":10141},"animating-custom-properties-with-property-typed-variables-that-interpolate",[14,10143,10144,10145,10148,10149,10152,10153,10156,10157,10159],{},"You set a transition on a custom property, change its value on hover, and nothing animates — the value just snaps. This is the single most common surprise with CSS variables, and the fix is not a different transition but a one-time registration. This guide, part of the ",[27,10146,3355],{"href":10147},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002F"," section, explains how the ",[18,10150,10151],{},"@property"," at-rule gives a custom property a ",[86,10154,10155],{},"type"," so the browser knows how to interpolate it, turning previously un-animatable values — gradient angles, raw numbers, colors fed into ",[18,10158,3693],{}," — into smoothly transitionable motion with no JavaScript.",[14,10161,10162],{},"The narrow scenario: you want a conic gradient to rotate, a counter-like number to count up, or a hue to glide across hover states, driven entirely by a custom property and a transition.",[47,10164],{},[50,10166,10168],{"id":10167},"why-registration-is-required-and-when-it-is-not-worth-it","Why registration is required, and when it is not worth it",[14,10170,10171,10172,10175,10176,10178,10179,10182],{},"By default every custom property is, for animation purposes, of the universal type ",[18,10173,10174],{},"\u003Ccustom-ident>"," — an opaque token the browser cannot subdivide. Because there is no halfway point between two arbitrary strings, the engine treats the change as ",[62,10177,64],{},": it holds the old value, then jumps to the new one at the end of the transition. That is why ",[18,10180,10181],{},"transition: --angle 1s"," does nothing visible even though the syntax is accepted.",[14,10184,10185,10187,10188,10191,10192,10195,10196,10199,10200,10202,10203,10206,10207,10211,10212,42],{},[18,10186,10151],{}," removes the ambiguity. Declaring ",[18,10189,10190],{},"syntax: \"\u003Cangle>\""," tells the browser the value is an angle, which it knows how to interpolate degree by degree, so the same transition now animates frame by frame on the compositor or main thread as appropriate. The cost is one at-rule per animatable property and the discipline of supplying a valid ",[18,10193,10194],{},"initial-value",". It is worth it whenever the visual change is a continuous quantity — angle, length, number, percentage, or color. It is ",[86,10197,10198],{},"not"," worth it for values that are inherently discrete (a ",[18,10201,59],{}," keyword, a ",[18,10204,10205],{},"grid-template"," string); those belong to other techniques such as ",[27,10208,10210],{"href":10209},"\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Ftransitioning-display-with-allow-discrete\u002F","transitioning display with allow-discrete",". There is no accessibility downside to registration itself, but the motion it unlocks must still collapse under ",[27,10213,10214],{"href":1412},"reduced motion preferences",[47,10216],{},[50,10218,10220],{"id":10219},"registered-versus-unregistered-animatable-versus-not","Registered versus unregistered: animatable versus not",[14,10222,10223,10224,10227],{},"The diagram contrasts the two paths. An unregistered property is typeless, so a transition produces a discrete jump. A registered property carries a typed ",[18,10225,10226],{},"syntax",", so the same transition interpolates.",[133,10229,140,10231,140,10234,140,10237,140,140,10240,140,10242,140,10246,140,10250,140,140,10254,140,10259,140,140,10264,140,10268,140,10270,140,10275,140,10277,140,10280,140,10285],{"viewBox":135,"role":136,"ariaLabel":10230,"xmlns":138,"style":139},"Comparison of an unregistered custom property that jumps versus a registered one that interpolates",[142,10232,10233],{},"Registered versus unregistered custom property",[146,10235,10236],{},"Two panels: an unregistered property treated as a discrete type that snaps between values, and a registered property with a typed syntax that interpolates smoothly.",[150,10238,10239],{"x":152,"y":2598,"style":154},"Why @property makes a variable animatable",[171,10241],{"x":158,"y":4172,"width":2613,"height":2601,"rx":5894,"fill":72,"stroke":167,"strokeWidth":168,"opacity":6041},[150,10243,10245],{"x":6669,"y":1823,"style":10244},"text-anchor:middle;fill:currentColor;font:600 15px sans-serif","Unregistered",[150,10247,10249],{"x":6669,"y":2614,"style":10248},"text-anchor:middle;fill:currentColor;font:12px ui-monospace,monospace;opacity:0.8","--angle: 0deg",[150,10251,10253],{"x":6669,"y":5123,"style":10252},"text-anchor:middle;fill:currentColor;font:11px sans-serif;opacity:0.7","type = custom-ident (opaque)",[10255,10256],"polyline",{"points":10257,"fill":72,"stroke":167,"strokeWidth":10258},"60,290 170,290 170,180 308,180","2.5",[150,10260,10263],{"x":6669,"y":10261,"style":10262},"318","text-anchor:middle;fill:currentColor;font:12px sans-serif;opacity:0.85","discrete: snaps at the end",[171,10265],{"x":10266,"y":4172,"width":2613,"height":2601,"rx":5894,"fill":177,"opacity":10267},"376","0.08",[171,10269],{"x":10266,"y":4172,"width":2613,"height":2601,"rx":5894,"fill":72,"stroke":177,"strokeWidth":168},[150,10271,10274],{"x":10272,"y":1823,"style":10273},"536","text-anchor:middle;fill:#7aa2ff;font:600 15px sans-serif","Registered",[150,10276,10190],{"x":10272,"y":2614,"style":1799},[150,10278,10279],{"x":10272,"y":5123,"style":10252},"type is known to the engine",[163,10281],{"x1":10282,"y1":10283,"x2":10284,"y2":2602,"stroke":177,"strokeWidth":1579},"412","290","660",[150,10286,10287],{"x":10272,"y":10261,"style":10262},"smooth: interpolates frame by frame",[47,10289],{},[50,10291,10293],{"id":10292},"a-complete-working-implementation-a-rotating-conic-gradient-ring","A complete working implementation: a rotating conic gradient ring",[14,10295,10296,10297,10299,10300,10303],{},"The classic demonstration is a conic-gradient border whose angle animates — impossible to animate before ",[18,10298,10151],{}," because gradient angles live inside a string the browser could not interpolate. Here a single registered ",[18,10301,10302],{},"--angle"," drives the rotation on hover.",[281,10305,10307],{"className":283,"code":10306,"language":285,"meta":286,"style":286},"\u003Cbutton class=\"ring\" type=\"button\">Hover for sweep\u003C\u002Fbutton>\n",[18,10308,10309],{"__ignoreMap":286},[290,10310,10311,10313,10315,10317,10319,10322,10324,10326,10328,10331,10333],{"class":163,"line":292},[290,10312,296],{"class":295},[290,10314,300],{"class":299},[290,10316,314],{"class":303},[290,10318,307],{"class":295},[290,10320,10321],{"class":310},"\"ring\"",[290,10323,393],{"class":303},[290,10325,307],{"class":295},[290,10327,398],{"class":310},[290,10329,10330],{"class":295},">Hover for sweep\u003C\u002F",[290,10332,300],{"class":299},[290,10334,327],{"class":295},[281,10336,10338],{"className":438,"code":10337,"language":440,"meta":286,"style":286},"\u002F* Register the property so the angle becomes a typed, animatable value.\n   All three descriptors are mandatory for a valid @property rule. *\u002F\n@property --angle {\n  syntax: \"\u003Cangle>\";   \u002F* tells the engine to interpolate as an angle *\u002F\n  inherits: false;     \u002F* stays local to the element it is set on *\u002F\n  initial-value: 0deg; \u002F* a guaranteed-valid starting value *\u002F\n}\n\n.ring {\n  --angle: 0deg;\n  position: relative;\n  padding: 0.75rem 1.5rem;\n  border: none;\n  border-radius: 999px;\n  background: #1118;\n  color: #fff;\n  \u002F* The transition now animates because --angle is typed *\u002F\n  transition: --angle 800ms cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n\u002F* Conic gradient used as an animated border via a padding-box mask *\u002F\n.ring::before {\n  content: \"\";\n  position: absolute;\n  inset: -2px;\n  border-radius: inherit;\n  padding: 2px;\n  background: conic-gradient(\n    from var(--angle),\n    #7aa2ff,\n    #c084fc,\n    #7aa2ff\n  );\n  \u002F* Show only the 2px border ring, not the fill *\u002F\n  -webkit-mask:\n    linear-gradient(#000 0 0) content-box,\n    linear-gradient(#000 0 0);\n  -webkit-mask-composite: xor;\n          mask-composite: exclude;\n}\n\n.ring:hover,\n.ring:focus-visible {\n  --angle: 360deg; \u002F* one full sweep; interpolated smoothly *\u002F\n}\n\n\u002F* Honour reduced-motion: keep the visual, drop the sweep *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .ring { transition: none; }\n}\n",[18,10339,10340,10345,10350,10357,10371,10379,10390,10394,10398,10405,10419,10429,10447,10457,10470,10481,10492,10497,10529,10533,10537,10542,10549,10559,10569,10581,10591,10603,10614,10627,10634,10641,10646,10650,10655,10662,10683,10697,10705,10717,10721,10725,10732,10739,10754,10758,10762,10767,10773,10788],{"__ignoreMap":286},[290,10341,10342],{"class":163,"line":292},[290,10343,10344],{"class":455},"\u002F* Register the property so the angle becomes a typed, animatable value.\n",[290,10346,10347],{"class":163,"line":330},[290,10348,10349],{"class":455},"   All three descriptors are mandatory for a valid @property rule. *\u002F\n",[290,10351,10352,10354],{"class":163,"line":337},[290,10353,10151],{"class":541},[290,10355,10356],{"class":295}," --angle {\n",[290,10358,10359,10362,10365,10368],{"class":163,"line":364},[290,10360,10361],{"class":295},"  syntax: \"\u003Cangle",[290,10363,10364],{"class":541},">",[290,10366,10367],{"class":295},"\";   ",[290,10369,10370],{"class":455},"\u002F* tells the engine to interpolate as an angle *\u002F\n",[290,10372,10373,10376],{"class":163,"line":386},[290,10374,10375],{"class":295},"  inherits: false;     ",[290,10377,10378],{"class":455},"\u002F* stays local to the element it is set on *\u002F\n",[290,10380,10381,10384,10387],{"class":163,"line":408},[290,10382,10383],{"class":299},"  initial-value",[290,10385,10386],{"class":295},": 0deg; ",[290,10388,10389],{"class":455},"\u002F* a guaranteed-valid starting value *\u002F\n",[290,10391,10392],{"class":163,"line":428},[290,10393,620],{"class":295},[290,10395,10396],{"class":163,"line":517},[290,10397,334],{"emptyLinePlaceholder":333},[290,10399,10400,10403],{"class":163,"line":523},[290,10401,10402],{"class":303},".ring",[290,10404,450],{"class":295},[290,10406,10407,10410,10412,10414,10417],{"class":163,"line":532},[290,10408,10409],{"class":1561},"  --angle",[290,10411,465],{"class":295},[290,10413,487],{"class":461},[290,10415,10416],{"class":541},"deg",[290,10418,471],{"class":295},[290,10420,10421,10423,10425,10427],{"class":163,"line":551},[290,10422,1866],{"class":461},[290,10424,465],{"class":295},[290,10426,1871],{"class":461},[290,10428,471],{"class":295},[290,10430,10431,10434,10436,10438,10440,10443,10445],{"class":163,"line":586},[290,10432,10433],{"class":461},"  padding",[290,10435,465],{"class":295},[290,10437,823],{"class":461},[290,10439,801],{"class":541},[290,10441,10442],{"class":461}," 1.5",[290,10444,801],{"class":541},[290,10446,471],{"class":295},[290,10448,10449,10451,10453,10455],{"class":163,"line":602},[290,10450,1948],{"class":461},[290,10452,465],{"class":295},[290,10454,72],{"class":461},[290,10456,471],{"class":295},[290,10458,10459,10461,10463,10466,10468],{"class":163,"line":617},[290,10460,1663],{"class":461},[290,10462,465],{"class":295},[290,10464,10465],{"class":461},"999",[290,10467,674],{"class":541},[290,10469,471],{"class":295},[290,10471,10472,10474,10476,10479],{"class":163,"line":623},[290,10473,1186],{"class":461},[290,10475,465],{"class":295},[290,10477,10478],{"class":461},"#1118",[290,10480,471],{"class":295},[290,10482,10483,10486,10488,10490],{"class":163,"line":628},[290,10484,10485],{"class":461},"  color",[290,10487,465],{"class":295},[290,10489,9138],{"class":461},[290,10491,471],{"class":295},[290,10493,10494],{"class":163,"line":634},[290,10495,10496],{"class":455},"  \u002F* The transition now animates because --angle is typed *\u002F\n",[290,10498,10499,10501,10504,10507,10509,10511,10513,10515,10517,10519,10521,10523,10525,10527],{"class":163,"line":649},[290,10500,526],{"class":461},[290,10502,10503],{"class":295},": --angle ",[290,10505,10506],{"class":461},"800",[290,10508,542],{"class":541},[290,10510,561],{"class":461},[290,10512,484],{"class":295},[290,10514,169],{"class":461},[290,10516,569],{"class":295},[290,10518,487],{"class":461},[290,10520,569],{"class":295},[290,10522,566],{"class":461},[290,10524,569],{"class":295},[290,10526,468],{"class":461},[290,10528,500],{"class":295},[290,10530,10531],{"class":163,"line":660},[290,10532,620],{"class":295},[290,10534,10535],{"class":163,"line":688},[290,10536,334],{"emptyLinePlaceholder":333},[290,10538,10539],{"class":163,"line":693},[290,10540,10541],{"class":455},"\u002F* Conic gradient used as an animated border via a padding-box mask *\u002F\n",[290,10543,10544,10547],{"class":163,"line":698},[290,10545,10546],{"class":303},".ring::before",[290,10548,450],{"class":295},[290,10550,10551,10553,10555,10557],{"class":163,"line":704},[290,10552,1911],{"class":461},[290,10554,465],{"class":295},[290,10556,1916],{"class":310},[290,10558,471],{"class":295},[290,10560,10561,10563,10565,10567],{"class":163,"line":710},[290,10562,1866],{"class":461},[290,10564,465],{"class":295},[290,10566,1927],{"class":461},[290,10568,471],{"class":295},[290,10570,10571,10573,10575,10577,10579],{"class":163,"line":717},[290,10572,1934],{"class":461},[290,10574,465],{"class":295},[290,10576,3561],{"class":461},[290,10578,674],{"class":541},[290,10580,471],{"class":295},[290,10582,10583,10585,10587,10589],{"class":163,"line":730},[290,10584,1663],{"class":461},[290,10586,465],{"class":295},[290,10588,1970],{"class":461},[290,10590,471],{"class":295},[290,10592,10593,10595,10597,10599,10601],{"class":163,"line":742},[290,10594,10433],{"class":461},[290,10596,465],{"class":295},[290,10598,194],{"class":461},[290,10600,674],{"class":541},[290,10602,471],{"class":295},[290,10604,10605,10607,10609,10612],{"class":163,"line":768},[290,10606,1186],{"class":461},[290,10608,465],{"class":295},[290,10610,10611],{"class":461},"conic-gradient",[290,10613,7322],{"class":295},[290,10615,10616,10619,10621,10623,10625],{"class":163,"line":774},[290,10617,10618],{"class":541},"    from",[290,10620,1635],{"class":461},[290,10622,484],{"class":295},[290,10624,10302],{"class":1561},[290,10626,583],{"class":295},[290,10628,10629,10632],{"class":163,"line":779},[290,10630,10631],{"class":461},"    #7aa2ff",[290,10633,548],{"class":295},[290,10635,10636,10639],{"class":163,"line":784},[290,10637,10638],{"class":461},"    #c084fc",[290,10640,548],{"class":295},[290,10642,10643],{"class":163,"line":812},[290,10644,10645],{"class":461},"    #7aa2ff\n",[290,10647,10648],{"class":163,"line":860},[290,10649,8205],{"class":295},[290,10651,10652],{"class":163,"line":865},[290,10653,10654],{"class":455},"  \u002F* Show only the 2px border ring, not the fill *\u002F\n",[290,10656,10657,10660],{"class":163,"line":871},[290,10658,10659],{"class":461},"  -webkit-mask",[290,10661,529],{"class":295},[290,10663,10664,10667,10669,10672,10674,10676,10678,10681],{"class":163,"line":880},[290,10665,10666],{"class":461},"    linear-gradient",[290,10668,484],{"class":295},[290,10670,10671],{"class":461},"#000",[290,10673,1198],{"class":461},[290,10675,1198],{"class":461},[290,10677,490],{"class":295},[290,10679,10680],{"class":461},"content-box",[290,10682,548],{"class":295},[290,10684,10685,10687,10689,10691,10693,10695],{"class":163,"line":896},[290,10686,10666],{"class":461},[290,10688,484],{"class":295},[290,10690,10671],{"class":461},[290,10692,1198],{"class":461},[290,10694,1198],{"class":461},[290,10696,500],{"class":295},[290,10698,10699,10702],{"class":163,"line":4734},[290,10700,10701],{"class":461},"  -webkit-mask-composite",[290,10703,10704],{"class":295},": xor;\n",[290,10706,10707,10710,10712,10715],{"class":163,"line":4742},[290,10708,10709],{"class":461},"          mask-composite",[290,10711,465],{"class":295},[290,10713,10714],{"class":461},"exclude",[290,10716,471],{"class":295},[290,10718,10719],{"class":163,"line":4761},[290,10720,620],{"class":295},[290,10722,10723],{"class":163,"line":4766},[290,10724,334],{"emptyLinePlaceholder":333},[290,10726,10727,10730],{"class":163,"line":4786},[290,10728,10729],{"class":303},".ring:hover",[290,10731,548],{"class":295},[290,10733,10734,10737],{"class":163,"line":9635},[290,10735,10736],{"class":303},".ring:focus-visible",[290,10738,450],{"class":295},[290,10740,10741,10743,10745,10747,10749,10751],{"class":163,"line":9641},[290,10742,10409],{"class":1561},[290,10744,465],{"class":295},[290,10746,152],{"class":461},[290,10748,10416],{"class":541},[290,10750,828],{"class":295},[290,10752,10753],{"class":455},"\u002F* one full sweep; interpolated smoothly *\u002F\n",[290,10755,10756],{"class":163,"line":9647},[290,10757,620],{"class":295},[290,10759,10760],{"class":163,"line":9665},[290,10761,334],{"emptyLinePlaceholder":333},[290,10763,10764],{"class":163,"line":9683},[290,10765,10766],{"class":455},"\u002F* Honour reduced-motion: keep the visual, drop the sweep *\u002F\n",[290,10768,10769,10771],{"class":163,"line":9688},[290,10770,874],{"class":541},[290,10772,877],{"class":295},[290,10774,10775,10778,10780,10782,10784,10786],{"class":163,"line":9694},[290,10776,10777],{"class":303},"  .ring",[290,10779,790],{"class":295},[290,10781,887],{"class":461},[290,10783,465],{"class":295},[290,10785,72],{"class":461},[290,10787,809],{"class":295},[290,10789,10790],{"class":163,"line":9700},[290,10791,620],{"class":295},[14,10793,10794,10795,10797,10798,80,10801,10804,10805,10808,10809,10811],{},"On hover ",[18,10796,10302],{}," glides from ",[18,10799,10800],{},"0deg",[18,10802,10803],{},"360deg",", and because the gradient reads ",[18,10806,10807],{},"from var(--angle)",", the colored ring rotates a full turn. Remove the ",[18,10810,10151],{}," block and the ring would jump straight to its final orientation with no rotation.",[47,10813],{},[50,10815,10817],{"id":10816},"the-key-technique-syntax-is-what-unlocks-interpolation","The key technique: syntax is what unlocks interpolation",[14,10819,10820,10821,10823,10824,10827,10828,10830,10831,10834,10835,10838,10839,2351,10842,10845,10846,10849,10850,10853,10854,10856,10857,10859],{},"The load-bearing descriptor is ",[18,10822,10226],{},". It is not documentation — it is the instruction that tells the browser ",[86,10825,10826],{},"how"," to compute intermediate values. With ",[18,10829,10190],{}," the engine knows to step degrees; with ",[18,10832,10833],{},"\"\u003Cnumber>\""," it steps a scalar; with ",[18,10836,10837],{},"\"\u003Ccolor>\""," it interpolates in the appropriate color space; with ",[18,10840,10841],{},"\"\u003Clength>\"",[18,10843,10844],{},"\"\u003Cpercentage>\""," it interpolates dimensions. The other two descriptors support that: ",[18,10847,10848],{},"inherits"," decides whether the typed value cascades to descendants (set ",[18,10851,10852],{},"false"," for component-local effects to avoid surprise inheritance), and ",[18,10855,10194],{}," guarantees the property always resolves to a valid value of the declared type, which is required so that animations and ",[18,10858,3693],{}," never encounter an invalid token.",[47,10861],{},[50,10863,10865],{"id":10864},"variation-counting-a-number-and-animating-a-hue","Variation: counting a number and animating a hue",[14,10867,10868],{},"The same registration unlocks a CSS-only counter and a hue sweep. Two registered properties of different types drive distinct effects from one hover.",[281,10870,10872],{"className":438,"code":10871,"language":440,"meta":286,"style":286},"@property --count {\n  syntax: \"\u003Cinteger>\";\n  inherits: false;\n  initial-value: 0;\n}\n@property --hue {\n  syntax: \"\u003Cangle>\";\n  inherits: false;\n  initial-value: 0deg;\n}\n\n.stat {\n  --count: 0;\n  --hue: 0deg;\n  \u002F* counter() reads the integer; the hue feeds an hsl() color *\u002F\n  counter-reset: n var(--count);\n  color: hsl(var(--hue) 80% 60%);\n  transition: --count 1200ms steps(60, jump-end), --hue 1200ms ease;\n}\n.stat::after { content: counter(n); }\n\n.stat:hover {\n  --count: 100; \u002F* ticks up to 100 *\u002F\n  --hue: 280deg; \u002F* glides across the hue wheel *\u002F\n}\n",[18,10873,10874,10881,10891,10896,10903,10907,10914,10922,10926,10933,10937,10941,10948,10959,10972,10977,10994,11026,11056,11060,11082,11086,11093,11106,11122],{"__ignoreMap":286},[290,10875,10876,10878],{"class":163,"line":292},[290,10877,10151],{"class":541},[290,10879,10880],{"class":295}," --count {\n",[290,10882,10883,10886,10888],{"class":163,"line":330},[290,10884,10885],{"class":295},"  syntax: \"\u003Cinteger",[290,10887,10364],{"class":541},[290,10889,10890],{"class":295},"\";\n",[290,10892,10893],{"class":163,"line":337},[290,10894,10895],{"class":295},"  inherits: false;\n",[290,10897,10898,10900],{"class":163,"line":364},[290,10899,10383],{"class":299},[290,10901,10902],{"class":295},": 0;\n",[290,10904,10905],{"class":163,"line":386},[290,10906,620],{"class":295},[290,10908,10909,10911],{"class":163,"line":408},[290,10910,10151],{"class":541},[290,10912,10913],{"class":295}," --hue {\n",[290,10915,10916,10918,10920],{"class":163,"line":428},[290,10917,10361],{"class":295},[290,10919,10364],{"class":541},[290,10921,10890],{"class":295},[290,10923,10924],{"class":163,"line":517},[290,10925,10895],{"class":295},[290,10927,10928,10930],{"class":163,"line":523},[290,10929,10383],{"class":299},[290,10931,10932],{"class":295},": 0deg;\n",[290,10934,10935],{"class":163,"line":532},[290,10936,620],{"class":295},[290,10938,10939],{"class":163,"line":551},[290,10940,334],{"emptyLinePlaceholder":333},[290,10942,10943,10946],{"class":163,"line":586},[290,10944,10945],{"class":303},".stat",[290,10947,450],{"class":295},[290,10949,10950,10953,10955,10957],{"class":163,"line":602},[290,10951,10952],{"class":1561},"  --count",[290,10954,465],{"class":295},[290,10956,487],{"class":461},[290,10958,471],{"class":295},[290,10960,10961,10964,10966,10968,10970],{"class":163,"line":617},[290,10962,10963],{"class":1561},"  --hue",[290,10965,465],{"class":295},[290,10967,487],{"class":461},[290,10969,10416],{"class":541},[290,10971,471],{"class":295},[290,10973,10974],{"class":163,"line":623},[290,10975,10976],{"class":455},"  \u002F* counter() reads the integer; the hue feeds an hsl() color *\u002F\n",[290,10978,10979,10982,10985,10987,10989,10992],{"class":163,"line":628},[290,10980,10981],{"class":461},"  counter-reset",[290,10983,10984],{"class":295},": n ",[290,10986,1622],{"class":461},[290,10988,484],{"class":295},[290,10990,10991],{"class":1561},"--count",[290,10993,500],{"class":295},[290,10995,10996,10998,11000,11003,11005,11007,11009,11012,11014,11016,11019,11022,11024],{"class":163,"line":634},[290,10997,10485],{"class":461},[290,10999,465],{"class":295},[290,11001,11002],{"class":461},"hsl",[290,11004,484],{"class":295},[290,11006,1622],{"class":461},[290,11008,484],{"class":295},[290,11010,11011],{"class":1561},"--hue",[290,11013,490],{"class":295},[290,11015,9105],{"class":461},[290,11017,11018],{"class":541},"%",[290,11020,11021],{"class":461}," 60",[290,11023,11018],{"class":541},[290,11025,500],{"class":295},[290,11027,11028,11030,11033,11036,11038,11041,11043,11045,11048,11050,11052,11054],{"class":163,"line":649},[290,11029,526],{"class":461},[290,11031,11032],{"class":295},": --count ",[290,11034,11035],{"class":461},"1200",[290,11037,542],{"class":541},[290,11039,11040],{"class":461}," steps",[290,11042,484],{"class":295},[290,11044,1786],{"class":461},[290,11046,11047],{"class":295},", jump-end), --hue ",[290,11049,11035],{"class":461},[290,11051,542],{"class":541},[290,11053,545],{"class":461},[290,11055,471],{"class":295},[290,11057,11058],{"class":163,"line":660},[290,11059,620],{"class":295},[290,11061,11062,11065,11067,11070,11072,11075,11077,11080],{"class":163,"line":688},[290,11063,11064],{"class":303},".stat::after",[290,11066,790],{"class":295},[290,11068,11069],{"class":461},"content",[290,11071,465],{"class":295},[290,11073,11074],{"class":461},"counter",[290,11076,484],{"class":295},[290,11078,11079],{"class":1561},"n",[290,11081,1122],{"class":295},[290,11083,11084],{"class":163,"line":693},[290,11085,334],{"emptyLinePlaceholder":333},[290,11087,11088,11091],{"class":163,"line":698},[290,11089,11090],{"class":303},".stat:hover",[290,11092,450],{"class":295},[290,11094,11095,11097,11099,11101,11103],{"class":163,"line":704},[290,11096,10952],{"class":1561},[290,11098,465],{"class":295},[290,11100,165],{"class":461},[290,11102,828],{"class":295},[290,11104,11105],{"class":455},"\u002F* ticks up to 100 *\u002F\n",[290,11107,11108,11110,11112,11115,11117,11119],{"class":163,"line":710},[290,11109,10963],{"class":1561},[290,11111,465],{"class":295},[290,11113,11114],{"class":461},"280",[290,11116,10416],{"class":541},[290,11118,828],{"class":295},[290,11120,11121],{"class":455},"\u002F* glides across the hue wheel *\u002F\n",[290,11123,11124],{"class":163,"line":717},[290,11125,620],{"class":295},[14,11127,1517,11128,11130],{},[18,11129,3971],{}," timing function makes the integer increment in visible ticks rather than blurring through fractional values it cannot display.",[47,11132],{},[50,11134,1299],{"id":1298},[14,11136,11137,11139,11140,11142,11143,11146,11147,11150,11151,11154],{},[18,11138,10151],{}," is supported in Chrome and Edge 85+, Safari 16.4+, and Firefox 128+, giving it broad evergreen coverage by mid-2026. Where it is unavailable the custom property still holds and applies its value correctly — only the ",[86,11141,5178],{}," is lost, so transitions snap instantly instead of interpolating. Treat the motion as a progressive enhancement and, if you want explicit detection, wrap the registration-dependent effect in ",[18,11144,11145],{},"@supports (background: conic-gradient(from var(--angle), red, blue))"," or feature-detect with ",[18,11148,11149],{},"CSS.supports",". The JavaScript ",[18,11152,11153],{},"CSS.registerProperty()"," API is an alternative registration path, but it is not needed for the CSS-only approach shown here.",[47,11156],{},[50,11158,1316],{"id":1315},[14,11160,11161,11164,11165,11167,11168,11170,11171,11173,11174,2351,11177,11179],{},[62,11162,11163],{},"Why won't my CSS variable animate even with a transition set?","\nAn unregistered custom property has the universal ",[18,11166,10174],{}," type for animation purposes, so the browser treats it as discrete and snaps from the start value to the end value. Register it with ",[18,11169,10151],{}," and a typed ",[18,11172,10226],{}," such as ",[18,11175,11176],{},"\u003Cangle>",[18,11178,7005],{},", and the browser will interpolate it smoothly across the transition.",[14,11181,11182,11185,11187,11188,11190,11191,11193,11194,11196],{},[62,11183,11184],{},"What do syntax, inherits, and initial-value do in @property?",[18,11186,10226],{}," declares the value's type so the browser knows how to interpolate it, ",[18,11189,10848],{}," controls whether the registered property cascades to descendant elements, and ",[18,11192,10194],{}," supplies a guaranteed-valid fallback starting value. All three descriptors are required for the ",[18,11195,10151],{}," rule to be valid.",[14,11198,11199,11202,11203,11205,11206,11208],{},[62,11200,11201],{},"Can I register a custom property entirely in CSS without JavaScript?","\nYes. The ",[18,11204,10151],{}," at-rule registers the property purely in CSS, which is all the CSS-only animation needs. The JavaScript ",[18,11207,11153],{}," API is an equivalent alternative but is not required.",[14,11210,11211,11214,11216],{},[62,11212,11213],{},"Which browsers support @property?",[18,11215,10151],{}," is supported in Chrome and Edge 85+, Safari 16.4+, and Firefox 128+. Where it is unavailable the property still holds its value but changes apply instantly rather than animating, so guard the motion as an enhancement.",[47,11218],{},[50,11220,1391],{"id":1390},[1393,11222,11223,11228,11234,11239,11244],{},[1396,11224,11225,11227],{},[27,11226,3355],{"href":10147}," — the parent guide on token structure, cascade layers, and theming.",[1396,11229,11230,11233],{},[27,11231,11232],{"href":4900},"Fluid spacing tokens driving transition durations"," — extend typed tokens into proportional motion.",[1396,11235,11236,11238],{},[27,11237,1406],{"href":1405}," — pick the easing curve for animated properties.",[1396,11240,11241,11243],{},[27,11242,1413],{"href":1412}," — collapse registered-property animations when requested.",[1396,11245,11246,11250],{},[27,11247,11249],{"href":11248},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-query-units-cqi-cqb-explained\u002F","Container query units (cqi, cqb) explained"," — feed container-relative units into registered properties for layout-aware motion.",[1430,11252,11253],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":286,"searchDepth":330,"depth":330,"links":11255},[11256,11257,11258,11259,11260,11261,11262,11263],{"id":10167,"depth":330,"text":10168},{"id":10219,"depth":330,"text":10220},{"id":10292,"depth":330,"text":10293},{"id":10816,"depth":330,"text":10817},{"id":10864,"depth":330,"text":10865},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Use @property to register typed CSS custom properties so they animate. Define syntax, inherits, and initial-value to interpolate gradient angles, numbers, and colors.",{"seoTitle":11266,"datePublished":1447,"dateModified":1447,"faq":11267},"Animate custom properties with @property",[11268,11270,11272,11274],{"q":11163,"a":11269},"An unregistered custom property has the type \u003Ccustom-ident> as far as animation is concerned, so the browser treats it as discrete and snaps from start to end. Register it with @property and a typed syntax such as \u003Cangle> or \u003Cnumber> and the browser will interpolate it smoothly.",{"q":11184,"a":11271},"syntax declares the value's type so the browser knows how to interpolate it, inherits controls whether the registered property cascades to descendants, and initial-value supplies a guaranteed-valid starting value. All three descriptors are required for a valid @property rule.",{"q":11201,"a":11273},"Yes. The @property at-rule registers the property purely in CSS. The JavaScript CSS.registerProperty() API exists as an alternative but is not required for CSS-only animation.",{"q":11213,"a":11275},"@property is supported in Chrome and Edge 85+, Safari 16.4+, and Firefox 128+. Where it is unavailable the property still holds its value but changes apply instantly instead of animating, so guard the effect as an enhancement.","\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fanimating-custom-properties-with-at-property",{"title":10136,"description":11264},"css-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fanimating-custom-properties-with-at-property\u002Findex","dX-mwrl0n_p7KY4N0ogMWI0cWn_65mIYDP_b-BXwB-4",{"id":11281,"title":11282,"body":11283,"description":12386,"extension":1444,"meta":12387,"navigation":333,"path":12398,"seo":12399,"stem":12400,"__hash__":12401},"content\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Ffluid-spacing-tokens-driving-transition-durations\u002Findex.md","Fluid Spacing Tokens That Drive Transition Durations",{"type":7,"value":11284,"toc":12376},[11285,11288,11298,11301,11303,11307,11320,11326,11328,11332,11335,11393,11395,11399,11405,11493,11967,11987,12029,12031,12035,12076,12078,12082,12096,12261,12264,12266,12268,12287,12289,12291,12297,12319,12328,12340,12342,12344,12373],[10,11286,11282],{"id":11287},"fluid-spacing-tokens-that-drive-transition-durations",[14,11289,11290,11291,11293,11294,11297],{},"Motion that feels right on a compact button can feel slow and heavy on a wide hero card, even with the identical duration, because the larger element travels farther for the same animation. The usual fix is to hand-tune durations per component, which scatters magic numbers and drifts out of sync the moment spacing changes. This guide, part of the ",[27,11292,3355],{"href":10147}," section, takes a different route: derive both spacing and transition durations from one shared set of fluid tokens, so when an element grows with the viewport or its container, its motion scales in proportion automatically. It deliberately reaches across into the responsive-sizing work in ",[27,11295,11296],{"href":5777},"Mastering Container Queries & Responsive Layouts",", because the tokens that size your layout are the same ones that should pace your motion.",[14,11299,11300],{},"The narrow scenario: a design system where padding, gaps, and travel distances scale fluidly, and you want the animation timing to track that scale instead of being a separate, manually maintained set of constants.",[47,11302],{},[50,11304,11306],{"id":11305},"why-couple-duration-to-the-spacing-scale","Why couple duration to the spacing scale",[14,11308,11309,11310,11314,11315,11319],{},"Perceived speed is not about absolute milliseconds; it is about velocity relative to size. A thumb that slides 24px in 200ms reads as snappy. A panel that slides 240px in the same 200ms reads as a blur, while at a comfortable velocity it would want closer to 360ms. If durations are hard-coded independently of size, every responsive breakpoint and every container-driven layout change silently desynchronises motion from geometry. By sourcing duration from the same fluid tokens that compute spacing — the scale built with techniques from ",[27,11311,11313],{"href":11312},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002F","fluid typography with clamp()"," and the space-scale approach in ",[27,11316,11318],{"href":11317},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-space-scale-with-clamp\u002F","fluid space scale with clamp()"," — the relationship holds at every size with no per-component tuning.",[14,11321,11322,11323,11325],{},"The approach is pure CSS and stays declarative, which is its main advantage over a JavaScript layer that measures elements and sets durations at runtime: there is nothing to recompute on resize, nothing to debounce, and no main-thread cost. The tradeoff is arithmetic discipline — you must convert lengths into unitless ratios before multiplying them into a time, and you must keep the whole thing collapsible for ",[27,11324,10214],{"href":1412},". When proportional motion would be a distraction rather than a help (dense data tables, rapidly repeating toggles), keep durations flat instead.",[47,11327],{},[50,11329,11331],{"id":11330},"one-token-two-outputs","One token, two outputs",[14,11333,11334],{},"The diagram shows the fan-out: a single fluid scale factor feeds both the spacing applied to an element and the duration of its transition, keeping the two locked together as the factor changes with viewport or container size.",[133,11336,140,11338,140,11341,140,11344,140,140,11347,140,11350,140,11352,140,11355,140,140,11358,140,11362,140,140,11365,140,11368,140,11371,140,140,11374,140,11376,140,11379,140,11382,140,11386],{"viewBox":135,"role":136,"ariaLabel":11337,"xmlns":138,"style":139},"A shared fluid token fanning out to drive both spacing and transition duration",[142,11339,11340],{},"Shared token driving spacing and duration",[146,11342,11343],{},"A central fluid scale token branches to two outputs: spacing computed with clamp and cqi, and transition duration computed from the same factor, keeping motion proportional to layout.",[150,11345,11346],{"x":152,"y":2598,"style":154},"One scale, proportional spacing and motion",[171,11348],{"x":11114,"y":1786,"width":1830,"height":4172,"rx":836,"fill":177,"opacity":11349},"0.22",[171,11351],{"x":11114,"y":1786,"width":1830,"height":4172,"rx":836,"fill":72,"stroke":177,"strokeWidth":168},[150,11353,11354],{"x":152,"y":159,"style":2606},"--scale",[150,11356,11357],{"x":152,"y":6645,"style":1799},"clamp() \u002F cqi",[253,11359],{"d":11360,"fill":72,"stroke":167,"strokeWidth":194,"markerEnd":11361},"M320 118 C 280 165, 200 165, 170 200","url(#a2)",[253,11363],{"d":11364,"fill":72,"stroke":167,"strokeWidth":194,"markerEnd":11361},"M400 118 C 440 165, 520 165, 550 200",[171,11366],{"x":1786,"y":11367,"width":538,"height":5905,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},"206",[150,11369,11370],{"x":1822,"y":6729,"style":5883},"spacing",[150,11372,11373],{"x":1822,"y":6641,"style":1799},"padding \u002F gap \u002F translate",[171,11375],{"x":4194,"y":11367,"width":538,"height":5905,"rx":836,"fill":72,"stroke":177,"strokeWidth":168},[150,11377,6940],{"x":1812,"y":6729,"style":11378},"text-anchor:middle;fill:#7aa2ff;font:600 14px sans-serif",[150,11380,11381],{"x":1812,"y":6641,"style":1799},"calc(--scale * base)",[150,11383,11385],{"x":152,"y":10261,"style":11384},"text-anchor:middle;fill:currentColor;font:13px sans-serif;opacity:0.75","larger element, longer travel, longer time, same felt velocity",[240,11387,242,11388,140],{},[244,11389,251,11391,242],{"id":11390,"markerWidth":247,"markerHeight":247,"refX":248,"refY":249,"orient":250},"a2",[253,11392],{"d":255,"fill":167},[47,11394],{},[50,11396,11398],{"id":11397},"a-complete-working-implementation-a-container-aware-card","A complete working implementation: a container-aware card",[14,11400,11401,11402,42],{},"The card sizes its padding and lift distance from a fluid scale, then derives its hover duration from the same scale so a card rendered in a wide column animates over a longer, proportionate time than the same component in a narrow sidebar. The card queries its own container, so the scale tracks the container width via ",[18,11403,11404],{},"cqi",[281,11406,11408],{"className":283,"code":11407,"language":285,"meta":286,"style":286},"\u003Cdiv class=\"grid\">\n  \u003Cdiv class=\"card-wrap\">\n    \u003Carticle class=\"card\">\u003Ch3>Proportional motion\u003C\u002Fh3>\u003Cp>Resize me.\u003C\u002Fp>\u003C\u002Farticle>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n",[18,11409,11410,11424,11439,11477,11485],{"__ignoreMap":286},[290,11411,11412,11414,11416,11418,11420,11422],{"class":163,"line":292},[290,11413,296],{"class":295},[290,11415,342],{"class":299},[290,11417,314],{"class":303},[290,11419,307],{"class":295},[290,11421,9274],{"class":310},[290,11423,327],{"class":295},[290,11425,11426,11428,11430,11432,11434,11437],{"class":163,"line":330},[290,11427,367],{"class":295},[290,11429,342],{"class":299},[290,11431,314],{"class":303},[290,11433,307],{"class":295},[290,11435,11436],{"class":310},"\"card-wrap\"",[290,11438,327],{"class":295},[290,11440,11441,11443,11446,11448,11450,11452,11454,11456,11459,11461,11463,11465,11468,11470,11473,11475],{"class":163,"line":337},[290,11442,4290],{"class":295},[290,11444,11445],{"class":299},"article",[290,11447,314],{"class":303},[290,11449,307],{"class":295},[290,11451,9295],{"class":310},[290,11453,9303],{"class":295},[290,11455,2757],{"class":299},[290,11457,11458],{"class":295},">Proportional motion\u003C\u002F",[290,11460,2757],{"class":299},[290,11462,9303],{"class":295},[290,11464,14],{"class":299},[290,11466,11467],{"class":295},">Resize me.\u003C\u002F",[290,11469,14],{"class":299},[290,11471,11472],{"class":295},">\u003C\u002F",[290,11474,11445],{"class":299},[290,11476,327],{"class":295},[290,11478,11479,11481,11483],{"class":163,"line":364},[290,11480,4315],{"class":295},[290,11482,342],{"class":299},[290,11484,327],{"class":295},[290,11486,11487,11489,11491],{"class":163,"line":386},[290,11488,431],{"class":295},[290,11490,342],{"class":299},[290,11492,327],{"class":295},[281,11494,11496],{"className":438,"code":11495,"language":440,"meta":286,"style":286},".card-wrap {\n  container-type: inline-size; \u002F* establishes the query container *\u002F\n}\n\n.card {\n  \u002F* --scale: a unitless ratio from ~1 (narrow) to ~2 (wide).\n     cqi is 1% of the container's inline size; we map a width band\n     into a ratio with clamp() so it never runs away. *\u002F\n  --scale: clamp(1, 0.6 + 2cqi \u002F 100, 2);\n\n  \u002F* Spacing derived from the scale *\u002F\n  --pad: calc(0.75rem * var(--scale));\n  --lift: calc(8px * var(--scale)); \u002F* how far the card rises on hover *\u002F\n\n  \u002F* Duration derived from the SAME scale, converted to ms.\n     Base 140ms, stretched proportionally to the scale factor. *\u002F\n  --dur: calc(140ms * var(--scale));\n\n  padding: var(--pad);\n  border: 1px solid currentColor;\n  border-radius: 12px;\n  transform: translateY(0);\n  transition:\n    transform var(--dur) cubic-bezier(0.2, 0.8, 0.2, 1),\n    box-shadow var(--dur) ease;\n}\n\n.card:hover,\n.card:focus-within {\n  transform: translateY(calc(-1 * var(--lift)));\n  box-shadow: 0 10px 28px rgb(0 0 0 \u002F 0.18);\n}\n\n\u002F* One override disables proportional motion everywhere *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .card { --dur: 0.01ms; }\n}\n\n.grid { display: grid; gap: 1rem; grid-template-columns: 1fr; }\n",[18,11497,11498,11505,11516,11520,11524,11531,11536,11541,11546,11582,11586,11591,11617,11646,11650,11655,11660,11685,11689,11704,11720,11732,11746,11752,11786,11802,11806,11810,11817,11824,11852,11887,11891,11895,11900,11906,11922,11926,11930],{"__ignoreMap":286},[290,11499,11500,11503],{"class":163,"line":292},[290,11501,11502],{"class":303},".card-wrap",[290,11504,450],{"class":295},[290,11506,11507,11510,11513],{"class":163,"line":330},[290,11508,11509],{"class":461},"  container-type",[290,11511,11512],{"class":295},": inline-size; ",[290,11514,11515],{"class":455},"\u002F* establishes the query container *\u002F\n",[290,11517,11518],{"class":163,"line":337},[290,11519,620],{"class":295},[290,11521,11522],{"class":163,"line":364},[290,11523,334],{"emptyLinePlaceholder":333},[290,11525,11526,11529],{"class":163,"line":386},[290,11527,11528],{"class":303},".card",[290,11530,450],{"class":295},[290,11532,11533],{"class":163,"line":408},[290,11534,11535],{"class":455},"  \u002F* --scale: a unitless ratio from ~1 (narrow) to ~2 (wide).\n",[290,11537,11538],{"class":163,"line":428},[290,11539,11540],{"class":455},"     cqi is 1% of the container's inline size; we map a width band\n",[290,11542,11543],{"class":163,"line":517},[290,11544,11545],{"class":455},"     into a ratio with clamp() so it never runs away. *\u002F\n",[290,11547,11548,11551,11553,11556,11558,11560,11562,11564,11566,11568,11570,11573,11576,11578,11580],{"class":163,"line":523},[290,11549,11550],{"class":1561},"  --scale",[290,11552,465],{"class":295},[290,11554,11555],{"class":461},"clamp",[290,11557,484],{"class":295},[290,11559,468],{"class":461},[290,11561,569],{"class":295},[290,11563,232],{"class":461},[290,11565,3592],{"class":1561},[290,11567,3290],{"class":461},[290,11569,11404],{"class":541},[290,11571,11572],{"class":1561}," \u002F",[290,11574,11575],{"class":461}," 100",[290,11577,569],{"class":295},[290,11579,194],{"class":461},[290,11581,500],{"class":295},[290,11583,11584],{"class":163,"line":532},[290,11585,334],{"emptyLinePlaceholder":333},[290,11587,11588],{"class":163,"line":551},[290,11589,11590],{"class":455},"  \u002F* Spacing derived from the scale *\u002F\n",[290,11592,11593,11596,11598,11600,11602,11604,11606,11608,11610,11612,11614],{"class":163,"line":586},[290,11594,11595],{"class":1561},"  --pad",[290,11597,465],{"class":295},[290,11599,3556],{"class":461},[290,11601,484],{"class":295},[290,11603,823],{"class":461},[290,11605,801],{"class":541},[290,11607,3566],{"class":541},[290,11609,1635],{"class":461},[290,11611,484],{"class":295},[290,11613,11354],{"class":1561},[290,11615,11616],{"class":295},"));\n",[290,11618,11619,11622,11624,11626,11628,11630,11632,11634,11636,11638,11640,11643],{"class":163,"line":602},[290,11620,11621],{"class":1561},"  --lift",[290,11623,465],{"class":295},[290,11625,3556],{"class":461},[290,11627,484],{"class":295},[290,11629,176],{"class":461},[290,11631,674],{"class":541},[290,11633,3566],{"class":541},[290,11635,1635],{"class":461},[290,11637,484],{"class":295},[290,11639,11354],{"class":1561},[290,11641,11642],{"class":295},")); ",[290,11644,11645],{"class":455},"\u002F* how far the card rises on hover *\u002F\n",[290,11647,11648],{"class":163,"line":617},[290,11649,334],{"emptyLinePlaceholder":333},[290,11651,11652],{"class":163,"line":623},[290,11653,11654],{"class":455},"  \u002F* Duration derived from the SAME scale, converted to ms.\n",[290,11656,11657],{"class":163,"line":628},[290,11658,11659],{"class":455},"     Base 140ms, stretched proportionally to the scale factor. *\u002F\n",[290,11661,11662,11665,11667,11669,11671,11673,11675,11677,11679,11681,11683],{"class":163,"line":634},[290,11663,11664],{"class":1561},"  --dur",[290,11666,465],{"class":295},[290,11668,3556],{"class":461},[290,11670,484],{"class":295},[290,11672,4195],{"class":461},[290,11674,542],{"class":541},[290,11676,3566],{"class":541},[290,11678,1635],{"class":461},[290,11680,484],{"class":295},[290,11682,11354],{"class":1561},[290,11684,11616],{"class":295},[290,11686,11687],{"class":163,"line":649},[290,11688,334],{"emptyLinePlaceholder":333},[290,11690,11691,11693,11695,11697,11699,11702],{"class":163,"line":660},[290,11692,10433],{"class":461},[290,11694,465],{"class":295},[290,11696,1622],{"class":461},[290,11698,484],{"class":295},[290,11700,11701],{"class":1561},"--pad",[290,11703,500],{"class":295},[290,11705,11706,11708,11710,11712,11714,11716,11718],{"class":163,"line":688},[290,11707,1948],{"class":461},[290,11709,465],{"class":295},[290,11711,468],{"class":461},[290,11713,674],{"class":541},[290,11715,852],{"class":461},[290,11717,855],{"class":461},[290,11719,471],{"class":295},[290,11721,11722,11724,11726,11728,11730],{"class":163,"line":693},[290,11723,1663],{"class":461},[290,11725,465],{"class":295},[290,11727,5894],{"class":461},[290,11729,674],{"class":541},[290,11731,471],{"class":295},[290,11733,11734,11736,11738,11740,11742,11744],{"class":163,"line":698},[290,11735,476],{"class":461},[290,11737,465],{"class":295},[290,11739,481],{"class":461},[290,11741,484],{"class":295},[290,11743,487],{"class":461},[290,11745,500],{"class":295},[290,11747,11748,11750],{"class":163,"line":704},[290,11749,526],{"class":461},[290,11751,529],{"class":295},[290,11753,11754,11756,11758,11760,11763,11765,11768,11770,11772,11774,11776,11778,11780,11782,11784],{"class":163,"line":710},[290,11755,554],{"class":295},[290,11757,1622],{"class":461},[290,11759,484],{"class":295},[290,11761,11762],{"class":1561},"--dur",[290,11764,490],{"class":295},[290,11766,11767],{"class":461},"cubic-bezier",[290,11769,484],{"class":295},[290,11771,566],{"class":461},[290,11773,569],{"class":295},[290,11775,572],{"class":461},[290,11777,569],{"class":295},[290,11779,566],{"class":461},[290,11781,569],{"class":295},[290,11783,468],{"class":461},[290,11785,583],{"class":295},[290,11787,11788,11790,11792,11794,11796,11798,11800],{"class":163,"line":717},[290,11789,3140],{"class":295},[290,11791,1622],{"class":461},[290,11793,484],{"class":295},[290,11795,11762],{"class":1561},[290,11797,490],{"class":295},[290,11799,6770],{"class":461},[290,11801,471],{"class":295},[290,11803,11804],{"class":163,"line":730},[290,11805,620],{"class":295},[290,11807,11808],{"class":163,"line":742},[290,11809,334],{"emptyLinePlaceholder":333},[290,11811,11812,11815],{"class":163,"line":768},[290,11813,11814],{"class":303},".card:hover",[290,11816,548],{"class":295},[290,11818,11819,11822],{"class":163,"line":774},[290,11820,11821],{"class":303},".card:focus-within",[290,11823,450],{"class":295},[290,11825,11826,11828,11830,11832,11834,11836,11838,11841,11843,11845,11847,11850],{"class":163,"line":779},[290,11827,476],{"class":461},[290,11829,465],{"class":295},[290,11831,481],{"class":461},[290,11833,484],{"class":295},[290,11835,3556],{"class":461},[290,11837,484],{"class":295},[290,11839,11840],{"class":461},"-1",[290,11842,3566],{"class":541},[290,11844,1635],{"class":461},[290,11846,484],{"class":295},[290,11848,11849],{"class":1561},"--lift",[290,11851,3576],{"class":295},[290,11853,11854,11856,11858,11860,11863,11865,11868,11870,11873,11875,11877,11879,11881,11883,11885],{"class":163,"line":784},[290,11855,3207],{"class":461},[290,11857,465],{"class":295},[290,11859,487],{"class":461},[290,11861,11862],{"class":461}," 10",[290,11864,674],{"class":541},[290,11866,11867],{"class":461}," 28",[290,11869,674],{"class":541},[290,11871,11872],{"class":461}," rgb",[290,11874,484],{"class":295},[290,11876,487],{"class":461},[290,11878,1198],{"class":461},[290,11880,1198],{"class":461},[290,11882,1203],{"class":295},[290,11884,178],{"class":461},[290,11886,500],{"class":295},[290,11888,11889],{"class":163,"line":812},[290,11890,620],{"class":295},[290,11892,11893],{"class":163,"line":860},[290,11894,334],{"emptyLinePlaceholder":333},[290,11896,11897],{"class":163,"line":865},[290,11898,11899],{"class":455},"\u002F* One override disables proportional motion everywhere *\u002F\n",[290,11901,11902,11904],{"class":163,"line":871},[290,11903,874],{"class":541},[290,11905,877],{"class":295},[290,11907,11908,11910,11912,11914,11916,11918,11920],{"class":163,"line":880},[290,11909,9083],{"class":303},[290,11911,790],{"class":295},[290,11913,11762],{"class":1561},[290,11915,465],{"class":295},[290,11917,2831],{"class":461},[290,11919,542],{"class":541},[290,11921,809],{"class":295},[290,11923,11924],{"class":163,"line":896},[290,11925,620],{"class":295},[290,11927,11928],{"class":163,"line":4734},[290,11929,334],{"emptyLinePlaceholder":333},[290,11931,11932,11935,11937,11939,11941,11943,11945,11947,11949,11951,11953,11955,11958,11960,11962,11965],{"class":163,"line":4742},[290,11933,11934],{"class":303},".grid",[290,11936,790],{"class":295},[290,11938,59],{"class":461},[290,11940,465],{"class":295},[290,11942,9147],{"class":461},[290,11944,828],{"class":295},[290,11946,9070],{"class":461},[290,11948,465],{"class":295},[290,11950,468],{"class":461},[290,11952,801],{"class":541},[290,11954,828],{"class":295},[290,11956,11957],{"class":461},"grid-template-columns",[290,11959,465],{"class":295},[290,11961,468],{"class":461},[290,11963,11964],{"class":541},"fr",[290,11966,809],{"class":295},[14,11968,11969,11970,11972,11973,11975,11976,569,11978,8393,11980,11982,11983,11986],{},"Because ",[18,11971,11354],{}," is computed once from ",[18,11974,11404],{}," and then feeds ",[18,11977,11701],{},[18,11979,11849],{},[18,11981,11762],{},", widening the container increases the padding, the lift distance, ",[86,11984,11985],{},"and"," the duration together. The card moves farther but over a longer time, so it keeps the same felt velocity at every container width.",[281,11988,11990],{"className":438,"code":11989,"language":440,"meta":286,"style":286},"\u002F* DEMO: two columns so the same component renders at two scales *\u002F\n@container (min-width: 400px) {\n  .grid { grid-template-columns: 1fr 1fr; }\n}\n",[18,11991,11992,11997,12005,12025],{"__ignoreMap":286},[290,11993,11994],{"class":163,"line":292},[290,11995,11996],{"class":455},"\u002F* DEMO: two columns so the same component renders at two scales *\u002F\n",[290,11998,11999,12002],{"class":163,"line":330},[290,12000,12001],{"class":541},"@container",[290,12003,12004],{"class":295}," (min-width: 400px) {\n",[290,12006,12007,12009,12011,12013,12015,12017,12019,12021,12023],{"class":163,"line":337},[290,12008,9046],{"class":303},[290,12010,790],{"class":295},[290,12012,11957],{"class":461},[290,12014,465],{"class":295},[290,12016,468],{"class":461},[290,12018,11964],{"class":541},[290,12020,804],{"class":461},[290,12022,11964],{"class":541},[290,12024,809],{"class":295},[290,12026,12027],{"class":163,"line":364},[290,12028,620],{"class":295},[47,12030],{},[50,12032,12034],{"id":12033},"the-key-technique-convert-length-to-a-ratio-before-timing-it","The key technique: convert length to a ratio before timing it",[14,12036,12037,12038,12041,12042,12044,12045,12047,12048,12050,12051,12054,12055,12057,12058,12060,12061,12063,12064,12067,12068,12070,12071,12075],{},"The one move that makes this work is keeping the scale ",[62,12039,12040],{},"unitless",". A duration must be a ",[18,12043,6923],{},", and you cannot multiply a length (",[18,12046,11404],{}," resolves to pixels) directly by a time. So the token ",[18,12049,11354],{}," deliberately produces a plain number — ",[18,12052,12053],{},"clamp(1, 0.6 + 2cqi \u002F 100, 2)"," divides the container unit down into a bounded ratio. That ratio is then safe to multiply into anything: a ",[18,12056,801],{}," for padding, a ",[18,12059,674],{}," for travel, or a ",[18,12062,542],{}," for duration via ",[18,12065,12066],{},"calc(140ms * var(--scale))",". The shared ratio is the linchpin; spacing and timing are just two ",[18,12069,3693],{}," expressions reading it. This is the same discipline used for ",[27,12072,12074],{"href":12073},"\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Fanimating-custom-properties-with-at-property\u002F","animating custom properties with @property"," — keep the raw value typed and unit-clean, then convert at the point of use.",[47,12077],{},[50,12079,12081],{"id":12080},"variation-viewport-driven-scale-for-full-bleed-sections","Variation: viewport-driven scale for full-bleed sections",[14,12083,12084,12085,12087,12088,12091,12092,12095],{},"Where the element spans the page rather than a container, swap ",[18,12086,11404],{}," for ",[18,12089,12090],{},"vi"," (or ",[18,12093,12094],{},"vw",") so the scale tracks the viewport instead. The rest of the arithmetic is unchanged, which is the point — only the source unit differs.",[281,12097,12099],{"className":438,"code":12098,"language":440,"meta":286,"style":286},".hero {\n  --scale: clamp(1, 0.5 + 1.2vi \u002F 100, 2.4);\n  --pad: calc(1rem * var(--scale));\n  --dur: calc(180ms * var(--scale));\n  padding-block: var(--pad);\n  transition: opacity var(--dur) ease, transform var(--dur) ease;\n}\n@media (prefers-reduced-motion: reduce) { .hero { --dur: 0.01ms; } }\n",[18,12100,12101,12108,12142,12166,12190,12205,12235,12239],{"__ignoreMap":286},[290,12102,12103,12106],{"class":163,"line":292},[290,12104,12105],{"class":303},".hero",[290,12107,450],{"class":295},[290,12109,12110,12112,12114,12116,12118,12120,12122,12124,12126,12129,12131,12133,12135,12137,12140],{"class":163,"line":330},[290,12111,11550],{"class":1561},[290,12113,465],{"class":295},[290,12115,11555],{"class":461},[290,12117,484],{"class":295},[290,12119,468],{"class":461},[290,12121,569],{"class":295},[290,12123,798],{"class":461},[290,12125,3592],{"class":1561},[290,12127,12128],{"class":461}," 1.2",[290,12130,12090],{"class":541},[290,12132,11572],{"class":1561},[290,12134,11575],{"class":461},[290,12136,569],{"class":295},[290,12138,12139],{"class":461},"2.4",[290,12141,500],{"class":295},[290,12143,12144,12146,12148,12150,12152,12154,12156,12158,12160,12162,12164],{"class":163,"line":337},[290,12145,11595],{"class":1561},[290,12147,465],{"class":295},[290,12149,3556],{"class":461},[290,12151,484],{"class":295},[290,12153,468],{"class":461},[290,12155,801],{"class":541},[290,12157,3566],{"class":541},[290,12159,1635],{"class":461},[290,12161,484],{"class":295},[290,12163,11354],{"class":1561},[290,12165,11616],{"class":295},[290,12167,12168,12170,12172,12174,12176,12178,12180,12182,12184,12186,12188],{"class":163,"line":364},[290,12169,11664],{"class":1561},[290,12171,465],{"class":295},[290,12173,3556],{"class":461},[290,12175,484],{"class":295},[290,12177,2602],{"class":461},[290,12179,542],{"class":541},[290,12181,3566],{"class":541},[290,12183,1635],{"class":461},[290,12185,484],{"class":295},[290,12187,11354],{"class":1561},[290,12189,11616],{"class":295},[290,12191,12192,12195,12197,12199,12201,12203],{"class":163,"line":386},[290,12193,12194],{"class":461},"  padding-block",[290,12196,465],{"class":295},[290,12198,1622],{"class":461},[290,12200,484],{"class":295},[290,12202,11701],{"class":1561},[290,12204,500],{"class":295},[290,12206,12207,12209,12211,12213,12215,12217,12219,12221,12223,12225,12227,12229,12231,12233],{"class":163,"line":408},[290,12208,526],{"class":461},[290,12210,6384],{"class":295},[290,12212,1622],{"class":461},[290,12214,484],{"class":295},[290,12216,11762],{"class":1561},[290,12218,490],{"class":295},[290,12220,6770],{"class":461},[290,12222,4679],{"class":295},[290,12224,1622],{"class":461},[290,12226,484],{"class":295},[290,12228,11762],{"class":1561},[290,12230,490],{"class":295},[290,12232,6770],{"class":461},[290,12234,471],{"class":295},[290,12236,12237],{"class":163,"line":428},[290,12238,620],{"class":295},[290,12240,12241,12243,12246,12248,12250,12252,12254,12256,12258],{"class":163,"line":517},[290,12242,874],{"class":541},[290,12244,12245],{"class":295}," (prefers-reduced-motion: reduce) { ",[290,12247,12105],{"class":303},[290,12249,790],{"class":295},[290,12251,11762],{"class":1561},[290,12253,465],{"class":295},[290,12255,2831],{"class":461},[290,12257,542],{"class":541},[290,12259,12260],{"class":295},"; } }\n",[14,12262,12263],{},"A small phone gets snappy, restrained motion; a wide desktop hero gets a longer, more cinematic glide — from one formula.",[47,12265],{},[50,12267,1299],{"id":1298},[14,12269,12270,12271,12273,12274,12277,12278,569,12280,12283,12284,12286],{},"The whole pattern rests on three well-supported features. ",[18,12272,3693],{}," and custom properties are universal across evergreen browsers. ",[18,12275,12276],{},"clamp()"," is supported in Chrome and Edge 79+, Safari 13.1+, and Firefox 75+. Container query units (",[18,12279,11404],{},[18,12281,12282],{},"cqb",") ship alongside size container queries in Chrome and Edge 105+, Safari 16+, and Firefox 110+, so the container-aware variant requires those versions; the viewport-unit variation works in any browser that has ",[18,12285,12276],{},". Because the duration tokens degrade to ordinary fixed times if a unit is unsupported, the fallback is simply non-proportional but otherwise functional motion.",[47,12288],{},[50,12290,1316],{"id":1315},[14,12292,12293,12296],{},[62,12294,12295],{},"Why should transition durations scale with layout size?","\nPerceived speed is velocity relative to size: a larger element travels farther for the same animation, so a fixed duration that feels snappy on a small control reads as sluggish on a big card. Deriving duration from the same fluid tokens that size the element keeps the apparent speed consistent at every breakpoint.",[14,12298,12299,12302,12303,2351,12305,12307,12308,12310,12311,12313,12314,12316,12317,42],{},[62,12300,12301],{},"Can I use cqi inside a custom property that feeds a duration?","\nYes. Compute a unitless ratio from ",[18,12304,11404],{},[18,12306,12276],{}," in one token, then multiply it by a base time with ",[18,12309,3693],{},". The custom property carries the raw ratio and the final ",[18,12312,3693],{}," converts it into a valid ",[18,12315,6923],{}," value such as ",[18,12318,12066],{},[14,12320,12321,12324,12325,12327],{},[62,12322,12323],{},"How do I keep proportional motion accessible?","\nWrap the duration tokens in a ",[18,12326,5557],{}," query and collapse them to a near-zero value. Because every transition reads the shared token, that single override disables proportional motion across the whole system at once.",[14,12329,12330,12333,12334,12336,12337,12339],{},[62,12331,12332],{},"Do container query units work inside calc() for durations?","\nContainer query units such as ",[18,12335,11404],{}," resolve to lengths, so you must convert them to a unitless ratio first — for example by dividing through a reference length inside ",[18,12338,12276],{}," — before multiplying by a base duration. Support matches container query units: Chrome and Edge 105+, Safari 16+, and Firefox 110+.",[47,12341],{},[50,12343,1391],{"id":1390},[1393,12345,12346,12351,12357,12363,12368],{},[1396,12347,12348,12350],{},[27,12349,3355],{"href":10147}," — the parent guide on token structure and theming.",[1396,12352,12353,12356],{},[27,12354,12355],{"href":12073},"Animating custom properties with @property"," — make derived tokens themselves animatable.",[1396,12358,12359,12362],{},[27,12360,12361],{"href":11312},"Fluid Typography with clamp()"," — the cross-area source of the fluid scaling math.",[1396,12364,12365,12367],{},[27,12366,11249],{"href":11248}," — the container-relative units that drive the scale.",[1396,12369,12370,12372],{},[27,12371,1406],{"href":1405}," — pair proportional durations with the right easing.",[1430,12374,12375],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":286,"searchDepth":330,"depth":330,"links":12377},[12378,12379,12380,12381,12382,12383,12384,12385],{"id":11305,"depth":330,"text":11306},{"id":11330,"depth":330,"text":11331},{"id":11397,"depth":330,"text":11398},{"id":12033,"depth":330,"text":12034},{"id":12080,"depth":330,"text":12081},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Drive transition durations and spacing from shared custom-property tokens that scale with clamp() and cqi, so motion stays proportional to layout across screen sizes.",{"seoTitle":12388,"datePublished":1447,"dateModified":1447,"faq":12389},"Fluid Tokens for Spacing & Durations",[12390,12392,12394,12396],{"q":12295,"a":12391},"Distance perception is relative to element size. A fixed 200ms feels right on a small control but sluggish on a large card that moves much farther, so deriving duration from the same tokens that size the element keeps the apparent speed consistent.",{"q":12301,"a":12393},"Yes. Compute a unitless number from cqi or clamp() in one token, then multiply it by a base time with calc() and a duration unit. Custom properties carry the raw value and the final calc() converts it to a valid time.",{"q":12323,"a":12395},"Wrap the duration tokens in a prefers-reduced-motion query and collapse them to a near-zero value. Because every transition reads the shared token, one override disables proportional motion everywhere at once.",{"q":12332,"a":12397},"Container query units like cqi resolve to lengths, so you convert them to a unitless ratio first (for example by dividing through a reference length) before multiplying by a base duration. Browser support matches container query units: Chrome and Edge 105+, Safari 16+, Firefox 110+.","\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Ffluid-spacing-tokens-driving-transition-durations",{"title":11282,"description":12386},"css-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Ffluid-spacing-tokens-driving-transition-durations\u002Findex","SRUQ9Kg5QXYwL1cLEJmoVpHK4KiDbePswhtnQr1r9rI",{"id":12403,"title":12404,"body":12405,"description":13589,"extension":1444,"meta":13590,"navigation":333,"path":13599,"seo":13600,"stem":13601,"__hash__":13602},"content\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Findex.md","CSS Custom Properties Architecture: Scalable Design Systems & Dynamic UI",{"type":7,"value":12406,"toc":13577},[12407,12410,12415,12420,12434,12482,12484,12488,12499,12532,12657,12670,12672,12676,12686,12803,12808,12834,12841,12843,12847,12856,12861,12911,12916,13019,13042,13044,13048,13063,13076,13213,13218,13234,13236,13240,13309,13324,13326,13330,13392,13394,13397,13475,13477,13481,13509,13511,13513,13519,13533,13542,13544,13546,13574],[10,12408,12404],{"id":12409},"css-custom-properties-architecture-scalable-design-systems-dynamic-ui",[14,12411,12412,12413,42],{},"A comprehensive guide to structuring CSS custom properties for scalable, maintainable interfaces. Learn how to implement a robust variable architecture that bridges design tokens with runtime CSS, enabling dynamic theming and seamless integration with modern ",[27,12414,1481],{"href":1480},[14,12416,12417],{},[62,12418,12419],{},"Key Implementation Focus:",[1393,12421,12422,12425,12428,12431],{},[1396,12423,12424],{},"Token-to-Variable Mapping",[1396,12426,12427],{},"Cascade & Scope Management",[1396,12429,12430],{},"Performance-Optimized Theming",[1396,12432,12433],{},"Component Isolation Strategies",[133,12435,140,12437,140,12440,140,12443,140,12446,140,12448,140,12452,140,12457,140,12459,140,12462,140,12465,140,12467,140,12471,140,12474,140,12476,140,12478],{"viewBox":2588,"role":136,"ariaLabel":12436,"xmlns":138,"style":139},"Token flow from primitive tokens through semantic tokens to component variables",[142,12438,12439],{},"Custom property token flow",[146,12441,12442],{},"Primitive tokens map to semantic tokens, which resolve into component-scoped variables at runtime.",[150,12444,12445],{"x":152,"y":2598,"style":154},"Token resolution pipeline",[171,12447],{"x":4146,"y":4147,"width":1822,"height":9105,"rx":836,"fill":177,"opacity":1883,"stroke":167,"strokeWidth":168},[150,12449,12451],{"x":12450,"y":1787,"style":5883},"125","Primitive",[150,12453,12456],{"x":12450,"y":12454,"style":12455},"174","text-anchor:middle;fill:currentColor;font:12px ui-monospace,monospace;opacity:0.9","--color-600",[171,12458],{"x":5127,"y":4147,"width":1822,"height":9105,"rx":836,"fill":177,"opacity":11349,"stroke":167,"strokeWidth":168},[150,12460,12461],{"x":152,"y":1787,"style":5883},"Semantic",[150,12463,12464],{"x":152,"y":12454,"style":12455},"--action-bg",[171,12466],{"x":2626,"y":4147,"width":1822,"height":9105,"rx":836,"fill":177,"opacity":1813,"stroke":167,"strokeWidth":168},[150,12468,12470],{"x":12469,"y":1787,"style":5883},"595","Component",[150,12472,12473],{"x":12469,"y":12454,"style":12455},".btn bg",[163,12475],{"x1":2622,"y1":1830,"x2":5127,"y2":1830,"stroke":177,"strokeWidth":10258},[163,12477],{"x1":5158,"y1":1830,"x2":2626,"y2":1830,"stroke":177,"strokeWidth":10258},[150,12479,12481],{"x":152,"y":12480,"style":1808},"260","var() resolves left to right at use time",[47,12483],{},[50,12485,12487],{"id":12486},"foundational-architecture-naming-conventions","Foundational Architecture & Naming Conventions",[14,12489,12490,12491,12494,12495,12498],{},"A predictable CSS Custom Properties Architecture begins with a strict separation between ",[62,12492,12493],{},"primitive"," (atomic) and ",[62,12496,12497],{},"semantic"," (contextual) tokens. Primitive tokens store raw values (colors, spacing scales, typography metrics), while semantic tokens map those primitives to UI roles. This prevents namespace collisions and enables predictable inheritance across deep component trees.",[14,12500,12501,12502,569,12504,569,12506,12509,12510,12512,12513,12516,12517,569,12520,569,12522,12524,12525,12527,12528,12531],{},"Adopt a BEM\u002FUtility hybrid scoping pattern: prefix variables by category (",[18,12503,9133],{},[18,12505,11370],{},[18,12507,12508],{},"motion","), scale (",[18,12511,165],{},"–",[18,12514,12515],{},"900","), and property (",[18,12518,12519],{},"bg",[18,12521,843],{},[18,12523,150],{},"). Avoid global ",[18,12526,1554],{}," declarations for component-specific values; instead, scope them to component roots or utility classes. Because spacing and motion scales share the same token system, you can even let ",[27,12529,12530],{"href":4900},"fluid spacing tokens drive transition durations",", keeping movement proportional to the responsive layout it animates.",[281,12533,12535],{"className":438,"code":12534,"language":440,"meta":286,"style":286},":root {\n  \u002F* Primitive Tokens *\u002F\n  --color-primary-100: #e0f2fe;\n  --color-primary-600: #0284c7;\n  --spacing-unit: 0.25rem;\n\n  \u002F* Semantic Tokens *\u002F\n  --surface-bg: var(--color-primary-100);\n  --action-bg: var(--color-primary-600);\n  --space-md: calc(var(--spacing-unit) * 4);\n}\n",[18,12536,12537,12543,12548,12560,12572,12585,12589,12594,12610,12626,12653],{"__ignoreMap":286},[290,12538,12539,12541],{"class":163,"line":292},[290,12540,1554],{"class":303},[290,12542,450],{"class":295},[290,12544,12545],{"class":163,"line":330},[290,12546,12547],{"class":455},"  \u002F* Primitive Tokens *\u002F\n",[290,12549,12550,12553,12555,12558],{"class":163,"line":337},[290,12551,12552],{"class":1561},"  --color-primary-100",[290,12554,465],{"class":295},[290,12556,12557],{"class":461},"#e0f2fe",[290,12559,471],{"class":295},[290,12561,12562,12565,12567,12570],{"class":163,"line":364},[290,12563,12564],{"class":1561},"  --color-primary-600",[290,12566,465],{"class":295},[290,12568,12569],{"class":461},"#0284c7",[290,12571,471],{"class":295},[290,12573,12574,12577,12579,12581,12583],{"class":163,"line":386},[290,12575,12576],{"class":1561},"  --spacing-unit",[290,12578,465],{"class":295},[290,12580,1668],{"class":461},[290,12582,801],{"class":541},[290,12584,471],{"class":295},[290,12586,12587],{"class":163,"line":408},[290,12588,334],{"emptyLinePlaceholder":333},[290,12590,12591],{"class":163,"line":428},[290,12592,12593],{"class":455},"  \u002F* Semantic Tokens *\u002F\n",[290,12595,12596,12599,12601,12603,12605,12608],{"class":163,"line":517},[290,12597,12598],{"class":1561},"  --surface-bg",[290,12600,465],{"class":295},[290,12602,1622],{"class":461},[290,12604,484],{"class":295},[290,12606,12607],{"class":1561},"--color-primary-100",[290,12609,500],{"class":295},[290,12611,12612,12615,12617,12619,12621,12624],{"class":163,"line":523},[290,12613,12614],{"class":1561},"  --action-bg",[290,12616,465],{"class":295},[290,12618,1622],{"class":461},[290,12620,484],{"class":295},[290,12622,12623],{"class":1561},"--color-primary-600",[290,12625,500],{"class":295},[290,12627,12628,12631,12633,12635,12637,12639,12641,12644,12646,12648,12651],{"class":163,"line":532},[290,12629,12630],{"class":1561},"  --space-md",[290,12632,465],{"class":295},[290,12634,3556],{"class":461},[290,12636,484],{"class":295},[290,12638,1622],{"class":461},[290,12640,484],{"class":295},[290,12642,12643],{"class":1561},"--spacing-unit",[290,12645,490],{"class":295},[290,12647,4415],{"class":541},[290,12649,12650],{"class":461}," 4",[290,12652,500],{"class":295},[290,12654,12655],{"class":163,"line":551},[290,12656,620],{"class":295},[14,12658,12659,12662,12663,12666,12667,42],{},[62,12660,12661],{},"Progressive Enhancement Note:"," Always provide fallbacks in ",[18,12664,12665],{},"var()"," declarations for legacy browsers or when tokens fail to resolve: ",[18,12668,12669],{},"background-color: var(--surface-bg, #ffffff);",[47,12671],{},[50,12673,12675],{"id":12674},"cascade-layers-variable-inheritance","Cascade Layers & Variable Inheritance",[14,12677,12678,12679,12682,12683,12685],{},"Specificity wars traditionally plague large-scale CSS. By leveraging ",[18,12680,12681],{},"@layer",", you can explicitly control the cascade order for base, component, and utility overrides without resorting to ",[18,12684,3900],{},". Custom properties evaluated inside layers inherit predictably, allowing theme variants to override base values cleanly.",[281,12687,12689],{"className":438,"code":12688,"language":440,"meta":286,"style":286},"@layer base, components, utilities;\n\n@layer base {\n  :root {\n    --btn-radius: 0.375rem;\n  }\n}\n\n@layer components {\n  .btn {\n    border-radius: var(--btn-radius);\n  }\n  .btn--pill {\n    --btn-radius: 9999px;\n  }\n}\n",[18,12690,12691,12698,12702,12709,12715,12729,12733,12737,12741,12748,12755,12771,12775,12782,12795,12799],{"__ignoreMap":286},[290,12692,12693,12695],{"class":163,"line":292},[290,12694,12681],{"class":541},[290,12696,12697],{"class":295}," base, components, utilities;\n",[290,12699,12700],{"class":163,"line":330},[290,12701,334],{"emptyLinePlaceholder":333},[290,12703,12704,12706],{"class":163,"line":337},[290,12705,12681],{"class":541},[290,12707,12708],{"class":295}," base {\n",[290,12710,12711,12713],{"class":163,"line":364},[290,12712,3430],{"class":303},[290,12714,450],{"class":295},[290,12716,12717,12720,12722,12725,12727],{"class":163,"line":386},[290,12718,12719],{"class":1561},"    --btn-radius",[290,12721,465],{"class":295},[290,12723,12724],{"class":461},"0.375",[290,12726,801],{"class":541},[290,12728,471],{"class":295},[290,12730,12731],{"class":163,"line":408},[290,12732,771],{"class":295},[290,12734,12735],{"class":163,"line":428},[290,12736,620],{"class":295},[290,12738,12739],{"class":163,"line":517},[290,12740,334],{"emptyLinePlaceholder":333},[290,12742,12743,12745],{"class":163,"line":523},[290,12744,12681],{"class":541},[290,12746,12747],{"class":295}," components {\n",[290,12749,12750,12753],{"class":163,"line":532},[290,12751,12752],{"class":303},"  .btn",[290,12754,450],{"class":295},[290,12756,12757,12760,12762,12764,12766,12769],{"class":163,"line":551},[290,12758,12759],{"class":461},"    border-radius",[290,12761,465],{"class":295},[290,12763,1622],{"class":461},[290,12765,484],{"class":295},[290,12767,12768],{"class":1561},"--btn-radius",[290,12770,500],{"class":295},[290,12772,12773],{"class":163,"line":586},[290,12774,771],{"class":295},[290,12776,12777,12780],{"class":163,"line":602},[290,12778,12779],{"class":303},"  .btn--pill",[290,12781,450],{"class":295},[290,12783,12784,12786,12788,12791,12793],{"class":163,"line":617},[290,12785,12719],{"class":1561},[290,12787,465],{"class":295},[290,12789,12790],{"class":461},"9999",[290,12792,674],{"class":541},[290,12794,471],{"class":295},[290,12796,12797],{"class":163,"line":623},[290,12798,771],{"class":295},[290,12800,12801],{"class":163,"line":628},[290,12802,620],{"class":295},[14,12804,12805],{},[62,12806,12807],{},"Inheritance Chain Strategy:",[3017,12809,12810,12816,12822,12828],{},[1396,12811,12812,12813,42],{},"Define base tokens in ",[18,12814,12815],{},"@layer base",[1396,12817,12818,12819,42],{},"Override component defaults in ",[18,12820,12821],{},"@layer components",[1396,12823,12824,12825,42],{},"Apply utility overrides in ",[18,12826,12827],{},"@layer utilities",[1396,12829,1499,12830,12833],{},[18,12831,12832],{},"var(--custom-prop, fallback)"," to gracefully degrade when a layer isn't applied.",[14,12835,12836,12837,12840],{},"This architecture ensures that ",[18,12838,12839],{},".btn--pill"," doesn't require higher specificity selectors; it simply redefines the variable within the same cascade tier.",[47,12842],{},[50,12844,12846],{"id":12845},"dynamic-theming-state-management","Dynamic Theming & State Management",[14,12848,12849,12850,2351,12852,12855],{},"Runtime theme switching requires minimal layout thrashing. Instead of swapping entire stylesheets, update custom properties on a high-level container (e.g., ",[18,12851,2901],{},[18,12853,12854],{},"\u003Cbody>","). This triggers targeted repaints only on affected elements.",[14,12857,12858],{},[62,12859,12860],{},"Media Query Integration:",[281,12862,12864],{"className":438,"code":12863,"language":440,"meta":286,"style":286},"@media (prefers-color-scheme: dark) {\n  :root {\n    --surface-bg: #0f172a;\n    --action-bg: #38bdf8;\n  }\n}\n",[18,12865,12866,12873,12879,12891,12903,12907],{"__ignoreMap":286},[290,12867,12868,12870],{"class":163,"line":292},[290,12869,874],{"class":541},[290,12871,12872],{"class":295}," (prefers-color-scheme: dark) {\n",[290,12874,12875,12877],{"class":163,"line":330},[290,12876,3430],{"class":303},[290,12878,450],{"class":295},[290,12880,12881,12884,12886,12889],{"class":163,"line":337},[290,12882,12883],{"class":1561},"    --surface-bg",[290,12885,465],{"class":295},[290,12887,12888],{"class":461},"#0f172a",[290,12890,471],{"class":295},[290,12892,12893,12896,12898,12901],{"class":163,"line":364},[290,12894,12895],{"class":1561},"    --action-bg",[290,12897,465],{"class":295},[290,12899,12900],{"class":461},"#38bdf8",[290,12902,471],{"class":295},[290,12904,12905],{"class":163,"line":386},[290,12906,771],{"class":295},[290,12908,12909],{"class":163,"line":408},[290,12910,620],{"class":295},[14,12912,12913],{},[62,12914,12915],{},"JS-Driven Updates (Batched for Performance):",[281,12917,12919],{"className":2904,"code":12918,"language":2906,"meta":286,"style":286},"const toggleTheme = (isDark) => {\n  document.documentElement.style.setProperty(\n    \"--surface-bg\",\n    isDark ? \"#0f172a\" : \"#f8fafc\",\n  );\n  document.documentElement.style.setProperty(\n    \"--action-bg\",\n    isDark ? \"#38bdf8\" : \"#0284c7\",\n  );\n};\n",[18,12920,12921,12941,12951,12958,12975,12979,12987,12994,13010,13014],{"__ignoreMap":286},[290,12922,12923,12925,12928,12930,12932,12935,12937,12939],{"class":163,"line":292},[290,12924,7282],{"class":541},[290,12926,12927],{"class":303}," toggleTheme",[290,12929,7288],{"class":541},[290,12931,3595],{"class":295},[290,12933,12934],{"class":1561},"isDark",[290,12936,490],{"class":295},[290,12938,2988],{"class":541},[290,12940,450],{"class":295},[290,12942,12943,12946,12949],{"class":163,"line":330},[290,12944,12945],{"class":295},"  document.documentElement.style.",[290,12947,12948],{"class":303},"setProperty",[290,12950,7322],{"class":295},[290,12952,12953,12956],{"class":163,"line":337},[290,12954,12955],{"class":310},"    \"--surface-bg\"",[290,12957,548],{"class":295},[290,12959,12960,12963,12965,12968,12970,12973],{"class":163,"line":364},[290,12961,12962],{"class":295},"    isDark ",[290,12964,5734],{"class":541},[290,12966,12967],{"class":310}," \"#0f172a\"",[290,12969,8083],{"class":541},[290,12971,12972],{"class":310}," \"#f8fafc\"",[290,12974,548],{"class":295},[290,12976,12977],{"class":163,"line":386},[290,12978,8205],{"class":295},[290,12980,12981,12983,12985],{"class":163,"line":408},[290,12982,12945],{"class":295},[290,12984,12948],{"class":303},[290,12986,7322],{"class":295},[290,12988,12989,12992],{"class":163,"line":428},[290,12990,12991],{"class":310},"    \"--action-bg\"",[290,12993,548],{"class":295},[290,12995,12996,12998,13000,13003,13005,13008],{"class":163,"line":517},[290,12997,12962],{"class":295},[290,12999,5734],{"class":541},[290,13001,13002],{"class":310}," \"#38bdf8\"",[290,13004,8083],{"class":541},[290,13006,13007],{"class":310}," \"#0284c7\"",[290,13009,548],{"class":295},[290,13011,13012],{"class":163,"line":523},[290,13013,8205],{"class":295},[290,13015,13016],{"class":163,"line":532},[290,13017,13018],{"class":295},"};\n",[14,13020,13021,13024,13025,569,13027,569,13029,13031,13032,569,13034,569,13036,13038,13039,13041],{},[62,13022,13023],{},"GPU-Friendly Mapping:"," Restrict variable-driven animations to composite-only properties (",[18,13026,103],{},[18,13028,76],{},[18,13030,6792],{},"). When mapping variables to layout properties (",[18,13033,1748],{},[18,13035,2728],{},[18,13037,2725],{},"), expect layout recalculations. Use ",[18,13040,3797],{}," sparingly and only when profiling confirms a bottleneck.",[47,13043],{},[50,13045,13047],{"id":13046},"integration-with-interactive-patterns","Integration with Interactive Patterns",[14,13049,13050,13051,569,13054,13056,13057,13059,13060,13062],{},"Bridging custom properties with interactive UI states unlocks highly performant micro-interactions. By mapping state-aware variables (e.g., ",[18,13052,13053],{},"--btn-hover-bg",[18,13055,1656],{},"), you centralize interaction logic while keeping component styles declarative. This aligns directly with established ",[27,13058,3345],{"href":3344}," patterns and foundational ",[27,13061,30],{"href":29}," for smooth, jank-free feedback.",[14,13064,13065,13066,13071,13072,13075],{},"Modern CSS allows you to ",[27,13067,13068,13069],{"href":12073},"animate custom properties natively using ",[18,13070,10151],{},". Registering a variable's syntax and initial value tells the browser to interpolate it smoothly, eliminating the need for JS-driven ",[18,13073,13074],{},"requestAnimationFrame"," loops.",[281,13077,13079],{"className":438,"code":13078,"language":440,"meta":286,"style":286},"@property --hover-progress {\n  syntax: \"\u003Cnumber>\";\n  initial-value: 0;\n  inherits: true;\n}\n\n.card {\n  background: linear-gradient(\n    to right,\n    var(--accent) var(--hover-progress),\n    transparent\n  );\n  transition: --hover-progress 0.3s ease;\n}\n.card:hover {\n  --hover-progress: 100;\n}\n",[18,13080,13081,13088,13097,13103,13108,13112,13116,13122,13133,13143,13164,13169,13173,13188,13192,13198,13209],{"__ignoreMap":286},[290,13082,13083,13085],{"class":163,"line":292},[290,13084,10151],{"class":541},[290,13086,13087],{"class":295}," --hover-progress {\n",[290,13089,13090,13093,13095],{"class":163,"line":330},[290,13091,13092],{"class":295},"  syntax: \"\u003Cnumber",[290,13094,10364],{"class":541},[290,13096,10890],{"class":295},[290,13098,13099,13101],{"class":163,"line":337},[290,13100,10383],{"class":299},[290,13102,10902],{"class":295},[290,13104,13105],{"class":163,"line":364},[290,13106,13107],{"class":295},"  inherits: true;\n",[290,13109,13110],{"class":163,"line":386},[290,13111,620],{"class":295},[290,13113,13114],{"class":163,"line":408},[290,13115,334],{"emptyLinePlaceholder":333},[290,13117,13118,13120],{"class":163,"line":428},[290,13119,11528],{"class":303},[290,13121,450],{"class":295},[290,13123,13124,13126,13128,13131],{"class":163,"line":517},[290,13125,1186],{"class":461},[290,13127,465],{"class":295},[290,13129,13130],{"class":461},"linear-gradient",[290,13132,7322],{"class":295},[290,13134,13135,13138,13141],{"class":163,"line":523},[290,13136,13137],{"class":541},"    to",[290,13139,13140],{"class":461}," right",[290,13142,548],{"class":295},[290,13144,13145,13148,13150,13153,13155,13157,13159,13162],{"class":163,"line":532},[290,13146,13147],{"class":461},"    var",[290,13149,484],{"class":295},[290,13151,13152],{"class":1561},"--accent",[290,13154,490],{"class":295},[290,13156,1622],{"class":461},[290,13158,484],{"class":295},[290,13160,13161],{"class":1561},"--hover-progress",[290,13163,583],{"class":295},[290,13165,13166],{"class":163,"line":551},[290,13167,13168],{"class":461},"    transparent\n",[290,13170,13171],{"class":163,"line":586},[290,13172,8205],{"class":295},[290,13174,13175,13177,13180,13182,13184,13186],{"class":163,"line":602},[290,13176,526],{"class":461},[290,13178,13179],{"class":295},": --hover-progress ",[290,13181,199],{"class":461},[290,13183,1886],{"class":541},[290,13185,545],{"class":461},[290,13187,471],{"class":295},[290,13189,13190],{"class":163,"line":617},[290,13191,620],{"class":295},[290,13193,13194,13196],{"class":163,"line":623},[290,13195,11814],{"class":303},[290,13197,450],{"class":295},[290,13199,13200,13203,13205,13207],{"class":163,"line":628},[290,13201,13202],{"class":1561},"  --hover-progress",[290,13204,465],{"class":295},[290,13206,165],{"class":461},[290,13208,471],{"class":295},[290,13210,13211],{"class":163,"line":634},[290,13212,620],{"class":295},[14,13214,13215],{},[62,13216,13217],{},"Composite Layer Optimization:",[1393,13219,13220,13223,13228],{},[1396,13221,13222],{},"Avoid animating variables that affect layout or paint.",[1396,13224,1499,13225,13227],{},[18,13226,10151],{}," only for numeric, color, or transform-compatible values.",[1396,13229,13230,13231,13233],{},"Keep transition durations under ",[18,13232,3066],{}," for perceived responsiveness.",[47,13235],{},[50,13237,13239],{"id":13238},"browser-compatibility-matrix","Browser Compatibility Matrix",[2250,13241,13242,13256],{},[2253,13243,13244],{},[2256,13245,13246,13248,13250,13252,13254],{},[2259,13247,3737],{},[2259,13249,2276],{},[2259,13251,2287],{},[2259,13253,2297],{},[2259,13255,2308],{},[2269,13257,13258,13276,13292],{},[2256,13259,13260,13265,13268,13271,13273],{},[2274,13261,13262,13263,3914],{},"Custom Properties (",[18,13264,12665],{},[2274,13266,13267],{},"49+",[2274,13269,13270],{},"31+",[2274,13272,3805],{},[2274,13274,13275],{},"15+",[2256,13277,13278,13282,13285,13288,13290],{},[2274,13279,13280],{},[18,13281,12681],{},[2274,13283,13284],{},"99+",[2274,13286,13287],{},"97+",[2274,13289,2300],{},[2274,13291,13284],{},[2256,13293,13294,13299,13301,13304,13307],{},[2274,13295,13296,13298],{},[18,13297,10151],{}," (Houdini)",[2274,13300,3781],{},[2274,13302,13303],{},"128+",[2274,13305,13306],{},"16.4+",[2274,13308,3781],{},[14,13310,13311,13314,13315,13317,13318,13320,13321,42],{},[62,13312,13313],{},"Fallback Strategy:"," For environments lacking ",[18,13316,12681],{},", rely on source order and specificity. For missing ",[18,13319,10151],{},", use JS-driven interpolation or stick to standard transition properties. Detect custom property support with ",[18,13322,13323],{},"CSS.supports('--x', '0')",[47,13325],{},[50,13327,13329],{"id":13328},"common-issues-resolutions","Common Issues & Resolutions",[2250,13331,13332,13340],{},[2253,13333,13334],{},[2256,13335,13336,13338],{},[2259,13337,2338],{},[2259,13339,2341],{},[2269,13341,13342,13352,13374],{},[2256,13343,13344,13347],{},[2274,13345,13346],{},"Specificity conflicts overriding custom properties",[2274,13348,1499,13349,13351],{},[18,13350,12681],{}," to explicitly define cascade order. Avoid inline styles or high-specificity selectors that bypass your architecture.",[2256,13353,13354,13357],{},[2274,13355,13356],{},"Unintended inheritance leaks in Shadow DOM",[2274,13358,13359,13360,13363,13364,13366,13367,2351,13370,13373],{},"Scope variables to component roots using ",[18,13361,13362],{},":host"," or explicit class selectors. Avoid global ",[18,13365,1554],{}," declarations for component-specific tokens; reset with ",[18,13368,13369],{},"initial",[18,13371,13372],{},"unset"," at boundaries.",[2256,13375,13376,13379],{},[2274,13377,13378],{},"Performance bottlenecks from excessive repaints",[2274,13380,13381,13382,13384,13385,13387,13388,13391],{},"Register animatable properties with ",[18,13383,10151],{},". Restrict transitions to composite-friendly properties. Batch DOM updates via ",[18,13386,13074],{}," when applying multiple ",[18,13389,13390],{},"style.setProperty()"," calls.",[47,13393],{},[50,13395,13396],{"id":8357},"DevTools Debugging Workflow",[3017,13398,13399,13413,13433,13454],{},[1396,13400,13401,13404,13405,13408,13409,13412],{},[62,13402,13403],{},"Computed Tab Inspection:"," Open the Elements panel, select a node, and view the ",[62,13406,13407],{},"Computed"," tab. Custom properties appear under ",[18,13410,13411],{},"Custom Properties"," and show resolved values.",[1396,13414,13415,13418,13419,13422,13423,13426,13427,13429,13430,13432],{},[62,13416,13417],{},"Layer Visualization:"," In Chrome DevTools, enable ",[18,13420,13421],{},"Layers"," panel (",[18,13424,13425],{},"Esc"," > ",[18,13428,13421],{},"). Hover over elements to see which ",[18,13431,12681],{}," rules are applied and how inheritance chains resolve.",[1396,13434,13435,13440,13441,13444,13445,569,13447,13449,13450,13453],{},[62,13436,13437,13439],{},[18,13438,10151],{}," Validation:"," Open the Console and run ",[18,13442,13443],{},"CSS.supports('@property --x { syntax: \"\u003Cnumber>\"; inherits: false; initial-value: 0; }')",". If it returns ",[18,13446,10852],{},[18,13448,10151],{}," isn't supported. Use the ",[62,13451,13452],{},"Styles"," pane to verify registered syntax matches your initial value type.",[1396,13455,13456,13459,13460,2351,13463,13466,13467,3041,13469,13471,13472,13474],{},[62,13457,13458],{},"Performance Profiling:"," Record a timeline while triggering theme switches or hover states. Look for ",[18,13461,13462],{},"Layout",[18,13464,13465],{},"Paint"," spikes. If present, refactor variable-driven transitions to ",[18,13468,103],{},[18,13470,76],{}," or apply ",[18,13473,10151],{}," registration.",[47,13476],{},[50,13478,13480],{"id":13479},"specification-references","Specification References",[1393,13482,13483,13491,13500],{},[1396,13484,13485],{},[27,13486,13490],{"href":13487,"rel":13488},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-variables-1\u002F",[13489],"nofollow","CSS Custom Properties for Cascading Variables Module Level 1",[1396,13492,13493],{},[27,13494,13497,13498,3914],{"href":13495,"rel":13496},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-cascade-5\u002F",[13489],"CSS Cascade and Inheritance Level 5 (",[18,13499,12681],{},[1396,13501,13502],{},[27,13503,13506,13507,3914],{"href":13504,"rel":13505},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-properties-values-api-1\u002F",[13489],"CSS Properties and Values API Level 1 (",[18,13508,10151],{},[47,13510],{},[50,13512,1316],{"id":1315},[14,13514,13515,13518],{},[62,13516,13517],{},"Should I use CSS custom properties or Sass variables for theming?","\nUse CSS custom properties for runtime theming and dynamic state changes, as they are evaluated in the browser and can be updated via JS or media queries. Sass variables are compile-time only and better suited for static design system foundations.",[14,13520,13521,13524,13525,13527,13528,2351,13530,13532],{},[62,13522,13523],{},"How do I prevent custom property inheritance from breaking component isolation?","\nScope variables explicitly to component containers rather than relying on global inheritance. Use ",[18,13526,12681],{}," to manage override precedence, and reset inherited values to ",[18,13529,13369],{},[18,13531,13372],{}," at component boundaries when necessary.",[14,13534,13535,13538,13539,13541],{},[62,13536,13537],{},"Can I animate CSS custom properties without JavaScript?","\nYes, by registering the property with ",[18,13540,10151],{}," to define its syntax and initial value. This enables native CSS transitions and keyframe animations on custom properties, unlocking advanced micro-interactions without JS overhead.",[47,13543],{},[50,13545,1391],{"id":1390},[1393,13547,13548,13553,13558,13563,13568],{},[1396,13549,13550,13552],{},[27,13551,12355],{"href":12073}," — registering syntax to interpolate variables natively.",[1396,13554,13555,13557],{},[27,13556,11232],{"href":4900}," — tying motion timing to a responsive spacing scale.",[1396,13559,13560,13562],{},[27,13561,1481],{"href":1480}," — the parent guide for transitions, keyframes, and motion architecture.",[1396,13564,13565,13567],{},[27,13566,30],{"href":29}," — the state-change mechanics that variable tokens feed into.",[1396,13569,13570,13573],{},[27,13571,13572],{"href":11317},"Fluid space scale with clamp"," — building the responsive token scale from the container-queries guide.",[1430,13575,13576],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":286,"searchDepth":330,"depth":330,"links":13578},[13579,13580,13581,13582,13583,13584,13585,13586,13587,13588],{"id":12486,"depth":330,"text":12487},{"id":12674,"depth":330,"text":12675},{"id":12845,"depth":330,"text":12846},{"id":13046,"depth":330,"text":13047},{"id":13238,"depth":330,"text":13239},{"id":13328,"depth":330,"text":13329},{"id":8357,"depth":330,"text":13396},{"id":13479,"depth":330,"text":13480},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Structure CSS custom properties for scalable design systems: design token architecture, dynamic theming, and runtime state control for modern UIs.",{"seoTitle":13591,"datePublished":1447,"dateModified":1447,"faq":13592},"CSS Custom Properties Architecture Guide",[13593,13595,13597],{"q":13517,"a":13594},"Use CSS custom properties for runtime theming and dynamic state changes, since they are evaluated in the browser and can be updated via JavaScript or media queries. Sass variables are compile-time only and better suited for static design system foundations.",{"q":13523,"a":13596},"Scope variables explicitly to component containers rather than relying on global inheritance. Use @layer to manage override precedence, and reset inherited values to initial or unset at component boundaries when necessary.",{"q":13537,"a":13598},"Yes, by registering the property with @property to define its syntax and initial value. This enables native CSS transitions and keyframe animations on custom properties, unlocking advanced micro-interactions without JavaScript overhead.","\u002Fcss-only-micro-interactions-animations\u002Fcss-custom-properties-architecture",{"title":12404,"description":13589},"css-only-micro-interactions-animations\u002Fcss-custom-properties-architecture\u002Findex","BNPsfV98K3jGfU9ewzfM3d8IEMYN74uuUOr2YgQiHOA",{"id":13604,"title":13605,"body":13606,"description":14927,"extension":1444,"meta":14928,"navigation":333,"path":14939,"seo":14940,"stem":14941,"__hash__":14942},"content\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fcss-transition-timing-functions\u002Findex.md","CSS Transition Timing Functions: ease, cubic-bezier(), steps() and linear()",{"type":7,"value":13607,"toc":14917},[13608,13611,13626,13629,13631,13635,13641,13654,13660,13662,13666,13679,13733,13767,13769,13773,13779,13924,14501,14509,14511,14515,14539,14546,14548,14552,14560,14820,14823,14825,14827,14845,14847,14849,14860,14868,14874,14882,14884,14886,14914],[10,13609,13605],{"id":13610},"css-transition-timing-functions-ease-cubic-bezier-steps-and-linear",[14,13612,13613,13614,13616,13617,569,13619,13621,13622,13625],{},"A transition that uses the right duration but the wrong timing function still feels wrong. The timing function controls how progress is distributed across the elapsed time, and that distribution is what the eye reads as \"weight,\" \"snap,\" or \"mechanical.\" This guide, part of the ",[27,13615,30],{"href":29}," section, isolates the four ways CSS expresses easing — the named keywords, ",[18,13618,6773],{},[18,13620,3971],{},", and the newer ",[18,13623,13624],{},"linear()"," — explains the perceptual differences between them, and shows how to package easing as reusable tokens so an entire interface shares one motion language.",[14,13627,13628],{},"The scenario is narrow but common: you have a working transition, motion is smooth, durations are tuned, yet hovers feel sluggish and panels feel cheap. The fix is almost never the duration. It is the curve.",[47,13630],{},[50,13632,13634],{"id":13633},"why-the-timing-function-matters-more-than-the-duration","Why the timing function matters more than the duration",[14,13636,13637,13638,13640],{},"Duration answers \"how long,\" but the timing function answers \"where is the element at each moment in between.\" Two transitions of identical length can feel completely different because one front-loads its movement and the other back-loads it. Human perception is tuned to physical motion: objects in the real world accelerate from rest and decelerate into a stop. A curve that ignores that — a constant-velocity ",[18,13639,5456],{}," ramp — reads as robotic precisely because nothing physical moves that way.",[14,13642,13643,13644,13647,13648,13650,13651,42],{},"CSS gives you a declarative way to encode these curves directly on ",[18,13645,13646],{},"transition-timing-function"," (or as the third value of the ",[18,13649,887],{}," shorthand). There is no JavaScript and no animation loop; the browser interpolates on the compositor where possible. The only judgement call is which curve communicates the intent of the interaction. Entrances that should feel inviting want a decelerating curve (slow to a stop). Exits that should feel decisive want an accelerating curve (speed away). Symmetric state toggles want something balanced like ",[18,13652,13653],{},"ease-in-out",[14,13655,13656,13657,13659],{},"The accessibility tradeoff is the same as for any motion: a more dramatic curve (one with overshoot) draws more attention, which is exactly what you sometimes want and exactly what some users cannot tolerate. Anything you do here must still collapse under ",[27,13658,10214],{"href":1412},", so treat expressive easing as an enhancement layered on top of a functional, instant baseline.",[47,13661],{},[50,13663,13665],{"id":13664},"the-shape-of-each-curve","The shape of each curve",[14,13667,13668,13669,13671,13672,3041,13674,13676,13677,3724],{},"The diagram below plots progress (y, 0 to 1) against elapsed time (x, 0 to 1) for the curves you reach for most. A straight diagonal is ",[18,13670,5456],{},"; the S-curve is ",[18,13673,6770],{},[18,13675,13653],{},"; the curve that bulges above the diagonal early decelerates (",[18,13678,5411],{},[133,13680,140,13682,140,13685,140,13688,140,140,13691,140,13693,140,13695,140,13701,140,140,13706,140,13709,140,140,13712,140,13715,140,140,13718,140,13721,140,13724,140,13728],{"viewBox":6614,"role":136,"ariaLabel":13681,"xmlns":138,"style":139},"Easing curves plotted as progress over time: linear, ease, and a cubic-bezier ease-out",[142,13683,13684],{},"Easing curves on a progress-over-time graph",[146,13686,13687],{},"An x\u002Fy graph where x is elapsed time and y is animation progress, comparing a straight linear line, an S-shaped ease curve, and a fast-then-slow ease-out cubic-bezier.",[150,13689,13690],{"x":152,"y":2598,"style":154},"Progress over time",[163,13692],{"x1":5115,"y1":2613,"x2":10284,"y2":2613,"stroke":167,"strokeWidth":168},[163,13694],{"x1":5115,"y1":2613,"x2":5115,"y2":5144,"stroke":167,"strokeWidth":168},[150,13696,13700],{"x":13697,"y":13698,"style":13699},"375","358","text-anchor:middle;fill:currentColor;font:13px sans-serif;opacity:0.7","time (0 to 1)",[150,13702,13705],{"x":4146,"y":13703,"style":13699,"transform":13704},"195","rotate(-90 40 195)","progress",[163,13707],{"x1":5115,"y1":2613,"x2":10284,"y2":5144,"stroke":167,"strokeWidth":194,"strokeDashArray":13708,"opacity":232},[5911,5911],[150,13710,5456],{"x":4176,"y":165,"style":13711},"text-anchor:start;fill:currentColor;font:13px sans-serif;opacity:0.75",[253,13713],{"d":13714,"fill":72,"stroke":177,"strokeWidth":1579},"M90 320 C 200 320, 240 95, 660 70",[150,13716,6770],{"x":3112,"y":3112,"style":13717},"text-anchor:start;fill:#7aa2ff;font:600 13px sans-serif",[253,13719],{"d":13720,"fill":72,"stroke":167,"strokeWidth":10258},"M90 320 C 230 70, 470 70, 660 70",[150,13722,5411],{"x":1787,"y":1787,"style":13723},"text-anchor:start;fill:currentColor;font:600 13px sans-serif;opacity:0.85",[150,13725,13727],{"x":165,"y":8944,"style":13726},"text-anchor:start;fill:currentColor;font:12px ui-monospace,monospace;opacity:0.7","0,0",[150,13729,13732],{"x":13730,"y":2630,"style":13731},"648","text-anchor:end;fill:currentColor;font:12px ui-monospace,monospace;opacity:0.7","1,1",[14,13734,13735,13736,13738,13739,13741,13742,569,13745,13741,13747,569,13750,13741,13753,569,13756,13741,13758,8393,13761,13741,13763,13766],{},"The named keywords are just shorthands for ",[18,13737,6773],{}," values: ",[18,13740,5456],{}," is ",[18,13743,13744],{},"cubic-bezier(0,0,1,1)",[18,13746,6770],{},[18,13748,13749],{},"cubic-bezier(0.25, 0.1, 0.25, 1)",[18,13751,13752],{},"ease-in",[18,13754,13755],{},"cubic-bezier(0.42, 0, 1, 1)",[18,13757,5411],{},[18,13759,13760],{},"cubic-bezier(0, 0, 0.58, 1)",[18,13762,13653],{},[18,13764,13765],{},"cubic-bezier(0.42, 0, 0.58, 1)",". Knowing the keyword equivalents lets you start from a familiar feel and nudge it rather than guessing at four numbers cold.",[47,13768],{},[50,13770,13772],{"id":13771},"a-complete-working-implementation-easing-tokens-applied-to-one-component","A complete working implementation: easing tokens applied to one component",[14,13774,13775,13776,13778],{},"The pattern below registers a small set of named easing tokens as custom properties, then drives a card's hover and a panel's expand from those tokens. Because the curves live in one place, retuning the motion of the whole interface is a single edit. This also connects to the ",[27,13777,3355],{"href":10147}," for the broader token system.",[281,13780,13782],{"className":283,"code":13781,"language":285,"meta":286,"style":286},"\u003Cdiv class=\"demo\">\n  \u003Cbutton class=\"card\" type=\"button\">\n    \u003Cspan class=\"card__label\">Hover or focus me\u003C\u002Fspan>\n  \u003C\u002Fbutton>\n\n  \u003Cdetails class=\"panel\">\n    \u003Csummary class=\"panel__summary\">Details (steps reveal)\u003C\u002Fsummary>\n    \u003Cdiv class=\"panel__body\">Content that ticks open in discrete steps.\u003C\u002Fdiv>\n  \u003C\u002Fdetails>\n\u003C\u002Fdiv>\n",[18,13783,13784,13799,13819,13839,13847,13851,13867,13888,13908,13916],{"__ignoreMap":286},[290,13785,13786,13788,13790,13792,13794,13797],{"class":163,"line":292},[290,13787,296],{"class":295},[290,13789,342],{"class":299},[290,13791,314],{"class":303},[290,13793,307],{"class":295},[290,13795,13796],{"class":310},"\"demo\"",[290,13798,327],{"class":295},[290,13800,13801,13803,13805,13807,13809,13811,13813,13815,13817],{"class":163,"line":330},[290,13802,367],{"class":295},[290,13804,300],{"class":299},[290,13806,314],{"class":303},[290,13808,307],{"class":295},[290,13810,9295],{"class":310},[290,13812,393],{"class":303},[290,13814,307],{"class":295},[290,13816,398],{"class":310},[290,13818,327],{"class":295},[290,13820,13821,13823,13825,13827,13829,13832,13835,13837],{"class":163,"line":337},[290,13822,4290],{"class":295},[290,13824,290],{"class":299},[290,13826,314],{"class":303},[290,13828,307],{"class":295},[290,13830,13831],{"class":310},"\"card__label\"",[290,13833,13834],{"class":295},">Hover or focus me\u003C\u002F",[290,13836,290],{"class":299},[290,13838,327],{"class":295},[290,13840,13841,13843,13845],{"class":163,"line":364},[290,13842,4315],{"class":295},[290,13844,300],{"class":299},[290,13846,327],{"class":295},[290,13848,13849],{"class":163,"line":386},[290,13850,334],{"emptyLinePlaceholder":333},[290,13852,13853,13855,13858,13860,13862,13865],{"class":163,"line":408},[290,13854,367],{"class":295},[290,13856,13857],{"class":299},"details",[290,13859,314],{"class":303},[290,13861,307],{"class":295},[290,13863,13864],{"class":310},"\"panel\"",[290,13866,327],{"class":295},[290,13868,13869,13871,13874,13876,13878,13881,13884,13886],{"class":163,"line":428},[290,13870,4290],{"class":295},[290,13872,13873],{"class":299},"summary",[290,13875,314],{"class":303},[290,13877,307],{"class":295},[290,13879,13880],{"class":310},"\"panel__summary\"",[290,13882,13883],{"class":295},">Details (steps reveal)\u003C\u002F",[290,13885,13873],{"class":299},[290,13887,327],{"class":295},[290,13889,13890,13892,13894,13896,13898,13901,13904,13906],{"class":163,"line":517},[290,13891,4290],{"class":295},[290,13893,342],{"class":299},[290,13895,314],{"class":303},[290,13897,307],{"class":295},[290,13899,13900],{"class":310},"\"panel__body\"",[290,13902,13903],{"class":295},">Content that ticks open in discrete steps.\u003C\u002F",[290,13905,342],{"class":299},[290,13907,327],{"class":295},[290,13909,13910,13912,13914],{"class":163,"line":523},[290,13911,4315],{"class":295},[290,13913,13857],{"class":299},[290,13915,327],{"class":295},[290,13917,13918,13920,13922],{"class":163,"line":532},[290,13919,431],{"class":295},[290,13921,342],{"class":299},[290,13923,327],{"class":295},[281,13925,13927],{"className":438,"code":13926,"language":440,"meta":286,"style":286},":root {\n  \u002F* Easing tokens: name the intent, not the numbers *\u002F\n  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);   \u002F* balanced UI default *\u002F\n  --ease-decelerate: cubic-bezier(0, 0, 0.2, 1);   \u002F* entrances: slow to a stop *\u002F\n  --ease-accelerate: cubic-bezier(0.4, 0, 1, 1);   \u002F* exits: speed away *\u002F\n  --ease-overshoot: cubic-bezier(0.34, 1.56, 0.64, 1); \u002F* playful spring *\u002F\n  --ease-tick: steps(6, jump-end);                 \u002F* discrete ticking *\u002F\n\n  \u002F* Duration tokens pair with the curves *\u002F\n  --dur-fast: 160ms;\n  --dur-base: 240ms;\n}\n\n.card {\n  border: 1px solid currentColor;\n  border-radius: 12px;\n  padding: 1rem 1.25rem;\n  background: transparent;\n  \u002F* The third value of the shorthand IS the timing function *\u002F\n  transition:\n    transform var(--dur-base) var(--ease-overshoot),\n    box-shadow var(--dur-fast) var(--ease-standard);\n}\n\n.card:hover,\n.card:focus-visible {\n  \u002F* Overshoot makes the lift feel springy rather than mechanical *\u002F\n  transform: translateY(-6px) scale(1.02);\n  box-shadow: 0 10px 24px rgb(0 0 0 \u002F 0.18);\n}\n\n.panel__body {\n  \u002F* steps() holds each frame, producing a ticking reveal instead of a glide *\u002F\n  overflow: hidden;\n  max-height: 0;\n  transition: max-height var(--dur-base) var(--ease-tick);\n}\n\n\u002F* native disclosure: when open, animate height via a generous max-height *\u002F\n.panel[open] .panel__body {\n  max-height: 200px;\n  transition-timing-function: var(--ease-decelerate);\n}\n\n\u002F* Always provide an instant baseline for reduced-motion users *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .card,\n  .panel__body {\n    transition-duration: 0.01ms;\n  }\n}\n",[18,13928,13929,13935,13940,13971,14001,14031,14065,14085,14089,14094,14107,14120,14124,14128,14134,14150,14162,14179,14190,14195,14201,14223,14245,14249,14253,14259,14266,14271,14296,14328,14332,14336,14343,14348,14358,14369,14393,14397,14401,14406,14421,14433,14449,14453,14457,14462,14468,14474,14481,14493,14497],{"__ignoreMap":286},[290,13930,13931,13933],{"class":163,"line":292},[290,13932,1554],{"class":303},[290,13934,450],{"class":295},[290,13936,13937],{"class":163,"line":330},[290,13938,13939],{"class":455},"  \u002F* Easing tokens: name the intent, not the numbers *\u002F\n",[290,13941,13942,13945,13947,13949,13951,13953,13955,13957,13959,13961,13963,13965,13968],{"class":163,"line":337},[290,13943,13944],{"class":1561},"  --ease-standard",[290,13946,465],{"class":295},[290,13948,11767],{"class":461},[290,13950,484],{"class":295},[290,13952,169],{"class":461},[290,13954,569],{"class":295},[290,13956,487],{"class":461},[290,13958,569],{"class":295},[290,13960,566],{"class":461},[290,13962,569],{"class":295},[290,13964,468],{"class":461},[290,13966,13967],{"class":295},");   ",[290,13969,13970],{"class":455},"\u002F* balanced UI default *\u002F\n",[290,13972,13973,13976,13978,13980,13982,13984,13986,13988,13990,13992,13994,13996,13998],{"class":163,"line":364},[290,13974,13975],{"class":1561},"  --ease-decelerate",[290,13977,465],{"class":295},[290,13979,11767],{"class":461},[290,13981,484],{"class":295},[290,13983,487],{"class":461},[290,13985,569],{"class":295},[290,13987,487],{"class":461},[290,13989,569],{"class":295},[290,13991,566],{"class":461},[290,13993,569],{"class":295},[290,13995,468],{"class":461},[290,13997,13967],{"class":295},[290,13999,14000],{"class":455},"\u002F* entrances: slow to a stop *\u002F\n",[290,14002,14003,14006,14008,14010,14012,14014,14016,14018,14020,14022,14024,14026,14028],{"class":163,"line":386},[290,14004,14005],{"class":1561},"  --ease-accelerate",[290,14007,465],{"class":295},[290,14009,11767],{"class":461},[290,14011,484],{"class":295},[290,14013,169],{"class":461},[290,14015,569],{"class":295},[290,14017,487],{"class":461},[290,14019,569],{"class":295},[290,14021,468],{"class":461},[290,14023,569],{"class":295},[290,14025,468],{"class":461},[290,14027,13967],{"class":295},[290,14029,14030],{"class":455},"\u002F* exits: speed away *\u002F\n",[290,14032,14033,14036,14038,14040,14042,14045,14047,14050,14052,14055,14057,14059,14062],{"class":163,"line":408},[290,14034,14035],{"class":1561},"  --ease-overshoot",[290,14037,465],{"class":295},[290,14039,11767],{"class":461},[290,14041,484],{"class":295},[290,14043,14044],{"class":461},"0.34",[290,14046,569],{"class":295},[290,14048,14049],{"class":461},"1.56",[290,14051,569],{"class":295},[290,14053,14054],{"class":461},"0.64",[290,14056,569],{"class":295},[290,14058,468],{"class":461},[290,14060,14061],{"class":295},"); ",[290,14063,14064],{"class":455},"\u002F* playful spring *\u002F\n",[290,14066,14067,14070,14072,14075,14077,14079,14082],{"class":163,"line":428},[290,14068,14069],{"class":1561},"  --ease-tick",[290,14071,465],{"class":295},[290,14073,14074],{"class":461},"steps",[290,14076,484],{"class":295},[290,14078,5901],{"class":461},[290,14080,14081],{"class":295},", jump-end);                 ",[290,14083,14084],{"class":455},"\u002F* discrete ticking *\u002F\n",[290,14086,14087],{"class":163,"line":517},[290,14088,334],{"emptyLinePlaceholder":333},[290,14090,14091],{"class":163,"line":523},[290,14092,14093],{"class":455},"  \u002F* Duration tokens pair with the curves *\u002F\n",[290,14095,14096,14099,14101,14103,14105],{"class":163,"line":532},[290,14097,14098],{"class":1561},"  --dur-fast",[290,14100,465],{"class":295},[290,14102,1830],{"class":461},[290,14104,542],{"class":541},[290,14106,471],{"class":295},[290,14108,14109,14112,14114,14116,14118],{"class":163,"line":551},[290,14110,14111],{"class":1561},"  --dur-base",[290,14113,465],{"class":295},[290,14115,8947],{"class":461},[290,14117,542],{"class":541},[290,14119,471],{"class":295},[290,14121,14122],{"class":163,"line":586},[290,14123,620],{"class":295},[290,14125,14126],{"class":163,"line":602},[290,14127,334],{"emptyLinePlaceholder":333},[290,14129,14130,14132],{"class":163,"line":617},[290,14131,11528],{"class":303},[290,14133,450],{"class":295},[290,14135,14136,14138,14140,14142,14144,14146,14148],{"class":163,"line":623},[290,14137,1948],{"class":461},[290,14139,465],{"class":295},[290,14141,468],{"class":461},[290,14143,674],{"class":541},[290,14145,852],{"class":461},[290,14147,855],{"class":461},[290,14149,471],{"class":295},[290,14151,14152,14154,14156,14158,14160],{"class":163,"line":628},[290,14153,1663],{"class":461},[290,14155,465],{"class":295},[290,14157,5894],{"class":461},[290,14159,674],{"class":541},[290,14161,471],{"class":295},[290,14163,14164,14166,14168,14170,14172,14175,14177],{"class":163,"line":634},[290,14165,10433],{"class":461},[290,14167,465],{"class":295},[290,14169,468],{"class":461},[290,14171,801],{"class":541},[290,14173,14174],{"class":461}," 1.25",[290,14176,801],{"class":541},[290,14178,471],{"class":295},[290,14180,14181,14183,14185,14188],{"class":163,"line":649},[290,14182,1186],{"class":461},[290,14184,465],{"class":295},[290,14186,14187],{"class":461},"transparent",[290,14189,471],{"class":295},[290,14191,14192],{"class":163,"line":660},[290,14193,14194],{"class":455},"  \u002F* The third value of the shorthand IS the timing function *\u002F\n",[290,14196,14197,14199],{"class":163,"line":688},[290,14198,526],{"class":461},[290,14200,529],{"class":295},[290,14202,14203,14205,14207,14209,14212,14214,14216,14218,14221],{"class":163,"line":693},[290,14204,554],{"class":295},[290,14206,1622],{"class":461},[290,14208,484],{"class":295},[290,14210,14211],{"class":1561},"--dur-base",[290,14213,490],{"class":295},[290,14215,1622],{"class":461},[290,14217,484],{"class":295},[290,14219,14220],{"class":1561},"--ease-overshoot",[290,14222,583],{"class":295},[290,14224,14225,14227,14229,14231,14234,14236,14238,14240,14243],{"class":163,"line":698},[290,14226,3140],{"class":295},[290,14228,1622],{"class":461},[290,14230,484],{"class":295},[290,14232,14233],{"class":1561},"--dur-fast",[290,14235,490],{"class":295},[290,14237,1622],{"class":461},[290,14239,484],{"class":295},[290,14241,14242],{"class":1561},"--ease-standard",[290,14244,500],{"class":295},[290,14246,14247],{"class":163,"line":704},[290,14248,620],{"class":295},[290,14250,14251],{"class":163,"line":710},[290,14252,334],{"emptyLinePlaceholder":333},[290,14254,14255,14257],{"class":163,"line":717},[290,14256,11814],{"class":303},[290,14258,548],{"class":295},[290,14260,14261,14264],{"class":163,"line":730},[290,14262,14263],{"class":303},".card:focus-visible",[290,14265,450],{"class":295},[290,14267,14268],{"class":163,"line":742},[290,14269,14270],{"class":455},"  \u002F* Overshoot makes the lift feel springy rather than mechanical *\u002F\n",[290,14272,14273,14275,14277,14279,14281,14284,14286,14288,14290,14292,14294],{"class":163,"line":768},[290,14274,476],{"class":461},[290,14276,465],{"class":295},[290,14278,481],{"class":461},[290,14280,484],{"class":295},[290,14282,14283],{"class":461},"-6",[290,14285,674],{"class":541},[290,14287,490],{"class":295},[290,14289,493],{"class":461},[290,14291,484],{"class":295},[290,14293,2057],{"class":461},[290,14295,500],{"class":295},[290,14297,14298,14300,14302,14304,14306,14308,14310,14312,14314,14316,14318,14320,14322,14324,14326],{"class":163,"line":774},[290,14299,3207],{"class":461},[290,14301,465],{"class":295},[290,14303,487],{"class":461},[290,14305,11862],{"class":461},[290,14307,674],{"class":541},[290,14309,3219],{"class":461},[290,14311,674],{"class":541},[290,14313,11872],{"class":461},[290,14315,484],{"class":295},[290,14317,487],{"class":461},[290,14319,1198],{"class":461},[290,14321,1198],{"class":461},[290,14323,1203],{"class":295},[290,14325,178],{"class":461},[290,14327,500],{"class":295},[290,14329,14330],{"class":163,"line":779},[290,14331,620],{"class":295},[290,14333,14334],{"class":163,"line":784},[290,14335,334],{"emptyLinePlaceholder":333},[290,14337,14338,14341],{"class":163,"line":812},[290,14339,14340],{"class":303},".panel__body",[290,14342,450],{"class":295},[290,14344,14345],{"class":163,"line":860},[290,14346,14347],{"class":455},"  \u002F* steps() holds each frame, producing a ticking reveal instead of a glide *\u002F\n",[290,14349,14350,14352,14354,14356],{"class":163,"line":865},[290,14351,7803],{"class":461},[290,14353,465],{"class":295},[290,14355,7808],{"class":461},[290,14357,471],{"class":295},[290,14359,14360,14363,14365,14367],{"class":163,"line":871},[290,14361,14362],{"class":461},"  max-height",[290,14364,465],{"class":295},[290,14366,487],{"class":461},[290,14368,471],{"class":295},[290,14370,14371,14373,14376,14378,14380,14382,14384,14386,14388,14391],{"class":163,"line":880},[290,14372,526],{"class":461},[290,14374,14375],{"class":295},": max-height ",[290,14377,1622],{"class":461},[290,14379,484],{"class":295},[290,14381,14211],{"class":1561},[290,14383,490],{"class":295},[290,14385,1622],{"class":461},[290,14387,484],{"class":295},[290,14389,14390],{"class":1561},"--ease-tick",[290,14392,500],{"class":295},[290,14394,14395],{"class":163,"line":896},[290,14396,620],{"class":295},[290,14398,14399],{"class":163,"line":4734},[290,14400,334],{"emptyLinePlaceholder":333},[290,14402,14403],{"class":163,"line":4742},[290,14404,14405],{"class":455},"\u002F* native disclosure: when open, animate height via a generous max-height *\u002F\n",[290,14407,14408,14410,14412,14414,14417,14419],{"class":163,"line":4761},[290,14409,6369],{"class":303},[290,14411,1140],{"class":295},[290,14413,1097],{"class":303},[290,14415,14416],{"class":295},"] ",[290,14418,14340],{"class":303},[290,14420,450],{"class":295},[290,14422,14423,14425,14427,14429,14431],{"class":163,"line":4766},[290,14424,14362],{"class":461},[290,14426,465],{"class":295},[290,14428,174],{"class":461},[290,14430,674],{"class":541},[290,14432,471],{"class":295},[290,14434,14435,14438,14440,14442,14444,14447],{"class":163,"line":4786},[290,14436,14437],{"class":461},"  transition-timing-function",[290,14439,465],{"class":295},[290,14441,1622],{"class":461},[290,14443,484],{"class":295},[290,14445,14446],{"class":1561},"--ease-decelerate",[290,14448,500],{"class":295},[290,14450,14451],{"class":163,"line":9635},[290,14452,620],{"class":295},[290,14454,14455],{"class":163,"line":9641},[290,14456,334],{"emptyLinePlaceholder":333},[290,14458,14459],{"class":163,"line":9647},[290,14460,14461],{"class":455},"\u002F* Always provide an instant baseline for reduced-motion users *\u002F\n",[290,14463,14464,14466],{"class":163,"line":9665},[290,14465,874],{"class":541},[290,14467,877],{"class":295},[290,14469,14470,14472],{"class":163,"line":9683},[290,14471,9083],{"class":303},[290,14473,548],{"class":295},[290,14475,14476,14479],{"class":163,"line":9688},[290,14477,14478],{"class":303},"  .panel__body",[290,14480,450],{"class":295},[290,14482,14483,14485,14487,14489,14491],{"class":163,"line":9694},[290,14484,2856],{"class":461},[290,14486,465],{"class":295},[290,14488,2831],{"class":461},[290,14490,542],{"class":541},[290,14492,471],{"class":295},[290,14494,14495],{"class":163,"line":9700},[290,14496,771],{"class":295},[290,14498,14499],{"class":163,"line":9710},[290,14500,620],{"class":295},[14,14502,14503,14504,12087,14506,14508],{},"Every interactive element references a token, so the four-number Bezier definitions never sprout copies across the codebase. Swapping ",[18,14505,14220],{},[18,14507,14242],{}," instantly de-emphasises every springy element at once.",[47,14510],{},[50,14512,14514],{"id":14513},"the-key-technique-a-bezier-is-a-position-vs-time-map","The key technique: a Bezier is a position-vs-time map",[14,14516,14517,14518,14521,14522,80,14525,14528,14529,14532,14533,14535,14536,42],{},"The single idea that makes ",[18,14519,14520],{},"cubic-bezier(x1, y1, x2, y2)"," predictable is that the curve maps ",[62,14523,14524],{},"time on the x axis",[62,14526,14527],{},"progress on the y axis",". The two control points pull the curve toward themselves. A control point with a large y relative to its x makes progress race ahead of time (fast start); a control point with a small y makes progress lag behind time (slow start). Pushing ",[18,14530,14531],{},"y2"," above ",[18,14534,468],{}," forces the curve past full progress and back down — that is the mathematical source of overshoot in ",[18,14537,14538],{},"cubic-bezier(0.34, 1.56, 0.64, 1)",[14,14540,14541,14542,14545],{},"The hard constraint is that both ",[62,14543,14544],{},"x values must stay within 0 and 1",". The x axis is time, and time cannot run backwards; an x outside that range produces an invalid curve that the browser rejects. The y values, by contrast, are free to exceed the range, which is exactly how bounce and snap-back are expressed.",[47,14547],{},[50,14549,14551],{"id":14550},"variation-spring-like-motion-with-linear","Variation: spring-like motion with linear()",[14,14553,14554,14556,14557,14559],{},[18,14555,6773],{}," has exactly two control points, so it can describe one acceleration and one deceleration but not a true multi-bounce spring. The ",[18,14558,13624],{}," function fills that gap by accepting a list of progress stops, letting you approximate any curve as a piecewise-linear sequence — including a damped bounce.",[281,14561,14563],{"className":438,"code":14562,"language":440,"meta":286,"style":286},":root {\n  \u002F* A damped spring approximated by linear() progress stops *\u002F\n  --ease-spring: linear(\n    0, 0.18 8%, 0.55 18%, 0.92 30%, 1.08 42%,\n    1 55%, 0.97 65%, 1 80%, 1\n  );\n}\n\n.toast {\n  transition: transform var(--dur-base) var(--ease-spring);\n  transform: translateY(-120%);\n}\n.toast[data-visible] {\n  transform: translateY(0);\n}\n\n\u002F* Graceful fallback: older engines ignore the unknown function and\n   fall back to the previously declared (or default) timing function *\u002F\n@supports not (transition-timing-function: linear(0, 1)) {\n  .toast { transition-timing-function: var(--ease-overshoot); }\n}\n",[18,14564,14565,14571,14576,14587,14632,14666,14670,14674,14678,14684,14707,14724,14728,14740,14754,14758,14762,14767,14772,14797,14816],{"__ignoreMap":286},[290,14566,14567,14569],{"class":163,"line":292},[290,14568,1554],{"class":303},[290,14570,450],{"class":295},[290,14572,14573],{"class":163,"line":330},[290,14574,14575],{"class":455},"  \u002F* A damped spring approximated by linear() progress stops *\u002F\n",[290,14577,14578,14581,14583,14585],{"class":163,"line":337},[290,14579,14580],{"class":1561},"  --ease-spring",[290,14582,465],{"class":295},[290,14584,5456],{"class":461},[290,14586,7322],{"class":295},[290,14588,14589,14592,14594,14596,14598,14600,14602,14605,14608,14610,14612,14615,14618,14620,14622,14625,14628,14630],{"class":163,"line":364},[290,14590,14591],{"class":461},"    0",[290,14593,569],{"class":295},[290,14595,178],{"class":461},[290,14597,3214],{"class":461},[290,14599,11018],{"class":541},[290,14601,569],{"class":295},[290,14603,14604],{"class":461},"0.55",[290,14606,14607],{"class":461}," 18",[290,14609,11018],{"class":541},[290,14611,569],{"class":295},[290,14613,14614],{"class":461},"0.92",[290,14616,14617],{"class":461}," 30",[290,14619,11018],{"class":541},[290,14621,569],{"class":295},[290,14623,14624],{"class":461},"1.08",[290,14626,14627],{"class":461}," 42",[290,14629,11018],{"class":541},[290,14631,548],{"class":295},[290,14633,14634,14637,14640,14642,14644,14647,14650,14652,14654,14656,14659,14661,14663],{"class":163,"line":386},[290,14635,14636],{"class":461},"    1",[290,14638,14639],{"class":461}," 55",[290,14641,11018],{"class":541},[290,14643,569],{"class":295},[290,14645,14646],{"class":461},"0.97",[290,14648,14649],{"class":461}," 65",[290,14651,11018],{"class":541},[290,14653,569],{"class":295},[290,14655,468],{"class":461},[290,14657,14658],{"class":461}," 80",[290,14660,11018],{"class":541},[290,14662,569],{"class":295},[290,14664,14665],{"class":461},"1\n",[290,14667,14668],{"class":163,"line":408},[290,14669,8205],{"class":295},[290,14671,14672],{"class":163,"line":428},[290,14673,620],{"class":295},[290,14675,14676],{"class":163,"line":517},[290,14677,334],{"emptyLinePlaceholder":333},[290,14679,14680,14682],{"class":163,"line":523},[290,14681,7117],{"class":303},[290,14683,450],{"class":295},[290,14685,14686,14688,14690,14692,14694,14696,14698,14700,14702,14705],{"class":163,"line":532},[290,14687,526],{"class":461},[290,14689,1880],{"class":295},[290,14691,1622],{"class":461},[290,14693,484],{"class":295},[290,14695,14211],{"class":1561},[290,14697,490],{"class":295},[290,14699,1622],{"class":461},[290,14701,484],{"class":295},[290,14703,14704],{"class":1561},"--ease-spring",[290,14706,500],{"class":295},[290,14708,14709,14711,14713,14715,14717,14720,14722],{"class":163,"line":551},[290,14710,476],{"class":461},[290,14712,465],{"class":295},[290,14714,481],{"class":461},[290,14716,484],{"class":295},[290,14718,14719],{"class":461},"-120",[290,14721,11018],{"class":541},[290,14723,500],{"class":295},[290,14725,14726],{"class":163,"line":586},[290,14727,620],{"class":295},[290,14729,14730,14732,14734,14737],{"class":163,"line":602},[290,14731,7117],{"class":303},[290,14733,1140],{"class":295},[290,14735,14736],{"class":303},"data-visible",[290,14738,14739],{"class":295},"] {\n",[290,14741,14742,14744,14746,14748,14750,14752],{"class":163,"line":617},[290,14743,476],{"class":461},[290,14745,465],{"class":295},[290,14747,481],{"class":461},[290,14749,484],{"class":295},[290,14751,487],{"class":461},[290,14753,500],{"class":295},[290,14755,14756],{"class":163,"line":623},[290,14757,620],{"class":295},[290,14759,14760],{"class":163,"line":628},[290,14761,334],{"emptyLinePlaceholder":333},[290,14763,14764],{"class":163,"line":634},[290,14765,14766],{"class":455},"\u002F* Graceful fallback: older engines ignore the unknown function and\n",[290,14768,14769],{"class":163,"line":649},[290,14770,14771],{"class":455},"   fall back to the previously declared (or default) timing function *\u002F\n",[290,14773,14774,14776,14778,14780,14782,14784,14786,14788,14790,14792,14794],{"class":163,"line":660},[290,14775,2086],{"class":541},[290,14777,2116],{"class":541},[290,14779,3595],{"class":295},[290,14781,13646],{"class":461},[290,14783,465],{"class":295},[290,14785,5456],{"class":461},[290,14787,484],{"class":295},[290,14789,487],{"class":461},[290,14791,569],{"class":295},[290,14793,468],{"class":461},[290,14795,14796],{"class":295},")) {\n",[290,14798,14799,14802,14804,14806,14808,14810,14812,14814],{"class":163,"line":688},[290,14800,14801],{"class":303},"  .toast",[290,14803,790],{"class":295},[290,14805,13646],{"class":461},[290,14807,465],{"class":295},[290,14809,1622],{"class":461},[290,14811,484],{"class":295},[290,14813,14220],{"class":1561},[290,14815,1122],{"class":295},[290,14817,14818],{"class":163,"line":693},[290,14819,620],{"class":295},[14,14821,14822],{},"The toast slides in, slightly overshoots past its resting position, settles back, and gently re-corrects — motion that genuinely reads as spring physics, achieved with no JavaScript and no keyframes.",[47,14824],{},[50,14826,1299],{"id":1298},[14,14828,14829,14830,8393,14832,14834,14835,14837,14838,14841,14842,14844],{},"The named keywords, ",[18,14831,6773],{},[18,14833,3971],{}," have been supported in every evergreen browser for over a decade and need no fallback. The ",[18,14836,13624],{}," easing function is newer: it shipped in Chrome and Edge 113+, Safari 17.2+, and Firefox 112+. Because an unrecognised timing function is simply ignored, a ",[18,14839,14840],{},"@supports not (transition-timing-function: linear(0, 1))"," guard (as above) lets you supply a ",[18,14843,6773],{}," substitute for any engine that predates it without breaking the transition.",[47,14846],{},[50,14848,1316],{"id":1315},[14,14850,14851,14854,14856,14857,14859],{},[62,14852,14853],{},"What is the difference between ease and linear easing?",[18,14855,5456],{}," moves at a constant rate from start to finish, which reads as mechanical because nothing physical moves at a fixed velocity. ",[18,14858,6770],{}," starts slow, accelerates through the middle, and slows into the end, matching real-world acceleration and deceleration, so it feels natural for most UI motion.",[14,14861,14862,5735,14865,14867],{},[62,14863,14864],{},"When should I use steps() instead of a smooth curve?",[18,14866,3971],{}," when you want discrete, snapping motion rather than continuous interpolation. It suits sprite-sheet animations, typewriter and ticking effects, and segmented progress indicators where each frame should hold briefly before jumping to the next value.",[14,14869,14870,14873],{},[62,14871,14872],{},"Can cubic-bezier() create a bounce or overshoot effect?","\nYes. If a control point's y value rises above 1 the curve overshoots its target before settling, and if it drops below 0 it undershoots and snaps back. Keep both x values within 0 to 1, since x represents time and cannot run backwards.",[14,14875,14876,14879,14881],{},[62,14877,14878],{},"What does the linear() function add over cubic-bezier()?",[18,14880,13624],{}," takes a list of progress stops, so it can approximate multi-segment curves such as damped springs and bounces that a single two-control-point cubic Bezier cannot express. It is supported in Chrome and Edge 113+, Safari 17.2+, and Firefox 112+.",[47,14883],{},[50,14885,1391],{"id":1390},[1393,14887,14888,14893,14899,14904,14909],{},[1396,14889,14890,14892],{},[27,14891,30],{"href":29}," — the parent guide on transition syntax, compositing, and state changes.",[1396,14894,14895,14898],{},[27,14896,14897],{"href":10209},"Transitioning display with allow-discrete"," — apply these curves to enter and exit transitions.",[1396,14900,14901,14903],{},[27,14902,3355],{"href":10147}," — structure easing and duration tokens at scale.",[1396,14905,14906,14908],{},[27,14907,1413],{"href":1412}," — collapse expressive easing for users who need it.",[1396,14910,14911,14913],{},[27,14912,12361],{"href":11312}," — the same token-driven thinking applied to responsive type sizing.",[1430,14915,14916],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":286,"searchDepth":330,"depth":330,"links":14918},[14919,14920,14921,14922,14923,14924,14925,14926],{"id":13633,"depth":330,"text":13634},{"id":13664,"depth":330,"text":13665},{"id":13771,"depth":330,"text":13772},{"id":14513,"depth":330,"text":14514},{"id":14550,"depth":330,"text":14551},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Master CSS timing functions: ease, cubic-bezier(), steps(), and linear() easing. Understand perceptual differences and build reusable easing tokens without JS.",{"seoTitle":14929,"datePublished":1447,"dateModified":1447,"faq":14930},"CSS Transition Timing Functions Guide",[14931,14933,14935,14937],{"q":14853,"a":14932},"linear moves at a constant rate from start to finish, which reads as mechanical. ease starts slow, speeds up in the middle, and slows down at the end, which matches how real objects accelerate and decelerate and feels more natural for UI motion.",{"q":14864,"a":14934},"Use steps() when you want discrete, snapping motion rather than continuous interpolation. It is ideal for sprite-sheet animations, typewriter or ticking effects, and segmented progress indicators where each frame should hold before jumping to the next.",{"q":14872,"a":14936},"Yes. If a control point's y value goes above 1 the curve overshoots its target before settling, and if it goes below 0 it undershoots. Keep both x values within 0 to 1 to avoid time-reversal artifacts.",{"q":14878,"a":14938},"linear() accepts a list of progress stops, letting you approximate multi-segment curves like springs and bounces that a single cubic Bezier cannot represent. It is supported in Chrome 113+, Safari 17.2+, and Firefox 112+.","\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fcss-transition-timing-functions",{"title":13605,"description":14927},"css-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Fcss-transition-timing-functions\u002Findex","aHSp0Z8NxWh_EpOVXPCrprzjB-xjFYVMAJEfUH1ZO9g",{"id":14944,"title":14945,"body":14946,"description":16351,"extension":1444,"meta":16352,"navigation":333,"path":16366,"seo":16367,"stem":16368,"__hash__":16369},"content\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Findex.md","CSS Transition Fundamentals: Architecture, Performance & Patterns",{"type":7,"value":14947,"toc":16325},[14948,14951,14957,14962,14976,15021,15023,15027,15042,15046,15052,15069,15211,15222,15224,15228,15234,15238,15241,15420,15424,15431,15433,15437,15451,15455,15465,15559,15566,15571,15573,15577,15590,15596,15610,15635,15729,15733,15739,15823,15825,15829,15843,15851,15875,15877,15881,15945,15950,16047,16049,16053,16146,16148,16213,16215,16219,16230,16245,16264,16284,16286,16288,16322],[10,14949,14945],{"id":14950},"css-transition-fundamentals-architecture-performance-patterns",[14,14952,14953,14954,14956],{},"Mastering CSS Transition Fundamentals is critical for building responsive, performant interfaces. Unlike imperative JavaScript animations, declarative transitions bridge UI state changes with minimal overhead and native browser optimization. This guide covers spec-compliant syntax, component-scoped architecture, and GPU compositing strategies. We will explore how transitions integrate into broader ",[27,14955,1481],{"href":1480}," systems, ensuring smooth UX without triggering costly layout recalculations.",[14,14958,14959],{},[62,14960,14961],{},"Key Implementation Points:",[1393,14963,14964,14967,14970,14973],{},[1396,14965,14966],{},"Declarative vs imperative animation paradigms",[1396,14968,14969],{},"Spec-compliant transition syntax & shorthand parsing",[1396,14971,14972],{},"GPU compositing & transform isolation techniques",[1396,14974,14975],{},"Component-scoped transition architecture & state management",[133,14977,140,14979,140,14982,140,14985,140,14988,140,14991,140,14995,140,14999,140,15001,140,15004,140,15007,140,15011,140,15014,140,15017],{"viewBox":4133,"role":136,"ariaLabel":14978,"xmlns":138,"style":139},"Anatomy of a transition from a start state to an end state over time",[142,14980,14981],{},"Transition timeline anatomy",[146,14983,14984],{},"A start state and end state connected by a duration, delay, and timing function along a time axis.",[150,14986,14987],{"x":152,"y":2598,"style":154},"Anatomy of a state transition",[163,14989],{"x1":9105,"y1":2622,"x2":14990,"y2":2622,"stroke":167,"strokeWidth":168,"opacity":798},"640",[150,14992,14994],{"x":9105,"y":8947,"style":14993},"text-anchor:start;fill:currentColor;font:12px sans-serif;opacity:0.7","t = 0",[150,14996,14998],{"x":14990,"y":8947,"style":14997},"text-anchor:end;fill:currentColor;font:12px sans-serif;opacity:0.7","t = end",[171,15000],{"x":1788,"y":165,"width":4147,"height":1786,"rx":176,"fill":177,"opacity":178,"stroke":167,"strokeWidth":168},[150,15002,15003],{"x":1843,"y":5909,"style":5883},"start state",[171,15005],{"x":15006,"y":165,"width":4147,"height":1786,"rx":176,"fill":177,"opacity":1813,"stroke":167,"strokeWidth":168},"544",[150,15008,15010],{"x":15009,"y":5909,"style":5883},"604","end state",[253,15012],{"d":15013,"fill":72,"stroke":177,"strokeWidth":10258},"M180 130 C 300 130, 420 130, 540 130",[150,15015,15016],{"x":11114,"y":5115,"style":12455},"transition-delay",[150,15018,15020],{"x":15019,"y":5115,"style":12455},"420","duration + easing",[47,15022],{},[50,15024,15026],{"id":15025},"the-transition-property-shorthand-syntax","The Transition Property & Shorthand Syntax",[14,15028,1517,15029,15031,15032,569,15035,569,15037,8393,15039,15041],{},[18,15030,887],{}," property is a shorthand for four longhand properties: ",[18,15033,15034],{},"transition-property",[18,15036,4873],{},[18,15038,13646],{},[18,15040,15016],{},". Understanding the parsing order is essential for predictable state changes.",[2757,15043,15045],{"id":15044},"spec-compliant-parsing-order","Spec-Compliant Parsing Order",[14,15047,15048,15049],{},"Browsers parse the shorthand in this strict sequence:\n",[18,15050,15051],{},"transition: \u003Cproperty> \u003Cduration> \u003Ctiming-function> \u003Cdelay>;",[14,15053,15054,15055,15057,15058,15060,15061,15064,15065,15068],{},"If only two time values are provided, the first is always ",[18,15056,6940],{}," and the second is ",[18,15059,6993],{},". Omitting ",[18,15062,15063],{},"property"," defaults to ",[18,15066,15067],{},"all",", which is strongly discouraged in production due to performance overhead.",[281,15070,15072],{"className":438,"code":15071,"language":440,"meta":286,"style":286},"\u002F* ✅ Explicit, performant, and spec-compliant *\u002F\n.card {\n  transition:\n    transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),\n    opacity 0.2s ease-out;\n}\n\n.card:hover {\n  transform: translateY(-4px);\n  opacity: 0.95;\n}\n\n\u002F* ❌ Avoid: Forces browser to monitor every animatable property *\u002F\n.card {\n  transition: all 0.3s ease;\n}\n",[18,15073,15074,15079,15085,15091,15119,15131,15135,15139,15145,15161,15171,15175,15179,15184,15190,15207],{"__ignoreMap":286},[290,15075,15076],{"class":163,"line":292},[290,15077,15078],{"class":455},"\u002F* ✅ Explicit, performant, and spec-compliant *\u002F\n",[290,15080,15081,15083],{"class":163,"line":330},[290,15082,11528],{"class":303},[290,15084,450],{"class":295},[290,15086,15087,15089],{"class":163,"line":337},[290,15088,526],{"class":461},[290,15090,529],{"class":295},[290,15092,15093,15095,15097,15099,15101,15103,15105,15107,15109,15111,15113,15115,15117],{"class":163,"line":364},[290,15094,554],{"class":295},[290,15096,199],{"class":461},[290,15098,1886],{"class":541},[290,15100,561],{"class":461},[290,15102,484],{"class":295},[290,15104,169],{"class":461},[290,15106,569],{"class":295},[290,15108,487],{"class":461},[290,15110,569],{"class":295},[290,15112,566],{"class":461},[290,15114,569],{"class":295},[290,15116,468],{"class":461},[290,15118,583],{"class":295},[290,15120,15121,15123,15125,15127,15129],{"class":163,"line":386},[290,15122,535],{"class":295},[290,15124,566],{"class":461},[290,15126,1886],{"class":541},[290,15128,1889],{"class":461},[290,15130,471],{"class":295},[290,15132,15133],{"class":163,"line":408},[290,15134,620],{"class":295},[290,15136,15137],{"class":163,"line":428},[290,15138,334],{"emptyLinePlaceholder":333},[290,15140,15141,15143],{"class":163,"line":517},[290,15142,11814],{"class":303},[290,15144,450],{"class":295},[290,15146,15147,15149,15151,15153,15155,15157,15159],{"class":163,"line":523},[290,15148,476],{"class":461},[290,15150,465],{"class":295},[290,15152,481],{"class":461},[290,15154,484],{"class":295},[290,15156,3189],{"class":461},[290,15158,674],{"class":541},[290,15160,500],{"class":295},[290,15162,15163,15165,15167,15169],{"class":163,"line":532},[290,15164,462],{"class":461},[290,15166,465],{"class":295},[290,15168,1119],{"class":461},[290,15170,471],{"class":295},[290,15172,15173],{"class":163,"line":551},[290,15174,620],{"class":295},[290,15176,15177],{"class":163,"line":586},[290,15178,334],{"emptyLinePlaceholder":333},[290,15180,15181],{"class":163,"line":602},[290,15182,15183],{"class":455},"\u002F* ❌ Avoid: Forces browser to monitor every animatable property *\u002F\n",[290,15185,15186,15188],{"class":163,"line":617},[290,15187,11528],{"class":303},[290,15189,450],{"class":295},[290,15191,15192,15194,15196,15198,15201,15203,15205],{"class":163,"line":623},[290,15193,526],{"class":461},[290,15195,465],{"class":295},[290,15197,15067],{"class":461},[290,15199,15200],{"class":461}," 0.3",[290,15202,1886],{"class":541},[290,15204,545],{"class":461},[290,15206,471],{"class":295},[290,15208,15209],{"class":163,"line":628},[290,15210,620],{"class":295},[14,15212,15213,2694,15216,15221],{},[62,15214,15215],{},"Spec Reference:",[27,15217,15220],{"href":15218,"rel":15219},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-transitions-1\u002F",[13489],"CSS Transitions Module Level 1 (W3C)"," defines the exact parsing algorithm and fallback behavior for malformed shorthand declarations.",[47,15223],{},[50,15225,15227],{"id":15226},"component-architecture-state-management","Component Architecture & State Management",[14,15229,15230,15231,15233],{},"Transitions should be scoped to interactive states without bleeding into global styles. Aligning with modern ",[27,15232,3345],{"href":3344}," methodologies, we leverage CSS custom properties to create predictable, theme-aware transition tokens.",[2757,15235,15237],{"id":15236},"dynamic-easing-duration-via-custom-properties","Dynamic Easing & Duration via Custom Properties",[14,15239,15240],{},"By defining transition parameters at the component root, you enable runtime overrides without duplicating transition logic across modifiers.",[281,15242,15244],{"className":438,"code":15243,"language":440,"meta":286,"style":286},".btn {\n  --transition-speed: 0.2s;\n  --transition-ease: ease-out;\n  transition:\n    background-color var(--transition-speed) var(--transition-ease),\n    transform var(--transition-speed) var(--transition-ease);\n}\n\n.btn--primary {\n  --transition-speed: 0.3s;\n  --transition-ease: cubic-bezier(0.34, 1.56, 0.64, 1);\n}\n\n.btn--loading {\n  --transition-speed: 0.4s;\n  --transition-ease: linear;\n}\n",[18,15245,15246,15253,15266,15277,15283,15306,15326,15330,15334,15341,15353,15379,15383,15387,15394,15406,15416],{"__ignoreMap":286},[290,15247,15248,15251],{"class":163,"line":292},[290,15249,15250],{"class":303},".btn",[290,15252,450],{"class":295},[290,15254,15255,15258,15260,15262,15264],{"class":163,"line":330},[290,15256,15257],{"class":1561},"  --transition-speed",[290,15259,465],{"class":295},[290,15261,566],{"class":461},[290,15263,1886],{"class":541},[290,15265,471],{"class":295},[290,15267,15268,15271,15273,15275],{"class":163,"line":337},[290,15269,15270],{"class":1561},"  --transition-ease",[290,15272,465],{"class":295},[290,15274,5411],{"class":461},[290,15276,471],{"class":295},[290,15278,15279,15281],{"class":163,"line":364},[290,15280,526],{"class":461},[290,15282,529],{"class":295},[290,15284,15285,15288,15290,15292,15295,15297,15299,15301,15304],{"class":163,"line":386},[290,15286,15287],{"class":295},"    background-color ",[290,15289,1622],{"class":461},[290,15291,484],{"class":295},[290,15293,15294],{"class":1561},"--transition-speed",[290,15296,490],{"class":295},[290,15298,1622],{"class":461},[290,15300,484],{"class":295},[290,15302,15303],{"class":1561},"--transition-ease",[290,15305,583],{"class":295},[290,15307,15308,15310,15312,15314,15316,15318,15320,15322,15324],{"class":163,"line":408},[290,15309,554],{"class":295},[290,15311,1622],{"class":461},[290,15313,484],{"class":295},[290,15315,15294],{"class":1561},[290,15317,490],{"class":295},[290,15319,1622],{"class":461},[290,15321,484],{"class":295},[290,15323,15303],{"class":1561},[290,15325,500],{"class":295},[290,15327,15328],{"class":163,"line":428},[290,15329,620],{"class":295},[290,15331,15332],{"class":163,"line":517},[290,15333,334],{"emptyLinePlaceholder":333},[290,15335,15336,15339],{"class":163,"line":523},[290,15337,15338],{"class":303},".btn--primary",[290,15340,450],{"class":295},[290,15342,15343,15345,15347,15349,15351],{"class":163,"line":532},[290,15344,15257],{"class":1561},[290,15346,465],{"class":295},[290,15348,199],{"class":461},[290,15350,1886],{"class":541},[290,15352,471],{"class":295},[290,15354,15355,15357,15359,15361,15363,15365,15367,15369,15371,15373,15375,15377],{"class":163,"line":551},[290,15356,15270],{"class":1561},[290,15358,465],{"class":295},[290,15360,11767],{"class":461},[290,15362,484],{"class":295},[290,15364,14044],{"class":461},[290,15366,569],{"class":295},[290,15368,14049],{"class":461},[290,15370,569],{"class":295},[290,15372,14054],{"class":461},[290,15374,569],{"class":295},[290,15376,468],{"class":461},[290,15378,500],{"class":295},[290,15380,15381],{"class":163,"line":586},[290,15382,620],{"class":295},[290,15384,15385],{"class":163,"line":602},[290,15386,334],{"emptyLinePlaceholder":333},[290,15388,15389,15392],{"class":163,"line":617},[290,15390,15391],{"class":303},".btn--loading",[290,15393,450],{"class":295},[290,15395,15396,15398,15400,15402,15404],{"class":163,"line":623},[290,15397,15257],{"class":1561},[290,15399,465],{"class":295},[290,15401,169],{"class":461},[290,15403,1886],{"class":541},[290,15405,471],{"class":295},[290,15407,15408,15410,15412,15414],{"class":163,"line":628},[290,15409,15270],{"class":1561},[290,15411,465],{"class":295},[290,15413,5456],{"class":461},[290,15415,471],{"class":295},[290,15417,15418],{"class":163,"line":634},[290,15419,620],{"class":295},[2757,15421,15423],{"id":15422},"avoiding-nested-conflicts","Avoiding Nested Conflicts",[14,15425,15426,15427,15430],{},"When components inherit transitions from parent containers, use ",[18,15428,15429],{},"transition: none"," on child elements that require instant state changes. This prevents unintended cascade behavior in deeply nested BEM or utility-class trees.",[47,15432],{},[50,15434,15436],{"id":15435},"performance-compositing-layers","Performance & Compositing Layers",[14,15438,15439,15440,569,15442,569,15444,569,15446,569,15448,15450],{},"Transitions only perform optimally when confined to properties handled by the browser's compositor thread. Animating layout-triggering properties (",[18,15441,1748],{},[18,15443,2722],{},[18,15445,2728],{},[18,15447,2731],{},[18,15449,2725],{},") forces synchronous repaint and reflow, causing jank.",[2757,15452,15454],{"id":15453},"compositor-only-properties","Compositor-Only Properties",[14,15456,15457,15458,569,15460,8393,15462,15464],{},"Stick to ",[18,15459,103],{},[18,15461,76],{},[18,15463,6792],{}," (with caution). These properties are isolated to the GPU compositor, bypassing the main thread entirely.",[281,15466,15468],{"className":438,"code":15467,"language":440,"meta":286,"style":286},".interactive-panel {\n  \u002F* Force layer promotion to avoid first-frame layout recalculation *\u002F\n  transform: translateZ(0);\n  will-change: transform;\n  transition: transform 0.25s ease;\n}\n\n.interactive-panel:hover {\n  transform: translateZ(0) scale(1.02);\n}\n",[18,15469,15470,15477,15482,15497,15504,15518,15522,15526,15533,15555],{"__ignoreMap":286},[290,15471,15472,15475],{"class":163,"line":292},[290,15473,15474],{"class":303},".interactive-panel",[290,15476,450],{"class":295},[290,15478,15479],{"class":163,"line":330},[290,15480,15481],{"class":455},"  \u002F* Force layer promotion to avoid first-frame layout recalculation *\u002F\n",[290,15483,15484,15486,15488,15491,15493,15495],{"class":163,"line":337},[290,15485,476],{"class":461},[290,15487,465],{"class":295},[290,15489,15490],{"class":461},"translateZ",[290,15492,484],{"class":295},[290,15494,487],{"class":461},[290,15496,500],{"class":295},[290,15498,15499,15501],{"class":163,"line":364},[290,15500,3518],{"class":461},[290,15502,15503],{"class":295},": transform;\n",[290,15505,15506,15508,15510,15512,15514,15516],{"class":163,"line":386},[290,15507,526],{"class":461},[290,15509,1880],{"class":295},[290,15511,1668],{"class":461},[290,15513,1886],{"class":541},[290,15515,545],{"class":461},[290,15517,471],{"class":295},[290,15519,15520],{"class":163,"line":408},[290,15521,620],{"class":295},[290,15523,15524],{"class":163,"line":428},[290,15525,334],{"emptyLinePlaceholder":333},[290,15527,15528,15531],{"class":163,"line":517},[290,15529,15530],{"class":303},".interactive-panel:hover",[290,15532,450],{"class":295},[290,15534,15535,15537,15539,15541,15543,15545,15547,15549,15551,15553],{"class":163,"line":523},[290,15536,476],{"class":461},[290,15538,465],{"class":295},[290,15540,15490],{"class":461},[290,15542,484],{"class":295},[290,15544,487],{"class":461},[290,15546,490],{"class":295},[290,15548,493],{"class":461},[290,15550,484],{"class":295},[290,15552,2057],{"class":461},[290,15554,500],{"class":295},[290,15556,15557],{"class":163,"line":532},[290,15558,620],{"class":295},[2757,15560,15562,15563,15565],{"id":15561},"strategic-will-change-usage","Strategic ",[18,15564,3797],{}," Usage",[14,15567,15568,15570],{},[18,15569,3797],{}," is a hint, not a guarantee. Overusing it consumes significant GPU memory. Apply it dynamically via JavaScript or CSS only when the element is about to transition, and remove it post-animation to free resources.",[47,15572],{},[50,15574,15576],{"id":15575},"advanced-easing-timing-functions","Advanced Easing & Timing Functions",[14,15578,15579,15580,15582,15583,15585,15586,15589],{},"Default easing (",[18,15581,6770],{},") is rarely optimal for production UIs. Fine-tuning ",[18,15584,11767],{}," and leveraging discrete stepping enables physics-based micro-interactions and precise feedback loops; the deep dive on ",[27,15587,15588],{"href":1405},"CSS transition timing functions"," walks through choosing curves for entrances, exits, and snappy feedback.",[2757,15591,15593,15594],{"id":15592},"mathematical-breakdown-of-cubic-bezier","Mathematical Breakdown of ",[18,15595,11767],{},[14,15597,15598,15599,15601,15602,15605,15606,15609],{},"The function ",[18,15600,14520],{}," defines a cubic Bézier curve where ",[18,15603,15604],{},"x"," values represent time (0–1) and ",[18,15607,15608],{},"y"," values represent progress (0–1).",[1393,15611,15612,15618,15624],{},[1396,15613,15614,15617],{},[18,15615,15616],{},"y > 1"," creates overshoot (elastic bounce)",[1396,15619,15620,15623],{},[18,15621,15622],{},"y \u003C 0"," creates undershoot (snap-back)",[1396,15625,15626,15627,15629,15630,69,15632,15634],{},"Keep ",[18,15628,15604],{}," values between ",[18,15631,487],{},[18,15633,468],{}," to prevent time reversal artifacts.",[281,15636,15638],{"className":438,"code":15637,"language":440,"meta":286,"style":286},"\u002F* Physics-based snap *\u002F\n.modal-enter {\n  transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);\n}\n\n\u002F* Discrete step animation for loaders\u002Fprogress *\u002F\n.progress-bar {\n  transition: width 0.5s steps(10, jump-end);\n}\n",[18,15639,15640,15645,15652,15685,15689,15693,15698,15705,15725],{"__ignoreMap":286},[290,15641,15642],{"class":163,"line":292},[290,15643,15644],{"class":455},"\u002F* Physics-based snap *\u002F\n",[290,15646,15647,15650],{"class":163,"line":330},[290,15648,15649],{"class":303},".modal-enter",[290,15651,450],{"class":295},[290,15653,15654,15656,15658,15660,15662,15664,15666,15669,15671,15674,15676,15678,15680,15683],{"class":163,"line":337},[290,15655,526],{"class":461},[290,15657,1880],{"class":295},[290,15659,169],{"class":461},[290,15661,1886],{"class":541},[290,15663,561],{"class":461},[290,15665,484],{"class":295},[290,15667,15668],{"class":461},"0.175",[290,15670,569],{"class":295},[290,15672,15673],{"class":461},"0.885",[290,15675,569],{"class":295},[290,15677,5906],{"class":461},[290,15679,569],{"class":295},[290,15681,15682],{"class":461},"1.275",[290,15684,500],{"class":295},[290,15686,15687],{"class":163,"line":364},[290,15688,620],{"class":295},[290,15690,15691],{"class":163,"line":386},[290,15692,334],{"emptyLinePlaceholder":333},[290,15694,15695],{"class":163,"line":408},[290,15696,15697],{"class":455},"\u002F* Discrete step animation for loaders\u002Fprogress *\u002F\n",[290,15699,15700,15703],{"class":163,"line":428},[290,15701,15702],{"class":303},".progress-bar",[290,15704,450],{"class":295},[290,15706,15707,15709,15712,15714,15716,15718,15720,15722],{"class":163,"line":517},[290,15708,526],{"class":461},[290,15710,15711],{"class":295},": width ",[290,15713,798],{"class":461},[290,15715,1886],{"class":541},[290,15717,11040],{"class":461},[290,15719,484],{"class":295},[290,15721,836],{"class":461},[290,15723,15724],{"class":295},", jump-end);\n",[290,15726,15727],{"class":163,"line":523},[290,15728,620],{"class":295},[2757,15730,15732],{"id":15731},"accessibility-motion-sensitivity","Accessibility & Motion Sensitivity",[14,15734,15735,15736,15738],{},"Always respect ",[18,15737,2584],{},". Wrap transition declarations in a media query to disable or simplify animations for users with vestibular disorders.",[281,15740,15742],{"className":438,"code":15741,"language":440,"meta":286,"style":286},"@media (prefers-reduced-motion: reduce) {\n  *,\n  *::before,\n  *::after {\n    transition-duration: 0.01ms !important;\n    transition-delay: 0ms !important;\n    animation-duration: 0.01ms !important;\n  }\n}\n",[18,15743,15744,15750,15756,15764,15772,15786,15801,15815,15819],{"__ignoreMap":286},[290,15745,15746,15748],{"class":163,"line":292},[290,15747,874],{"class":541},[290,15749,877],{"class":295},[290,15751,15752,15754],{"class":163,"line":330},[290,15753,2802],{"class":299},[290,15755,548],{"class":295},[290,15757,15758,15760,15762],{"class":163,"line":337},[290,15759,2802],{"class":299},[290,15761,2811],{"class":303},[290,15763,548],{"class":295},[290,15765,15766,15768,15770],{"class":163,"line":364},[290,15767,2802],{"class":299},[290,15769,2412],{"class":303},[290,15771,450],{"class":295},[290,15773,15774,15776,15778,15780,15782,15784],{"class":163,"line":386},[290,15775,2856],{"class":461},[290,15777,465],{"class":295},[290,15779,2831],{"class":461},[290,15781,542],{"class":541},[290,15783,2836],{"class":541},[290,15785,471],{"class":295},[290,15787,15788,15791,15793,15795,15797,15799],{"class":163,"line":408},[290,15789,15790],{"class":461},"    transition-delay",[290,15792,465],{"class":295},[290,15794,487],{"class":461},[290,15796,542],{"class":541},[290,15798,2836],{"class":541},[290,15800,471],{"class":295},[290,15802,15803,15805,15807,15809,15811,15813],{"class":163,"line":428},[290,15804,2826],{"class":461},[290,15806,465],{"class":295},[290,15808,2831],{"class":461},[290,15810,542],{"class":541},[290,15812,2836],{"class":541},[290,15814,471],{"class":295},[290,15816,15817],{"class":163,"line":517},[290,15818,771],{"class":295},[290,15820,15821],{"class":163,"line":523},[290,15822,620],{"class":295},[47,15824],{},[50,15826,15828],{"id":15827},"transition-vs-keyframe-integration","Transition vs Keyframe Integration",[14,15830,15831,15832,15834,15835,15838,15839,15842],{},"Understanding when to use transitions versus ",[18,15833,3968],{}," dictates animation architecture. Transitions are ",[62,15836,15837],{},"state-driven"," (A → B), while keyframes are ",[62,15840,15841],{},"time-driven"," (A → B → C → A).",[14,15844,15845,15846,15848,15849,42],{},"Use transitions for hover, focus, and toggle states where start\u002Fend values are deterministic. Reserve ",[18,15847,3968],{}," for complex, multi-step sequences, looping backgrounds, or entrance\u002Fexit choreography that requires intermediate control points. In practice, they complement each other: a transition can handle the hover trigger, while a keyframe manages the internal loading spinner, as documented in ",[27,15850,1420],{"href":1419},[14,15852,15853,15856,15857,569,15859,15862,15863,15865,15866,15868,15869,15871,15872,15874],{},[62,15854,15855],{},"Graceful Fallback Strategy:"," For unsupported discrete properties (e.g., ",[18,15858,59],{},[18,15860,15861],{},"content-visibility","), pair ",[18,15864,34],{}," (Chrome 117+, Safari 17.4+) with a JavaScript fallback that toggles classes after a ",[18,15867,24],{}," matching the transition duration. The guide on ",[27,15870,10210],{"href":10209}," shows how to combine this with ",[18,15873,38],{}," for flicker-free enter\u002Fexit animations.",[47,15876],{},[50,15878,15880],{"id":15879},"browser-support-progressive-enhancement","Browser Support & Progressive Enhancement",[2250,15882,15883,15894],{},[2253,15884,15885],{},[2256,15886,15887,15889,15892],{},[2259,15888,3737],{},[2259,15890,15891],{},"Evergreen Support",[2259,15893,2267],{},[2269,15895,15896,15909,15921,15933],{},[2256,15897,15898,15903,15906],{},[2274,15899,15900,15902],{},[18,15901,887],{}," (shorthand)",[2274,15904,15905],{},"Chrome 1+, FF 4+, Safari 3.1+, Edge 12+",[2274,15907,15908],{},"Fully standardized",[2256,15910,15911,15915,15918],{},[2274,15912,15913],{},[18,15914,34],{},[2274,15916,15917],{},"Chrome 117+, Safari 17.4+",[2274,15919,15920],{},"Enables discrete property transitions",[2256,15922,15923,15927,15930],{},[2274,15924,15925],{},[18,15926,3797],{},[2274,15928,15929],{},"Chrome 36+, FF 36+, Safari 9+",[2274,15931,15932],{},"Memory-heavy; scope carefully",[2256,15934,15935,15939,15942],{},[2274,15936,15937],{},[18,15938,2584],{},[2274,15940,15941],{},"Chrome 74+, FF 63+, Safari 10.1+",[2274,15943,15944],{},"Critical for WCAG 2.1 AA compliance",[14,15946,15947],{},[62,15948,15949],{},"Progressive Enhancement Pattern:",[281,15951,15953],{"className":438,"code":15952,"language":440,"meta":286,"style":286},"\u002F* Base: Instant state change (works everywhere) *\u002F\n.btn {\n  background: #333;\n}\n.btn:hover {\n  background: #555;\n}\n\n\u002F* Enhanced: Smooth transition (modern browsers) *\u002F\n@media (prefers-reduced-motion: no-preference) {\n  .btn {\n    transition: background-color 0.2s ease;\n  }\n}\n",[18,15954,15955,15960,15966,15977,15981,15988,15999,16003,16007,16012,16019,16025,16039,16043],{"__ignoreMap":286},[290,15956,15957],{"class":163,"line":292},[290,15958,15959],{"class":455},"\u002F* Base: Instant state change (works everywhere) *\u002F\n",[290,15961,15962,15964],{"class":163,"line":330},[290,15963,15250],{"class":303},[290,15965,450],{"class":295},[290,15967,15968,15970,15972,15975],{"class":163,"line":337},[290,15969,1186],{"class":461},[290,15971,465],{"class":295},[290,15973,15974],{"class":461},"#333",[290,15976,471],{"class":295},[290,15978,15979],{"class":163,"line":364},[290,15980,620],{"class":295},[290,15982,15983,15986],{"class":163,"line":386},[290,15984,15985],{"class":303},".btn:hover",[290,15987,450],{"class":295},[290,15989,15990,15992,15994,15997],{"class":163,"line":408},[290,15991,1186],{"class":461},[290,15993,465],{"class":295},[290,15995,15996],{"class":461},"#555",[290,15998,471],{"class":295},[290,16000,16001],{"class":163,"line":428},[290,16002,620],{"class":295},[290,16004,16005],{"class":163,"line":517},[290,16006,334],{"emptyLinePlaceholder":333},[290,16008,16009],{"class":163,"line":523},[290,16010,16011],{"class":455},"\u002F* Enhanced: Smooth transition (modern browsers) *\u002F\n",[290,16013,16014,16016],{"class":163,"line":532},[290,16015,874],{"class":541},[290,16017,16018],{"class":295}," (prefers-reduced-motion: no-preference) {\n",[290,16020,16021,16023],{"class":163,"line":551},[290,16022,12752],{"class":303},[290,16024,450],{"class":295},[290,16026,16027,16029,16031,16033,16035,16037],{"class":163,"line":586},[290,16028,4745],{"class":461},[290,16030,4670],{"class":295},[290,16032,566],{"class":461},[290,16034,1886],{"class":541},[290,16036,545],{"class":461},[290,16038,471],{"class":295},[290,16040,16041],{"class":163,"line":602},[290,16042,771],{"class":295},[290,16044,16045],{"class":163,"line":617},[290,16046,620],{"class":295},[47,16048],{},[50,16050,16052],{"id":16051},"common-issues-devtools-debugging","Common Issues & DevTools Debugging",[2250,16054,16055,16065],{},[2253,16056,16057],{},[2256,16058,16059,16061,16063],{},[2259,16060,2338],{},[2259,16062,3876],{},[2259,16064,8563],{},[2269,16066,16067,16085,16109,16129],{},[2256,16068,16069,16072,16075],{},[2274,16070,16071],{},"Initial render flicker",[2274,16073,16074],{},"Transition applied before DOM paint",[2274,16076,16077,16078,16081,16082],{},"Defer class application until ",[18,16079,16080],{},"DOMContentLoaded"," or use ",[18,16083,16084],{},"@media (prefers-reduced-motion: no-preference)",[2256,16086,16087,16090,16101],{},[2274,16088,16089],{},"Layout thrashing",[2274,16091,16092,16093,3041,16095,3041,16097,3041,16099],{},"Animating ",[18,16094,1748],{},[18,16096,2722],{},[18,16098,2728],{},[18,16100,2731],{},[2274,16102,16103,16104,2351,16106],{},"Refactor to ",[18,16105,2071],{},[18,16107,16108],{},"translate()",[2256,16110,16111,16116,16119],{},[2274,16112,16113,16115],{},[18,16114,3797],{}," memory leak",[2274,16117,16118],{},"Persistent layer promotion",[2274,16120,16121,16122,16124,16125,3041,16127],{},"Remove via JS after ",[18,16123,2769],{},", or scope to ",[18,16126,3689],{},[18,16128,1532],{},[2256,16130,16131,16134,16137],{},[2274,16132,16133],{},"Inconsistent easing",[2274,16135,16136],{},"Vendor-specific curve interpretation",[2274,16138,16139,16140,16142,16143,16145],{},"Test with ",[18,16141,6773],{}," values; avoid ",[18,16144,3971],{}," on fractional durations",[2757,16147,13396],{"id":8357},[3017,16149,16150,16166,16189],{},[1396,16151,16152,16155,16156,16158,16159,69,16162,16165],{},[62,16153,16154],{},"Chrome\u002FEdge:"," Open DevTools → ",[18,16157,3024],{}," tab → Enable ",[18,16160,16161],{},"Paint flashing",[18,16163,16164],{},"Layer borders",". Hover the element to verify only the target layer repaints.",[1396,16167,16168,16155,16171,16174,16175,16178,16179,3041,16181,16183,16184,69,16186,16188],{},[62,16169,16170],{},"Firefox:",[18,16172,16173],{},"Performance"," panel → Record interaction. Check the ",[18,16176,16177],{},"Compositing"," waterfall to ensure ",[18,16180,103],{},[18,16182,76],{}," bypass ",[18,16185,13462],{},[18,16187,13465],{}," phases.",[1396,16190,16191,16194,16195,16198,16199,16198,16202,16205,16206,16209,16210,16212],{},[62,16192,16193],{},"Safari:"," Enable ",[18,16196,16197],{},"Web Inspector"," → ",[18,16200,16201],{},"Timelines",[18,16203,16204],{},"Layout & Rendering",". Look for ",[18,16207,16208],{},"Composite"," markers instead of ",[18,16211,13462],{}," during transition execution.",[47,16214],{},[50,16216,16218],{"id":16217},"frequently-asked-questions","Frequently Asked Questions",[14,16220,16221,16226,16227,16229],{},[62,16222,16223,16224,5734],{},"When should I use CSS transitions instead of ",[18,16225,3968],{},"\nUse transitions for state-driven changes (hover, focus, toggle) where the start and end states are known. Use ",[18,16228,3968],{}," for complex, multi-step, or time-driven sequences that require intermediate keyframes or looping.",[14,16231,16232,16235,16236,16238,16239,16241,16242,16244],{},[62,16233,16234],{},"How do I prevent transition flicker on initial page load?","\nApply the ",[18,16237,887],{}," property only after the initial render cycle. This can be achieved by adding a utility class via JavaScript after ",[18,16240,16080],{},", or by wrapping transition declarations in ",[18,16243,16084],{}," to defer parsing until the browser confirms motion preferences.",[14,16246,16247,16253,16254,16256,16257,16259,16260,69,16262,42],{},[62,16248,5714,16249,16252],{},[18,16250,16251],{},"transition: all"," negatively impact performance?","\nYes. Using ",[18,16255,15067],{}," forces the browser to monitor every CSS property for changes, increasing computational overhead during state changes. Always explicitly declare ",[18,16258,15034],{}," to limit monitoring to compositor-safe properties like ",[18,16261,103],{},[18,16263,76],{},[14,16265,16266,16272,16273,16275,16276,16081,16278,16280,16281,16283],{},[62,16267,16268,16269,16271],{},"How do I respect ",[18,16270,2584],{}," with CSS transitions?","\nWrap transition declarations in a ",[18,16274,4126],{}," block to override durations to ",[18,16277,6935],{},[18,16279,15429],{},". Alternatively, use ",[18,16282,34],{}," for modern browsers to handle discrete property changes gracefully without abrupt state jumps.",[47,16285],{},[50,16287,1391],{"id":1390},[1393,16289,16290,16295,16303,16308,16316],{},[1396,16291,16292,16294],{},[27,16293,15588],{"href":1405}," — picking easing curves for natural, intentional motion.",[1396,16296,16297,16299,16300,16302],{},[27,16298,14897],{"href":10209}," — animating ",[18,16301,59],{}," and other discrete properties without flicker.",[1396,16304,16305,16307],{},[27,16306,1481],{"href":1480}," — the parent guide covering transitions, keyframes, and motion accessibility.",[1396,16309,16310,16312,16313,16315],{},[27,16311,1420],{"href":1419}," — when to reach for time-driven ",[18,16314,3968],{}," instead of transitions.",[1396,16317,16318,16321],{},[27,16319,16320],{"href":5001},"Fluid typography without JavaScript"," — pairing transitions with responsive sizing from the container-queries guide.",[1430,16323,16324],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":286,"searchDepth":330,"depth":330,"links":16326},[16327,16330,16334,16339,16344,16345,16346,16349,16350],{"id":15025,"depth":330,"text":15026,"children":16328},[16329],{"id":15044,"depth":337,"text":15045},{"id":15226,"depth":330,"text":15227,"children":16331},[16332,16333],{"id":15236,"depth":337,"text":15237},{"id":15422,"depth":337,"text":15423},{"id":15435,"depth":330,"text":15436,"children":16335},[16336,16337],{"id":15453,"depth":337,"text":15454},{"id":15561,"depth":337,"text":16338},"Strategic will-change Usage",{"id":15575,"depth":330,"text":15576,"children":16340},[16341,16343],{"id":15592,"depth":337,"text":16342},"Mathematical Breakdown of cubic-bezier",{"id":15731,"depth":337,"text":15732},{"id":15827,"depth":330,"text":15828},{"id":15879,"depth":330,"text":15880},{"id":16051,"depth":330,"text":16052,"children":16347},[16348],{"id":8357,"depth":337,"text":13396},{"id":16217,"depth":330,"text":16218},{"id":1390,"depth":330,"text":1391},"CSS Transition Fundamentals: spec-compliant syntax, GPU compositing strategies, component-scoped architecture, and performance-optimised state change patterns.",{"seoTitle":16353,"datePublished":1447,"dateModified":1447,"faq":16354},"CSS Transition Fundamentals Guide",[16355,16358,16360,16363],{"q":16356,"a":16357},"When should I use CSS transitions instead of @keyframes?","Use transitions for state-driven changes such as hover, focus, and toggle where the start and end states are known. Use @keyframes for complex, multi-step, or time-driven sequences that require intermediate keyframes or looping.",{"q":16234,"a":16359},"Apply the transition property only after the initial render cycle, for example by adding a utility class via JavaScript after DOMContentLoaded, or by wrapping transition declarations in a prefers-reduced-motion: no-preference media query to defer parsing.",{"q":16361,"a":16362},"Does transition: all negatively impact performance?","Yes. Using all forces the browser to monitor every CSS property for changes, increasing overhead during state changes. Always declare transition-property explicitly to limit monitoring to compositor-safe properties like transform and opacity.",{"q":16364,"a":16365},"How do I respect prefers-reduced-motion with CSS transitions?","Wrap transition declarations in a prefers-reduced-motion: reduce block to override durations to near zero or use transition: none. For modern browsers you can pair this with transition-behavior: allow-discrete to handle discrete property changes gracefully.","\u002Fcss-only-micro-interactions-animations\u002Fcss-transition-fundamentals",{"title":14945,"description":16351},"css-only-micro-interactions-animations\u002Fcss-transition-fundamentals\u002Findex","2_-3QGyOrc1zlyxSrc4Dow72k0kBfHOn-YrXZR8XJcg",{"id":4,"title":5,"body":16371,"description":1443,"extension":1444,"meta":17423,"navigation":333,"path":1457,"seo":17429,"stem":1459,"__hash__":1460},{"type":7,"value":16372,"toc":17413},[16373,16375,16389,16391,16393,16395,16413,16429,16431,16433,16441,16491,16493,16495,16505,16621,16979,16995,16997,16999,17021,17023,17025,17037,17321,17323,17325,17333,17335,17337,17355,17365,17369,17385,17387,17389,17411],[10,16374,5],{"id":12},[14,16376,16,16377,21,16379,25,16381,31,16383,35,16385,39,16387,42],{},[18,16378,20],{},[18,16380,24],{},[27,16382,30],{"href":29},[18,16384,34],{},[18,16386,38],{},[18,16388,20],{},[14,16390,45],{},[47,16392],{},[50,16394,53],{"id":52},[14,16396,56,16397,60,16399,65,16401,69,16403,73,16405,77,16407,80,16409,84,16411,89],{},[18,16398,59],{},[62,16400,64],{},[18,16402,68],{},[18,16404,72],{},[18,16406,76],{},[18,16408,20],{},[18,16410,83],{},[86,16412,88],{},[14,16414,92,16415,95,16417,98,16419,69,16421,104,16423,107,16425,110,16427,113],{},[18,16416,34],{},[18,16418,83],{},[18,16420,76],{},[18,16422,103],{},[18,16424,72],{},[18,16426,38],{},[18,16428,24],{},[47,16430],{},[50,16432,119],{"id":118},[14,16434,122,16435,125,16437,128,16439,131],{},[18,16436,38],{},[18,16438,59],{},[18,16440,72],{},[133,16442,140,16443,140,16445,140,16447,140,140,16449,140,16451,140,16453,140,16455,140,16457,140,16459,140,16461,140,16463,140,16465,140,140,16467,140,16469,140,16471,140,16473,140,16475,140,16477,140,16479,140,16481,140,16483,140,16485],{"viewBox":135,"role":136,"ariaLabel":137,"xmlns":138,"style":139},[142,16444,144],{},[146,16446,148],{},[150,16448,155],{"x":152,"y":153,"style":154},[150,16450,161],{"x":158,"y":159,"style":160},[163,16452],{"x1":158,"y1":165,"x2":166,"y2":165,"stroke":167,"strokeWidth":168,"opacity":169},[171,16454],{"x":158,"y":173,"width":174,"height":175,"rx":176,"fill":177,"opacity":178},[150,16456,38],{"x":181,"y":182,"style":183},[150,16458,188],{"x":181,"y":186,"style":187},[163,16460],{"x1":191,"y1":192,"x2":193,"y2":192,"stroke":177,"strokeWidth":194,"markerEnd":195},[171,16462],{"x":198,"y":173,"width":174,"height":175,"rx":176,"fill":177,"opacity":199},[150,16464,203],{"x":202,"y":182,"style":183},[150,16466,206],{"x":202,"y":186,"style":187},[150,16468,211],{"x":158,"y":209,"style":210},[163,16470],{"x1":158,"y1":214,"x2":166,"y2":214,"stroke":167,"strokeWidth":168,"opacity":169},[171,16472],{"x":158,"y":217,"width":174,"height":175,"rx":176,"fill":177,"opacity":199},[150,16474,222],{"x":181,"y":220,"style":221},[150,16476,226],{"x":181,"y":225,"style":187},[163,16478],{"x1":191,"y1":229,"x2":193,"y2":229,"stroke":167,"strokeWidth":194,"markerEnd":195},[171,16480],{"x":198,"y":217,"width":174,"height":175,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},[150,16482,235],{"x":202,"y":220,"style":183},[150,16484,238],{"x":202,"y":225,"style":187},[240,16486,242,16487,140],{},[244,16488,251,16489,242],{"id":246,"markerWidth":247,"markerHeight":247,"refX":248,"refY":249,"orient":250},[253,16490],{"d":255,"fill":167},[47,16492],{},[50,16494,261],{"id":260},[14,16496,264,16497,268,16499,272,16501,276,16503,42],{},[18,16498,267],{},[18,16500,271],{},[18,16502,275],{},[18,16504,279],{},[281,16506,16507],{"className":283,"code":284,"language":285,"meta":286,"style":286},[18,16508,16509,16533,16537,16559,16577,16595,16613],{"__ignoreMap":286},[290,16510,16511,16513,16515,16517,16519,16521,16523,16525,16527,16529,16531],{"class":163,"line":292},[290,16512,296],{"class":295},[290,16514,300],{"class":299},[290,16516,304],{"class":303},[290,16518,307],{"class":295},[290,16520,311],{"class":310},[290,16522,314],{"class":303},[290,16524,307],{"class":295},[290,16526,319],{"class":310},[290,16528,322],{"class":295},[290,16530,300],{"class":299},[290,16532,327],{"class":295},[290,16534,16535],{"class":163,"line":330},[290,16536,334],{"emptyLinePlaceholder":333},[290,16538,16539,16541,16543,16545,16547,16549,16551,16553,16555,16557],{"class":163,"line":337},[290,16540,296],{"class":295},[290,16542,342],{"class":299},[290,16544,345],{"class":303},[290,16546,307],{"class":295},[290,16548,311],{"class":310},[290,16550,352],{"class":303},[290,16552,314],{"class":303},[290,16554,307],{"class":295},[290,16556,359],{"class":310},[290,16558,327],{"class":295},[290,16560,16561,16563,16565,16567,16569,16571,16573,16575],{"class":163,"line":364},[290,16562,367],{"class":295},[290,16564,14],{"class":299},[290,16566,314],{"class":303},[290,16568,307],{"class":295},[290,16570,376],{"class":310},[290,16572,379],{"class":295},[290,16574,14],{"class":299},[290,16576,327],{"class":295},[290,16578,16579,16581,16583,16585,16587,16589,16591,16593],{"class":163,"line":386},[290,16580,367],{"class":295},[290,16582,300],{"class":299},[290,16584,393],{"class":303},[290,16586,307],{"class":295},[290,16588,398],{"class":310},[290,16590,401],{"class":295},[290,16592,300],{"class":299},[290,16594,327],{"class":295},[290,16596,16597,16599,16601,16603,16605,16607,16609,16611],{"class":163,"line":408},[290,16598,367],{"class":295},[290,16600,300],{"class":299},[290,16602,393],{"class":303},[290,16604,307],{"class":295},[290,16606,398],{"class":310},[290,16608,421],{"class":295},[290,16610,300],{"class":299},[290,16612,327],{"class":295},[290,16614,16615,16617,16619],{"class":163,"line":428},[290,16616,431],{"class":295},[290,16618,342],{"class":299},[290,16620,327],{"class":295},[281,16622,16623],{"className":438,"code":439,"language":440,"meta":286,"style":286},[18,16624,16625,16631,16635,16645,16667,16671,16675,16679,16683,16689,16701,16729,16741,16753,16757,16761,16765,16775,16785,16809,16813,16817,16821,16825,16831,16841,16851,16875,16879,16883,16887,16907,16947,16951,16955,16961,16975],{"__ignoreMap":286},[290,16626,16627,16629],{"class":163,"line":292},[290,16628,447],{"class":303},[290,16630,450],{"class":295},[290,16632,16633],{"class":163,"line":330},[290,16634,456],{"class":455},[290,16636,16637,16639,16641,16643],{"class":163,"line":337},[290,16638,462],{"class":461},[290,16640,465],{"class":295},[290,16642,468],{"class":461},[290,16644,471],{"class":295},[290,16646,16647,16649,16651,16653,16655,16657,16659,16661,16663,16665],{"class":163,"line":364},[290,16648,476],{"class":461},[290,16650,465],{"class":295},[290,16652,481],{"class":461},[290,16654,484],{"class":295},[290,16656,487],{"class":461},[290,16658,490],{"class":295},[290,16660,493],{"class":461},[290,16662,484],{"class":295},[290,16664,468],{"class":461},[290,16666,500],{"class":295},[290,16668,16669],{"class":163,"line":386},[290,16670,334],{"emptyLinePlaceholder":333},[290,16672,16673],{"class":163,"line":408},[290,16674,509],{"class":455},[290,16676,16677],{"class":163,"line":428},[290,16678,514],{"class":455},[290,16680,16681],{"class":163,"line":517},[290,16682,520],{"class":455},[290,16684,16685,16687],{"class":163,"line":523},[290,16686,526],{"class":461},[290,16688,529],{"class":295},[290,16690,16691,16693,16695,16697,16699],{"class":163,"line":532},[290,16692,535],{"class":295},[290,16694,538],{"class":461},[290,16696,542],{"class":541},[290,16698,545],{"class":461},[290,16700,548],{"class":295},[290,16702,16703,16705,16707,16709,16711,16713,16715,16717,16719,16721,16723,16725,16727],{"class":163,"line":551},[290,16704,554],{"class":295},[290,16706,538],{"class":461},[290,16708,542],{"class":541},[290,16710,561],{"class":461},[290,16712,484],{"class":295},[290,16714,566],{"class":461},[290,16716,569],{"class":295},[290,16718,572],{"class":461},[290,16720,569],{"class":295},[290,16722,566],{"class":461},[290,16724,569],{"class":295},[290,16726,468],{"class":461},[290,16728,583],{"class":295},[290,16730,16731,16733,16735,16737,16739],{"class":163,"line":586},[290,16732,589],{"class":461},[290,16734,592],{"class":461},[290,16736,542],{"class":541},[290,16738,545],{"class":461},[290,16740,599],{"class":295},[290,16742,16743,16745,16747,16749,16751],{"class":163,"line":602},[290,16744,605],{"class":295},[290,16746,538],{"class":461},[290,16748,542],{"class":541},[290,16750,545],{"class":461},[290,16752,614],{"class":295},[290,16754,16755],{"class":163,"line":617},[290,16756,620],{"class":295},[290,16758,16759],{"class":163,"line":623},[290,16760,334],{"emptyLinePlaceholder":333},[290,16762,16763],{"class":163,"line":628},[290,16764,631],{"class":455},[290,16766,16767,16769,16771,16773],{"class":163,"line":634},[290,16768,637],{"class":303},[290,16770,640],{"class":295},[290,16772,643],{"class":299},[290,16774,646],{"class":295},[290,16776,16777,16779,16781,16783],{"class":163,"line":649},[290,16778,462],{"class":461},[290,16780,465],{"class":295},[290,16782,487],{"class":461},[290,16784,471],{"class":295},[290,16786,16787,16789,16791,16793,16795,16797,16799,16801,16803,16805,16807],{"class":163,"line":660},[290,16788,476],{"class":461},[290,16790,465],{"class":295},[290,16792,481],{"class":461},[290,16794,484],{"class":295},[290,16796,671],{"class":461},[290,16798,674],{"class":541},[290,16800,490],{"class":295},[290,16802,493],{"class":461},[290,16804,484],{"class":295},[290,16806,683],{"class":461},[290,16808,500],{"class":295},[290,16810,16811],{"class":163,"line":688},[290,16812,620],{"class":295},[290,16814,16815],{"class":163,"line":693},[290,16816,334],{"emptyLinePlaceholder":333},[290,16818,16819],{"class":163,"line":698},[290,16820,701],{"class":455},[290,16822,16823],{"class":163,"line":704},[290,16824,707],{"class":455},[290,16826,16827,16829],{"class":163,"line":710},[290,16828,38],{"class":541},[290,16830,450],{"class":295},[290,16832,16833,16835,16837,16839],{"class":163,"line":717},[290,16834,720],{"class":303},[290,16836,723],{"class":295},[290,16838,643],{"class":299},[290,16840,450],{"class":295},[290,16842,16843,16845,16847,16849],{"class":163,"line":730},[290,16844,733],{"class":461},[290,16846,465],{"class":295},[290,16848,487],{"class":461},[290,16850,471],{"class":295},[290,16852,16853,16855,16857,16859,16861,16863,16865,16867,16869,16871,16873],{"class":163,"line":742},[290,16854,745],{"class":461},[290,16856,465],{"class":295},[290,16858,481],{"class":461},[290,16860,484],{"class":295},[290,16862,671],{"class":461},[290,16864,674],{"class":541},[290,16866,490],{"class":295},[290,16868,493],{"class":461},[290,16870,484],{"class":295},[290,16872,683],{"class":461},[290,16874,500],{"class":295},[290,16876,16877],{"class":163,"line":768},[290,16878,771],{"class":295},[290,16880,16881],{"class":163,"line":774},[290,16882,620],{"class":295},[290,16884,16885],{"class":163,"line":779},[290,16886,334],{"emptyLinePlaceholder":333},[290,16888,16889,16891,16893,16895,16897,16899,16901,16903,16905],{"class":163,"line":784},[290,16890,787],{"class":303},[290,16892,790],{"class":295},[290,16894,793],{"class":461},[290,16896,465],{"class":295},[290,16898,798],{"class":461},[290,16900,801],{"class":541},[290,16902,804],{"class":461},[290,16904,801],{"class":541},[290,16906,809],{"class":295},[290,16908,16909,16911,16913,16915,16917,16919,16921,16923,16925,16927,16929,16931,16933,16935,16937,16939,16941,16943,16945],{"class":163,"line":812},[290,16910,447],{"class":303},[290,16912,790],{"class":295},[290,16914,793],{"class":461},[290,16916,465],{"class":295},[290,16918,823],{"class":461},[290,16920,801],{"class":541},[290,16922,828],{"class":295},[290,16924,831],{"class":461},[290,16926,465],{"class":295},[290,16928,836],{"class":461},[290,16930,674],{"class":541},[290,16932,828],{"class":295},[290,16934,843],{"class":461},[290,16936,465],{"class":295},[290,16938,468],{"class":461},[290,16940,674],{"class":541},[290,16942,852],{"class":461},[290,16944,855],{"class":461},[290,16946,809],{"class":295},[290,16948,16949],{"class":163,"line":860},[290,16950,334],{"emptyLinePlaceholder":333},[290,16952,16953],{"class":163,"line":865},[290,16954,868],{"class":455},[290,16956,16957,16959],{"class":163,"line":871},[290,16958,874],{"class":541},[290,16960,877],{"class":295},[290,16962,16963,16965,16967,16969,16971,16973],{"class":163,"line":880},[290,16964,720],{"class":303},[290,16966,790],{"class":295},[290,16968,887],{"class":461},[290,16970,465],{"class":295},[290,16972,72],{"class":461},[290,16974,809],{"class":295},[290,16976,16977],{"class":163,"line":896},[290,16978,620],{"class":295},[14,16980,901,16981,904,16983,908,16985,69,16987,913,16989,917,16991,920,16993,924],{},[18,16982,38],{},[18,16984,907],{},[18,16986,76],{},[18,16988,103],{},[18,16990,916],{},[18,16992,59],{},[18,16994,923],{},[47,16996],{},[50,16998,930],{"id":929},[14,17000,933,17001,936,17003,940,17005,943,17007,946,17009,949,17011,953,17013,957,17015,960,17017,963,17019,966],{},[18,17002,916],{},[18,17004,939],{},[18,17006,887],{},[18,17008,59],{},[18,17010,916],{},[86,17012,952],{},[86,17014,956],{},[18,17016,76],{},[18,17018,20],{},[18,17020,38],{},[47,17022],{},[50,17024,972],{"id":971},[14,17026,975,17027,978,17029,982,17031,69,17033,987,17035,42],{},[18,17028,279],{},[18,17030,981],{},[18,17032,59],{},[18,17034,923],{},[18,17036,916],{},[281,17038,17039],{"className":438,"code":992,"language":440,"meta":286,"style":286},[18,17040,17041,17047,17057,17071,17077,17089,17101,17113,17125,17129,17161,17165,17171,17201,17205,17209,17217,17239,17273,17277,17283,17317],{"__ignoreMap":286},[290,17042,17043,17045],{"class":163,"line":292},[290,17044,999],{"class":299},[290,17046,450],{"class":295},[290,17048,17049,17051,17053,17055],{"class":163,"line":330},[290,17050,462],{"class":461},[290,17052,465],{"class":295},[290,17054,468],{"class":461},[290,17056,471],{"class":295},[290,17058,17059,17061,17063,17065,17067,17069],{"class":163,"line":337},[290,17060,476],{"class":461},[290,17062,465],{"class":295},[290,17064,493],{"class":461},[290,17066,484],{"class":295},[290,17068,468],{"class":461},[290,17070,500],{"class":295},[290,17072,17073,17075],{"class":163,"line":364},[290,17074,526],{"class":461},[290,17076,529],{"class":295},[290,17078,17079,17081,17083,17085,17087],{"class":163,"line":386},[290,17080,535],{"class":295},[290,17082,174],{"class":461},[290,17084,542],{"class":541},[290,17086,545],{"class":461},[290,17088,548],{"class":295},[290,17090,17091,17093,17095,17097,17099],{"class":163,"line":408},[290,17092,554],{"class":295},[290,17094,174],{"class":461},[290,17096,542],{"class":541},[290,17098,545],{"class":461},[290,17100,548],{"class":295},[290,17102,17103,17105,17107,17109,17111],{"class":163,"line":428},[290,17104,589],{"class":461},[290,17106,1062],{"class":461},[290,17108,542],{"class":541},[290,17110,545],{"class":461},[290,17112,599],{"class":295},[290,17114,17115,17117,17119,17121,17123],{"class":163,"line":517},[290,17116,605],{"class":295},[290,17118,174],{"class":461},[290,17120,542],{"class":541},[290,17122,545],{"class":461},[290,17124,614],{"class":295},[290,17126,17127],{"class":163,"line":523},[290,17128,620],{"class":295},[290,17130,17131,17133,17135,17137,17139,17141,17143,17145,17147,17149,17151,17153,17155,17157,17159],{"class":163,"line":532},[290,17132,999],{"class":299},[290,17134,1091],{"class":303},[290,17136,1094],{"class":295},[290,17138,1097],{"class":303},[290,17140,1100],{"class":295},[290,17142,76],{"class":461},[290,17144,465],{"class":295},[290,17146,487],{"class":461},[290,17148,828],{"class":295},[290,17150,103],{"class":461},[290,17152,465],{"class":295},[290,17154,493],{"class":461},[290,17156,484],{"class":295},[290,17158,1119],{"class":461},[290,17160,1122],{"class":295},[290,17162,17163],{"class":163,"line":551},[290,17164,334],{"emptyLinePlaceholder":333},[290,17166,17167,17169],{"class":163,"line":586},[290,17168,38],{"class":541},[290,17170,450],{"class":295},[290,17172,17173,17175,17177,17179,17181,17183,17185,17187,17189,17191,17193,17195,17197,17199],{"class":163,"line":602},[290,17174,1137],{"class":299},[290,17176,1140],{"class":295},[290,17178,1097],{"class":303},[290,17180,1145],{"class":295},[290,17182,76],{"class":461},[290,17184,465],{"class":295},[290,17186,487],{"class":461},[290,17188,828],{"class":295},[290,17190,103],{"class":461},[290,17192,465],{"class":295},[290,17194,493],{"class":461},[290,17196,484],{"class":295},[290,17198,1119],{"class":461},[290,17200,1122],{"class":295},[290,17202,17203],{"class":163,"line":617},[290,17204,620],{"class":295},[290,17206,17207],{"class":163,"line":623},[290,17208,334],{"emptyLinePlaceholder":333},[290,17210,17211,17213,17215],{"class":163,"line":628},[290,17212,999],{"class":299},[290,17214,981],{"class":303},[290,17216,450],{"class":295},[290,17218,17219,17221,17223,17225,17227,17229,17231,17233,17235,17237],{"class":163,"line":634},[290,17220,1186],{"class":461},[290,17222,465],{"class":295},[290,17224,1191],{"class":461},[290,17226,484],{"class":295},[290,17228,487],{"class":461},[290,17230,1198],{"class":461},[290,17232,1198],{"class":461},[290,17234,1203],{"class":295},[290,17236,169],{"class":461},[290,17238,500],{"class":295},[290,17240,17241,17243,17245,17247,17249,17251,17253,17255,17257,17259,17261,17263,17265,17267,17269,17271],{"class":163,"line":649},[290,17242,526],{"class":461},[290,17244,465],{"class":295},[290,17246,1217],{"class":1216},[290,17248,1062],{"class":461},[290,17250,542],{"class":541},[290,17252,545],{"class":461},[290,17254,569],{"class":295},[290,17256,923],{"class":461},[290,17258,1062],{"class":461},[290,17260,542],{"class":541},[290,17262,545],{"class":461},[290,17264,1236],{"class":295},[290,17266,174],{"class":461},[290,17268,542],{"class":541},[290,17270,545],{"class":461},[290,17272,614],{"class":295},[290,17274,17275],{"class":163,"line":660},[290,17276,620],{"class":295},[290,17278,17279,17281],{"class":163,"line":688},[290,17280,38],{"class":541},[290,17282,450],{"class":295},[290,17284,17285,17287,17289,17291,17293,17295,17297,17299,17301,17303,17305,17307,17309,17311,17313,17315],{"class":163,"line":693},[290,17286,1137],{"class":299},[290,17288,1140],{"class":295},[290,17290,1097],{"class":303},[290,17292,1265],{"class":295},[290,17294,981],{"class":303},[290,17296,790],{"class":295},[290,17298,1217],{"class":461},[290,17300,465],{"class":295},[290,17302,1191],{"class":461},[290,17304,484],{"class":295},[290,17306,487],{"class":461},[290,17308,1198],{"class":461},[290,17310,1198],{"class":461},[290,17312,1203],{"class":295},[290,17314,487],{"class":461},[290,17316,1122],{"class":295},[290,17318,17319],{"class":163,"line":698},[290,17320,620],{"class":295},[47,17322],{},[50,17324,1299],{"id":1298},[14,17326,17327,69,17329,1306,17331,1310],{},[18,17328,34],{},[18,17330,38],{},[18,17332,1309],{},[47,17334],{},[50,17336,1316],{"id":1315},[14,17338,17339,1322,17341,1325,17343,1328,17345,1331,17347,1334,17349,1337,17351,1340,17353,1343],{},[62,17340,1321],{},[18,17342,59],{},[18,17344,76],{},[18,17346,916],{},[18,17348,59],{},[18,17350,923],{},[18,17352,887],{},[18,17354,72],{},[14,17356,17357,17359,1351,17361,1354,17363,1357],{},[62,17358,1348],{},[18,17360,38],{},[86,17362,88],{},[18,17364,20],{},[14,17366,17367,1363],{},[62,17368,1362],{},[14,17370,17371,1369,17373,69,17375,1374,17377,69,17379,1379,17381,1382,17383,1385],{},[62,17372,1368],{},[18,17374,916],{},[18,17376,38],{},[18,17378,76],{},[18,17380,103],{},[18,17382,59],{},[18,17384,923],{},[47,17386],{},[50,17388,1391],{"id":1390},[1393,17390,17391,17395,17399,17403,17407],{},[1396,17392,17393,1400],{},[27,17394,30],{"href":29},[1396,17396,17397,1407],{},[27,17398,1406],{"href":1405},[1396,17400,17401,1414],{},[27,17402,1413],{"href":1412},[1396,17404,17405,1421],{},[27,17406,1420],{"href":1419},[1396,17408,17409,1428],{},[27,17410,1427],{"href":1426},[1430,17412,1432],{},{"title":286,"searchDepth":330,"depth":330,"links":17414},[17415,17416,17417,17418,17419,17420,17421,17422],{"id":52,"depth":330,"text":53},{"id":118,"depth":330,"text":119},{"id":260,"depth":330,"text":261},{"id":929,"depth":330,"text":930},{"id":971,"depth":330,"text":972},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},{"seoTitle":1446,"datePublished":1447,"dateModified":1447,"faq":17424},[17425,17426,17427,17428],{"q":1321,"a":1450},{"q":1348,"a":1452},{"q":1362,"a":1454},{"q":1368,"a":1456},{"title":5,"description":1443},{"id":17431,"title":17432,"body":17433,"description":18724,"extension":1444,"meta":18725,"navigation":333,"path":18738,"seo":18739,"stem":18740,"__hash__":18741},"content\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Faccessible-css-only-tooltips\u002Findex.md","Accessible CSS-Only Tooltips and Hover Cards With  and ",{"type":7,"value":17434,"toc":18715},[17435,17445,17451,17456,17475,17479,17503,17516,17518,17521,17582,17717,18289,18308,18310,18332,18336,18342,18399,18529,18536,18617,18619,18632,18634,18642,18654,18670,18676,18678,18712],[10,17436,17438,17439,69,17442],{"id":17437},"accessible-css-only-tooltips-and-hover-cards-with-and","Accessible CSS-Only Tooltips and Hover Cards With ",[17440,17441],"hover",{},[17443,17444],"focus-within",{},[14,17446,17447,17448,17450],{},"A tooltip looks like the simplest interaction on the page, yet it is one of the easiest to build in a way that excludes keyboard and screen-reader users. The narrow problem this guide solves: you want a small explanatory popover — a definition, a help hint, or a richer hover card — that appears on pointer hover and on keyboard focus, stays put long enough to read, and survives the cursor crossing the gap between trigger and bubble, all without a line of JavaScript. This page sits under ",[27,17449,3345],{"href":3344},", and it leans hard on the accessibility expectations described in WCAG Success Criterion 1.4.13, \"Content on Hover or Focus.\"",[14,17452,17453],{},[62,17454,17455],{},"What you get here:",[1393,17457,17458,17466,17469,17472],{},[1396,17459,17460,17461,69,17463],{},"A trigger that reveals a popover on both ",[18,17462,3689],{},[18,17464,17465],{},":focus-within",[1396,17467,17468],{},"A transparent bridge so the popover is hoverable across the gap",[1396,17470,17471],{},"A persistent popover that does not auto-dismiss on a timer",[1396,17473,17474],{},"An honest account of where CSS stops and a tiny script is needed",[50,17476,17478],{"id":17477},"why-css-only-and-where-it-falls-short","Why CSS-only, and where it falls short",[14,17480,17481,17482,17484,17485,17487,17488,17491,17492,17495,17496,17499,17500,17502],{},"The instinct to reach for the native ",[18,17483,142],{}," attribute is understandable and wrong. A ",[18,17486,142],{}," tooltip is not keyboard-focusable, cannot be hovered, disappears on its own timer, and is announced inconsistently across assistive technology. WCAG 1.4.13 sets three conditions for content that appears on hover or focus: it must be ",[62,17489,17490],{},"dismissible"," (you can remove it without moving the pointer or focus), ",[62,17493,17494],{},"hoverable"," (you can move the pointer onto the new content without it vanishing), and ",[62,17497,17498],{},"persistent"," (it stays visible until you move away, dismiss it, or it becomes invalid). The native ",[18,17501,142],{}," fails all three.",[14,17504,17505,17506,69,17508,17510,17511,17515],{},"A CSS-driven popover gets you two of the three cleanly. By attaching the trigger to a real focusable element and revealing a sibling popover with ",[18,17507,3689],{},[18,17509,17465],{},", you satisfy hoverable and persistent with no script. The dismissible criterion — pressing ",[17512,17513,17514],"kbd",{},"Escape"," to clear the popover without moving focus — is the one piece CSS cannot express, because there is no selector for \"a key was pressed.\" For non-essential decorative hints this is often acceptable; for content that obscures other UI you should add the small handler shown in the variation section. Knowing exactly where the boundary lies is the point of choosing CSS deliberately rather than by accident.",[50,17517,4203],{"id":4202},[14,17519,17520],{},"The structure is a wrapper that owns the hover and focus state, a focusable trigger, and a popover that is its sibling so it can read the wrapper's state. The diagram below traces how the state on the wrapper feeds both the pointer and keyboard paths into the same visible popover.",[133,17522,140,17524,140,17527,140,17530,140,17533,140,17535,140,17538,140,17541,140,17544,140,17547,140,17550,140,17552,140,17556,140,17559,140,17561,140,17565,140,17568,140,17570,140,17573,140,17576,140,17579],{"viewBox":2588,"role":136,"ariaLabel":17523,"xmlns":138,"style":139},"Hover and keyboard focus both reveal the same popover via focus-within on the wrapper",[142,17525,17526],{},"Trigger to popover state flow",[146,17528,17529],{},"Pointer hover and keyboard focus both set state on the wrapper, which reveals a popover; a transparent bridge keeps the popover hoverable.",[150,17531,17532],{"x":152,"y":2598,"style":1781},":hover \u002F :focus-within reveal a shared popover",[171,17534],{"x":4146,"y":2630,"width":174,"height":1788,"rx":176,"fill":177,"opacity":178},[150,17536,17537],{"x":4195,"y":1823,"style":1794},"pointer hover",[150,17539,17540],{"x":4195,"y":173,"style":1799},".tip:hover",[171,17542],{"x":4146,"y":17543,"width":174,"height":1788,"rx":176,"fill":177,"opacity":178},"148",[150,17545,17546],{"x":4195,"y":6673,"style":1794},"keyboard focus",[150,17548,17465],{"x":4195,"y":17549,"style":1799},"192",[171,17551],{"x":5397,"y":6645,"width":1830,"height":1788,"rx":176,"fill":72,"stroke":167,"strokeWidth":168},[150,17553,17555],{"x":17554,"y":182,"style":1794},"380","wrapper state",[150,17557,17558],{"x":17554,"y":1787,"style":1799},".tip",[171,17560],{"x":5138,"y":6645,"width":1822,"height":1788,"rx":176,"fill":177,"opacity":4173},[150,17562,17564],{"x":17563,"y":182,"style":1794},"605","popover shown",[150,17566,17567],{"x":17563,"y":1787,"style":1799},".tip__bubble",[163,17569],{"x1":8947,"y1":4196,"x2":5397,"y2":181,"stroke":167,"strokeWidth":168},[163,17571],{"x1":8947,"y1":17572,"x2":5397,"y2":4162,"stroke":167,"strokeWidth":168},"176",[163,17574],{"x1":2641,"y1":17575,"x2":5138,"y2":17575,"stroke":167,"strokeWidth":168},"134",[150,17577,17578],{"x":17554,"y":3112,"style":1808},"transparent bridge keeps the gap hoverable (WCAG 1.4.13)",[171,17580],{"x":5397,"y":220,"width":1830,"height":17581,"rx":249,"fill":177,"opacity":1883},"22",[281,17583,17585],{"className":283,"code":17584,"language":285,"meta":286,"style":286},"\u003Cspan class=\"tip\">\n  \u003Cbutton type=\"button\" class=\"tip__trigger\" aria-describedby=\"tip-1\">\n    What is a compositor?\n  \u003C\u002Fbutton>\n  \u003Cspan role=\"tooltip\" id=\"tip-1\" class=\"tip__bubble\">\n    The compositor is the browser thread that assembles already-painted\n    layers into the final frame. \u003Ca href=\"#layers\">More on layers\u003C\u002Fa>.\n  \u003C\u002Fspan>\n\u003C\u002Fspan>\n",[18,17586,17587,17602,17631,17636,17644,17673,17678,17701,17709],{"__ignoreMap":286},[290,17588,17589,17591,17593,17595,17597,17600],{"class":163,"line":292},[290,17590,296],{"class":295},[290,17592,290],{"class":299},[290,17594,314],{"class":303},[290,17596,307],{"class":295},[290,17598,17599],{"class":310},"\"tip\"",[290,17601,327],{"class":295},[290,17603,17604,17606,17608,17610,17612,17614,17616,17618,17621,17624,17626,17629],{"class":163,"line":330},[290,17605,367],{"class":295},[290,17607,300],{"class":299},[290,17609,393],{"class":303},[290,17611,307],{"class":295},[290,17613,398],{"class":310},[290,17615,314],{"class":303},[290,17617,307],{"class":295},[290,17619,17620],{"class":310},"\"tip__trigger\"",[290,17622,17623],{"class":303}," aria-describedby",[290,17625,307],{"class":295},[290,17627,17628],{"class":310},"\"tip-1\"",[290,17630,327],{"class":295},[290,17632,17633],{"class":163,"line":337},[290,17634,17635],{"class":295},"    What is a compositor?\n",[290,17637,17638,17640,17642],{"class":163,"line":364},[290,17639,4315],{"class":295},[290,17641,300],{"class":299},[290,17643,327],{"class":295},[290,17645,17646,17648,17650,17653,17655,17658,17660,17662,17664,17666,17668,17671],{"class":163,"line":386},[290,17647,367],{"class":295},[290,17649,290],{"class":299},[290,17651,17652],{"class":303}," role",[290,17654,307],{"class":295},[290,17656,17657],{"class":310},"\"tooltip\"",[290,17659,345],{"class":303},[290,17661,307],{"class":295},[290,17663,17628],{"class":310},[290,17665,314],{"class":303},[290,17667,307],{"class":295},[290,17669,17670],{"class":310},"\"tip__bubble\"",[290,17672,327],{"class":295},[290,17674,17675],{"class":163,"line":408},[290,17676,17677],{"class":295},"    The compositor is the browser thread that assembles already-painted\n",[290,17679,17680,17683,17685,17688,17690,17693,17696,17698],{"class":163,"line":428},[290,17681,17682],{"class":295},"    layers into the final frame. \u003C",[290,17684,27],{"class":299},[290,17686,17687],{"class":303}," href",[290,17689,307],{"class":295},[290,17691,17692],{"class":310},"\"#layers\"",[290,17694,17695],{"class":295},">More on layers\u003C\u002F",[290,17697,27],{"class":299},[290,17699,17700],{"class":295},">.\n",[290,17702,17703,17705,17707],{"class":163,"line":517},[290,17704,4315],{"class":295},[290,17706,290],{"class":299},[290,17708,327],{"class":295},[290,17710,17711,17713,17715],{"class":163,"line":523},[290,17712,431],{"class":295},[290,17714,290],{"class":299},[290,17716,327],{"class":295},[281,17718,17720],{"className":438,"code":17719,"language":440,"meta":286,"style":286},".tip {\n  position: relative;\n  display: inline-block;\n}\n\n\u002F* The trigger must be a real focusable control (button\u002Flink), not a div. *\u002F\n.tip__trigger {\n  font: inherit;\n  border: 0;\n  background: none;\n  padding: 0;\n  color: #2d5bff;\n  text-decoration: underline dotted;\n  cursor: help;\n}\n\n.tip__bubble {\n  position: absolute;\n  left: 0;\n  bottom: 100%;          \u002F* sit above the trigger *\u002F\n  width: max-content;\n  max-width: 18rem;\n  padding: 0.6rem 0.8rem;\n  background: #1e2430;\n  color: #fff;\n  border-radius: 8px;\n  font-size: 0.85rem;\n  line-height: 1.4;\n\n  \u002F* Hidden but still in the accessibility tree as a labelled region. *\u002F\n  opacity: 0;\n  visibility: hidden;\n  transform: translateY(4px);\n  transition: opacity 0.15s ease, transform 0.15s ease,\n              visibility 0s linear 0.15s; \u002F* delay hiding so the fade can run *\u002F\n}\n\n\u002F* The transparent bridge: a pseudo-element fills the gap above the\n   trigger so the pointer never leaves a hoverable region (hoverable). *\u002F\n.tip__bubble::after {\n  content: \"\";\n  position: absolute;\n  inset: 100% 0 auto 0;  \u002F* sits directly below the bubble, over the gap *\u002F\n  height: 8px;\n}\n\n\u002F* Reveal on pointer hover AND when focus is anywhere inside the wrapper.\n   :focus-within keeps it open while focus moves into the bubble's link. *\u002F\n.tip:hover .tip__bubble,\n.tip:focus-within .tip__bubble {\n  opacity: 1;\n  visibility: visible;\n  transform: translateY(0);\n  transition-delay: 0s; \u002F* show immediately, only hiding is delayed *\u002F\n}\n\n.tip__bubble a {\n  color: #9cc0ff;\n}\n",[18,17721,17722,17728,17738,17750,17754,17758,17763,17770,17781,17791,17801,17811,17822,17837,17849,17853,17857,17863,17873,17884,17900,17912,17926,17943,17954,17964,17976,17990,18001,18005,18010,18020,18031,18047,18069,18090,18094,18098,18103,18108,18115,18125,18135,18158,18171,18175,18179,18184,18189,18198,18207,18217,18228,18242,18258,18262,18266,18274,18285],{"__ignoreMap":286},[290,17723,17724,17726],{"class":163,"line":292},[290,17725,17558],{"class":303},[290,17727,450],{"class":295},[290,17729,17730,17732,17734,17736],{"class":163,"line":330},[290,17731,1866],{"class":461},[290,17733,465],{"class":295},[290,17735,1871],{"class":461},[290,17737,471],{"class":295},[290,17739,17740,17743,17745,17748],{"class":163,"line":337},[290,17741,17742],{"class":461},"  display",[290,17744,465],{"class":295},[290,17746,17747],{"class":461},"inline-block",[290,17749,471],{"class":295},[290,17751,17752],{"class":163,"line":364},[290,17753,620],{"class":295},[290,17755,17756],{"class":163,"line":386},[290,17757,334],{"emptyLinePlaceholder":333},[290,17759,17760],{"class":163,"line":408},[290,17761,17762],{"class":455},"\u002F* The trigger must be a real focusable control (button\u002Flink), not a div. *\u002F\n",[290,17764,17765,17768],{"class":163,"line":428},[290,17766,17767],{"class":303},".tip__trigger",[290,17769,450],{"class":295},[290,17771,17772,17775,17777,17779],{"class":163,"line":517},[290,17773,17774],{"class":461},"  font",[290,17776,465],{"class":295},[290,17778,1970],{"class":461},[290,17780,471],{"class":295},[290,17782,17783,17785,17787,17789],{"class":163,"line":523},[290,17784,1948],{"class":461},[290,17786,465],{"class":295},[290,17788,487],{"class":461},[290,17790,471],{"class":295},[290,17792,17793,17795,17797,17799],{"class":163,"line":532},[290,17794,1186],{"class":461},[290,17796,465],{"class":295},[290,17798,72],{"class":461},[290,17800,471],{"class":295},[290,17802,17803,17805,17807,17809],{"class":163,"line":551},[290,17804,10433],{"class":461},[290,17806,465],{"class":295},[290,17808,487],{"class":461},[290,17810,471],{"class":295},[290,17812,17813,17815,17817,17820],{"class":163,"line":586},[290,17814,10485],{"class":461},[290,17816,465],{"class":295},[290,17818,17819],{"class":461},"#2d5bff",[290,17821,471],{"class":295},[290,17823,17824,17827,17829,17832,17835],{"class":163,"line":602},[290,17825,17826],{"class":461},"  text-decoration",[290,17828,465],{"class":295},[290,17830,17831],{"class":461},"underline",[290,17833,17834],{"class":461}," dotted",[290,17836,471],{"class":295},[290,17838,17839,17842,17844,17847],{"class":163,"line":617},[290,17840,17841],{"class":461},"  cursor",[290,17843,465],{"class":295},[290,17845,17846],{"class":461},"help",[290,17848,471],{"class":295},[290,17850,17851],{"class":163,"line":623},[290,17852,620],{"class":295},[290,17854,17855],{"class":163,"line":628},[290,17856,334],{"emptyLinePlaceholder":333},[290,17858,17859,17861],{"class":163,"line":634},[290,17860,17567],{"class":303},[290,17862,450],{"class":295},[290,17864,17865,17867,17869,17871],{"class":163,"line":649},[290,17866,1866],{"class":461},[290,17868,465],{"class":295},[290,17870,1927],{"class":461},[290,17872,471],{"class":295},[290,17874,17875,17878,17880,17882],{"class":163,"line":660},[290,17876,17877],{"class":461},"  left",[290,17879,465],{"class":295},[290,17881,487],{"class":461},[290,17883,471],{"class":295},[290,17885,17886,17889,17891,17893,17895,17897],{"class":163,"line":688},[290,17887,17888],{"class":461},"  bottom",[290,17890,465],{"class":295},[290,17892,165],{"class":461},[290,17894,11018],{"class":541},[290,17896,6144],{"class":295},[290,17898,17899],{"class":455},"\u002F* sit above the trigger *\u002F\n",[290,17901,17902,17905,17907,17910],{"class":163,"line":693},[290,17903,17904],{"class":461},"  width",[290,17906,465],{"class":295},[290,17908,17909],{"class":461},"max-content",[290,17911,471],{"class":295},[290,17913,17914,17917,17919,17922,17924],{"class":163,"line":698},[290,17915,17916],{"class":461},"  max-width",[290,17918,465],{"class":295},[290,17920,17921],{"class":461},"18",[290,17923,801],{"class":541},[290,17925,471],{"class":295},[290,17927,17928,17930,17932,17934,17936,17939,17941],{"class":163,"line":704},[290,17929,10433],{"class":461},[290,17931,465],{"class":295},[290,17933,232],{"class":461},[290,17935,801],{"class":541},[290,17937,17938],{"class":461}," 0.8",[290,17940,801],{"class":541},[290,17942,471],{"class":295},[290,17944,17945,17947,17949,17952],{"class":163,"line":710},[290,17946,1186],{"class":461},[290,17948,465],{"class":295},[290,17950,17951],{"class":461},"#1e2430",[290,17953,471],{"class":295},[290,17955,17956,17958,17960,17962],{"class":163,"line":717},[290,17957,10485],{"class":461},[290,17959,465],{"class":295},[290,17961,9138],{"class":461},[290,17963,471],{"class":295},[290,17965,17966,17968,17970,17972,17974],{"class":163,"line":730},[290,17967,1663],{"class":461},[290,17969,465],{"class":295},[290,17971,176],{"class":461},[290,17973,674],{"class":541},[290,17975,471],{"class":295},[290,17977,17978,17981,17983,17986,17988],{"class":163,"line":742},[290,17979,17980],{"class":461},"  font-size",[290,17982,465],{"class":295},[290,17984,17985],{"class":461},"0.85",[290,17987,801],{"class":541},[290,17989,471],{"class":295},[290,17991,17992,17995,17997,17999],{"class":163,"line":768},[290,17993,17994],{"class":461},"  line-height",[290,17996,465],{"class":295},[290,17998,1824],{"class":461},[290,18000,471],{"class":295},[290,18002,18003],{"class":163,"line":774},[290,18004,334],{"emptyLinePlaceholder":333},[290,18006,18007],{"class":163,"line":779},[290,18008,18009],{"class":455},"  \u002F* Hidden but still in the accessibility tree as a labelled region. *\u002F\n",[290,18011,18012,18014,18016,18018],{"class":163,"line":784},[290,18013,462],{"class":461},[290,18015,465],{"class":295},[290,18017,487],{"class":461},[290,18019,471],{"class":295},[290,18021,18022,18025,18027,18029],{"class":163,"line":812},[290,18023,18024],{"class":461},"  visibility",[290,18026,465],{"class":295},[290,18028,7808],{"class":461},[290,18030,471],{"class":295},[290,18032,18033,18035,18037,18039,18041,18043,18045],{"class":163,"line":860},[290,18034,476],{"class":461},[290,18036,465],{"class":295},[290,18038,481],{"class":461},[290,18040,484],{"class":295},[290,18042,249],{"class":461},[290,18044,674],{"class":541},[290,18046,500],{"class":295},[290,18048,18049,18051,18053,18055,18057,18059,18061,18063,18065,18067],{"class":163,"line":865},[290,18050,526],{"class":461},[290,18052,6384],{"class":295},[290,18054,1883],{"class":461},[290,18056,1886],{"class":541},[290,18058,545],{"class":461},[290,18060,4679],{"class":295},[290,18062,1883],{"class":461},[290,18064,1886],{"class":541},[290,18066,545],{"class":461},[290,18068,548],{"class":295},[290,18070,18071,18074,18076,18078,18080,18083,18085,18087],{"class":163,"line":871},[290,18072,18073],{"class":295},"              visibility ",[290,18075,487],{"class":461},[290,18077,1886],{"class":541},[290,18079,3159],{"class":461},[290,18081,18082],{"class":461}," 0.15",[290,18084,1886],{"class":541},[290,18086,828],{"class":295},[290,18088,18089],{"class":455},"\u002F* delay hiding so the fade can run *\u002F\n",[290,18091,18092],{"class":163,"line":880},[290,18093,620],{"class":295},[290,18095,18096],{"class":163,"line":896},[290,18097,334],{"emptyLinePlaceholder":333},[290,18099,18100],{"class":163,"line":4734},[290,18101,18102],{"class":455},"\u002F* The transparent bridge: a pseudo-element fills the gap above the\n",[290,18104,18105],{"class":163,"line":4742},[290,18106,18107],{"class":455},"   trigger so the pointer never leaves a hoverable region (hoverable). *\u002F\n",[290,18109,18110,18113],{"class":163,"line":4761},[290,18111,18112],{"class":303},".tip__bubble::after",[290,18114,450],{"class":295},[290,18116,18117,18119,18121,18123],{"class":163,"line":4766},[290,18118,1911],{"class":461},[290,18120,465],{"class":295},[290,18122,1916],{"class":310},[290,18124,471],{"class":295},[290,18126,18127,18129,18131,18133],{"class":163,"line":4786},[290,18128,1866],{"class":461},[290,18130,465],{"class":295},[290,18132,1927],{"class":461},[290,18134,471],{"class":295},[290,18136,18137,18139,18141,18143,18145,18147,18150,18152,18155],{"class":163,"line":9635},[290,18138,1934],{"class":461},[290,18140,465],{"class":295},[290,18142,165],{"class":461},[290,18144,11018],{"class":541},[290,18146,1198],{"class":461},[290,18148,18149],{"class":461}," auto",[290,18151,1198],{"class":461},[290,18153,18154],{"class":295},";  ",[290,18156,18157],{"class":455},"\u002F* sits directly below the bubble, over the gap *\u002F\n",[290,18159,18160,18163,18165,18167,18169],{"class":163,"line":9641},[290,18161,18162],{"class":461},"  height",[290,18164,465],{"class":295},[290,18166,176],{"class":461},[290,18168,674],{"class":541},[290,18170,471],{"class":295},[290,18172,18173],{"class":163,"line":9647},[290,18174,620],{"class":295},[290,18176,18177],{"class":163,"line":9665},[290,18178,334],{"emptyLinePlaceholder":333},[290,18180,18181],{"class":163,"line":9683},[290,18182,18183],{"class":455},"\u002F* Reveal on pointer hover AND when focus is anywhere inside the wrapper.\n",[290,18185,18186],{"class":163,"line":9688},[290,18187,18188],{"class":455},"   :focus-within keeps it open while focus moves into the bubble's link. *\u002F\n",[290,18190,18191,18193,18196],{"class":163,"line":9694},[290,18192,17540],{"class":303},[290,18194,18195],{"class":303}," .tip__bubble",[290,18197,548],{"class":295},[290,18199,18200,18203,18205],{"class":163,"line":9700},[290,18201,18202],{"class":303},".tip:focus-within",[290,18204,18195],{"class":303},[290,18206,450],{"class":295},[290,18208,18209,18211,18213,18215],{"class":163,"line":9710},[290,18210,462],{"class":461},[290,18212,465],{"class":295},[290,18214,468],{"class":461},[290,18216,471],{"class":295},[290,18218,18219,18221,18223,18226],{"class":163,"line":9716},[290,18220,18024],{"class":461},[290,18222,465],{"class":295},[290,18224,18225],{"class":461},"visible",[290,18227,471],{"class":295},[290,18229,18230,18232,18234,18236,18238,18240],{"class":163,"line":9738},[290,18231,476],{"class":461},[290,18233,465],{"class":295},[290,18235,481],{"class":461},[290,18237,484],{"class":295},[290,18239,487],{"class":461},[290,18241,500],{"class":295},[290,18243,18244,18247,18249,18251,18253,18255],{"class":163,"line":9748},[290,18245,18246],{"class":461},"  transition-delay",[290,18248,465],{"class":295},[290,18250,487],{"class":461},[290,18252,1886],{"class":541},[290,18254,828],{"class":295},[290,18256,18257],{"class":455},"\u002F* show immediately, only hiding is delayed *\u002F\n",[290,18259,18260],{"class":163,"line":9754},[290,18261,620],{"class":295},[290,18263,18264],{"class":163,"line":9760},[290,18265,334],{"emptyLinePlaceholder":333},[290,18267,18268,18270,18272],{"class":163,"line":9777},[290,18269,17567],{"class":303},[290,18271,7556],{"class":299},[290,18273,450],{"class":295},[290,18275,18276,18278,18280,18283],{"class":163,"line":9787},[290,18277,10485],{"class":461},[290,18279,465],{"class":295},[290,18281,18282],{"class":461},"#9cc0ff",[290,18284,471],{"class":295},[290,18286,18287],{"class":163,"line":9793},[290,18288,620],{"class":295},[14,18290,18291,18292,69,18294,18296,18297,18300,18301,18303,18304,18307],{},"The popover starts with ",[18,18293,907],{},[18,18295,5681],{},". Keeping ",[18,18298,18299],{},"visibility"," rather than ",[18,18302,20],{}," means the element stays in the layout and in the accessibility tree as a labelled region, and the ",[18,18305,18306],{},"aria-describedby"," reference on the trigger stays valid so screen readers announce the description when the control receives focus.",[50,18309,4795],{"id":4794},[14,18311,18312,18313,18315,18316,18319,18320,18322,18323,18325,18326,18328,18329,18331],{},"Two selectors do the heavy lifting. ",[18,18314,17465],{}," matches the wrapper whenever focus lands on the trigger ",[62,18317,18318],{},"or"," on anything inside the popover, so a keyboard user can ",[17512,18321,1764],{}," from the trigger into a link inside the bubble and the bubble stays open — a plain ",[18,18324,1532],{}," on the trigger would slam it shut the instant focus moved off the trigger. The second trick is the transparent bridge: the ",[18,18327,2412],{}," pseudo-element extends the hoverable area across the visual gap between trigger and bubble, so moving the cursor up to click a link never crosses dead space that would drop the ",[18,18330,3689],{}," state. Together they satisfy the hoverable and persistent halves of WCAG 1.4.13 with no script.",[50,18333,18335],{"id":18334},"variation-adding-escape-to-dismiss","Variation: adding Escape-to-dismiss",[14,18337,18338,18339,18341],{},"The one criterion CSS cannot meet is dismissible. A tiny, progressively-enhanced handler closes the popover on ",[17512,18340,17514],{}," without moving the pointer or focus. It toggles an attribute the CSS already understands, so the base experience survives if the script never loads.",[281,18343,18345],{"className":438,"code":18344,"language":440,"meta":286,"style":286},"\u002F* Honour an explicit dismissal flag set by the keyboard handler. *\u002F\n.tip[data-dismissed=\"true\"] .tip__bubble {\n  opacity: 0 !important;\n  visibility: hidden !important;\n}\n",[18,18346,18347,18352,18371,18383,18395],{"__ignoreMap":286},[290,18348,18349],{"class":163,"line":292},[290,18350,18351],{"class":455},"\u002F* Honour an explicit dismissal flag set by the keyboard handler. *\u002F\n",[290,18353,18354,18356,18358,18361,18363,18365,18367,18369],{"class":163,"line":330},[290,18355,17558],{"class":303},[290,18357,1140],{"class":295},[290,18359,18360],{"class":303},"data-dismissed",[290,18362,307],{"class":541},[290,18364,6406],{"class":310},[290,18366,14416],{"class":295},[290,18368,17567],{"class":303},[290,18370,450],{"class":295},[290,18372,18373,18375,18377,18379,18381],{"class":163,"line":337},[290,18374,462],{"class":461},[290,18376,465],{"class":295},[290,18378,487],{"class":461},[290,18380,2836],{"class":541},[290,18382,471],{"class":295},[290,18384,18385,18387,18389,18391,18393],{"class":163,"line":364},[290,18386,18024],{"class":461},[290,18388,465],{"class":295},[290,18390,7808],{"class":461},[290,18392,2836],{"class":541},[290,18394,471],{"class":295},[290,18396,18397],{"class":163,"line":386},[290,18398,620],{"class":295},[281,18400,18402],{"className":2904,"code":18401,"language":2906,"meta":286,"style":286},"document.querySelectorAll(\".tip\").forEach((tip) => {\n  tip.addEventListener(\"keydown\", (e) => {\n    if (e.key === \"Escape\") tip.dataset.dismissed = \"true\";\n  });\n  \u002F\u002F Re-arm on the next hover\u002Ffocus so it can show again.\n  tip.addEventListener(\"pointerenter\", () => delete tip.dataset.dismissed);\n  tip.addEventListener(\"focusin\", () => delete tip.dataset.dismissed);\n});\n",[18,18403,18404,18432,18454,18475,18479,18484,18505,18524],{"__ignoreMap":286},[290,18405,18406,18409,18412,18414,18417,18419,18421,18423,18426,18428,18430],{"class":163,"line":292},[290,18407,18408],{"class":295},"document.",[290,18410,18411],{"class":303},"querySelectorAll",[290,18413,484],{"class":295},[290,18415,18416],{"class":310},"\".tip\"",[290,18418,3724],{"class":295},[290,18420,9546],{"class":303},[290,18422,9509],{"class":295},[290,18424,18425],{"class":1561},"tip",[290,18427,490],{"class":295},[290,18429,2988],{"class":541},[290,18431,450],{"class":295},[290,18433,18434,18437,18439,18441,18444,18446,18448,18450,18452],{"class":163,"line":330},[290,18435,18436],{"class":295},"  tip.",[290,18438,2972],{"class":303},[290,18440,484],{"class":295},[290,18442,18443],{"class":310},"\"keydown\"",[290,18445,2980],{"class":295},[290,18447,2983],{"class":1561},[290,18449,490],{"class":295},[290,18451,2988],{"class":541},[290,18453,450],{"class":295},[290,18455,18456,18458,18461,18463,18466,18469,18471,18473],{"class":163,"line":337},[290,18457,8227],{"class":541},[290,18459,18460],{"class":295}," (e.key ",[290,18462,7979],{"class":541},[290,18464,18465],{"class":310}," \"Escape\"",[290,18467,18468],{"class":295},") tip.dataset.dismissed ",[290,18470,307],{"class":541},[290,18472,7982],{"class":310},[290,18474,471],{"class":295},[290,18476,18477],{"class":163,"line":364},[290,18478,3010],{"class":295},[290,18480,18481],{"class":163,"line":386},[290,18482,18483],{"class":455},"  \u002F\u002F Re-arm on the next hover\u002Ffocus so it can show again.\n",[290,18485,18486,18488,18490,18492,18495,18497,18499,18502],{"class":163,"line":408},[290,18487,18436],{"class":295},[290,18489,2972],{"class":303},[290,18491,484],{"class":295},[290,18493,18494],{"class":310},"\"pointerenter\"",[290,18496,9454],{"class":295},[290,18498,2988],{"class":541},[290,18500,18501],{"class":541}," delete",[290,18503,18504],{"class":295}," tip.dataset.dismissed);\n",[290,18506,18507,18509,18511,18513,18516,18518,18520,18522],{"class":163,"line":428},[290,18508,18436],{"class":295},[290,18510,2972],{"class":303},[290,18512,484],{"class":295},[290,18514,18515],{"class":310},"\"focusin\"",[290,18517,9454],{"class":295},[290,18519,2988],{"class":541},[290,18521,18501],{"class":541},[290,18523,18504],{"class":295},[290,18525,18526],{"class":163,"line":517},[290,18527,18528],{"class":295},"});\n",[14,18530,18531,18532,42],{},"For users who prefer less motion, drop the slide and keep only the fade so the popover does not travel across the screen — the same restraint covered for transitions in ",[27,18533,18535],{"href":18534},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fsmooth-hover-effects-without-javascript\u002F","Smooth Hover Effects Without JavaScript",[281,18537,18539],{"className":438,"code":18538,"language":440,"meta":286,"style":286},"@media (prefers-reduced-motion: reduce) {\n  .tip__bubble { transition: opacity 0.15s ease, visibility 0s linear 0.15s; transform: none; }\n  .tip:hover .tip__bubble,\n  .tip:focus-within .tip__bubble { transform: none; }\n}\n",[18,18540,18541,18547,18587,18596,18613],{"__ignoreMap":286},[290,18542,18543,18545],{"class":163,"line":292},[290,18544,874],{"class":541},[290,18546,877],{"class":295},[290,18548,18549,18552,18554,18556,18558,18560,18562,18564,18567,18569,18571,18573,18575,18577,18579,18581,18583,18585],{"class":163,"line":330},[290,18550,18551],{"class":303},"  .tip__bubble",[290,18553,790],{"class":295},[290,18555,887],{"class":461},[290,18557,6384],{"class":295},[290,18559,1883],{"class":461},[290,18561,1886],{"class":541},[290,18563,545],{"class":461},[290,18565,18566],{"class":295},", visibility ",[290,18568,487],{"class":461},[290,18570,1886],{"class":541},[290,18572,3159],{"class":461},[290,18574,18082],{"class":461},[290,18576,1886],{"class":541},[290,18578,828],{"class":295},[290,18580,103],{"class":461},[290,18582,465],{"class":295},[290,18584,72],{"class":461},[290,18586,809],{"class":295},[290,18588,18589,18592,18594],{"class":163,"line":337},[290,18590,18591],{"class":303},"  .tip:hover",[290,18593,18195],{"class":303},[290,18595,548],{"class":295},[290,18597,18598,18601,18603,18605,18607,18609,18611],{"class":163,"line":364},[290,18599,18600],{"class":303},"  .tip:focus-within",[290,18602,18195],{"class":303},[290,18604,790],{"class":295},[290,18606,103],{"class":461},[290,18608,465],{"class":295},[290,18610,72],{"class":461},[290,18612,809],{"class":295},[290,18614,18615],{"class":163,"line":386},[290,18616,620],{"class":295},[50,18618,4904],{"id":2247},[14,18620,18621,18623,18624,69,18626,18628,18629,18631],{},[18,18622,17465],{}," is supported in Chrome 60+, Edge 79+, Firefox 52+, and Safari 10.1+, so it is safe everywhere evergreen. ",[18,18625,3689],{},[18,18627,887],{}," carry no caveats. The only feature you cannot rely on in CSS is keyboard dismissal, which is why the Escape handler is plain DOM JavaScript with broad support rather than a CSS feature. No ",[18,18630,2086],{}," guard is needed for the base popover.",[50,18633,1316],{"id":1315},[14,18635,18636,18639,18640,42],{},[62,18637,18638],{},"Can a pure CSS tooltip be dismissible per WCAG 1.4.13?","\nPartly. CSS alone cannot listen for the Escape key, so the dismissible criterion needs a small JavaScript handler for full conformance. The hoverable and persistent requirements are fully achievable in CSS using a padded bridge and ",[18,18641,17465],{},[14,18643,18644,18650,18651,18653],{},[62,18645,18646,18647,18649],{},"Should I use the ",[18,18648,142],{}," attribute for tooltips instead?","\nNo. The native ",[18,18652,142],{}," attribute is not keyboard-focusable, is not hoverable, vanishes after a timeout, and is unreliable for screen readers. A CSS popover tied to a focusable trigger is far more accessible.",[14,18655,18656,18664,18666,18667,18669],{},[62,18657,18658,18659,2774,18661,18663],{},"Why use ",[18,18660,17465],{},[18,18662,1532],{}," on the trigger?",[18,18665,17443],{}," keeps the popover open while focus moves into the popover content, letting keyboard users reach links or buttons inside it. A plain ",[18,18668,1532],{}," on the trigger would close the tooltip the moment focus left the trigger.",[14,18671,18672,18675],{},[62,18673,18674],{},"How do I stop the tooltip from disappearing when the cursor crosses the gap?","\nRemove the visual gap from the hit area. Give the popover a transparent padding bridge or position it flush against the trigger so the hover region is continuous between the two elements.",[50,18677,1391],{"id":1390},[1393,18679,18680,18685,18690,18701,18706],{},[1396,18681,18682,18684],{},[27,18683,3345],{"href":3344}," — the parent guide covering interactive state patterns.",[1396,18686,18687,18689],{},[27,18688,18535],{"href":18534}," — compositor-friendly transitions for the reveal animation.",[1396,18691,18692,18700],{},[27,18693,18694,1529,18696,18699],{"href":1526},[2321,18695],{},[18697,18698],"focus",{}," and Polyfill Alternatives"," — keyboard-aware focus styling for the trigger.",[1396,18702,18703,18705],{},[27,18704,2511],{"href":1412}," — honouring reduced-motion in the reveal.",[1396,18707,18708,18711],{},[27,18709,18710],{"href":1426},"Building Responsive Cards With Container Queries"," — pairing hover cards with container-aware layouts.",[1430,18713,18714],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":286,"searchDepth":330,"depth":330,"links":18716},[18717,18718,18719,18720,18721,18722,18723],{"id":17477,"depth":330,"text":17478},{"id":4202,"depth":330,"text":4203},{"id":4794,"depth":330,"text":4795},{"id":18334,"depth":330,"text":18335},{"id":2247,"depth":330,"text":4904},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Build accessible CSS-only tooltips with :hover and :focus-within that satisfy WCAG 1.4.13 — hoverable, dismissible, and persistent — with no JavaScript.",{"seoTitle":18726,"datePublished":1447,"dateModified":1447,"faq":18727},"Accessible CSS-Only Tooltips & Hover Cards",[18728,18730,18733,18736],{"q":18638,"a":18729},"Partly. CSS alone cannot listen for the Escape key, so the dismissible criterion needs a small JavaScript handler for full conformance. The hoverable and persistent requirements are fully achievable in CSS using a padded bridge and :focus-within.",{"q":18731,"a":18732},"Should I use the title attribute for tooltips instead?","No. The native title attribute is not keyboard-focusable, is not hoverable, vanishes after a timeout, and is unreliable for screen readers. A CSS popover tied to a focusable trigger is far more accessible.",{"q":18734,"a":18735},"Why use :focus-within instead of :focus on the trigger?","focus-within keeps the popover open while focus moves into the popover content, letting keyboard users reach links or buttons inside it. A plain :focus on the trigger would close the tooltip the moment focus left the trigger.",{"q":18674,"a":18737},"Remove the visual gap from the hit area. Give the popover a transparent padding bridge or position it flush against the trigger so the hover region is continuous between the two elements.","\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Faccessible-css-only-tooltips",{"title":17432,"description":18724},"css-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Faccessible-css-only-tooltips\u002Findex","HHh8izR6JO6FZsp0JBygeSghZXAY1s9MZ6dNA40LB5U",{"id":18743,"title":18744,"body":18745,"description":19579,"extension":1444,"meta":19580,"navigation":333,"path":19593,"seo":19594,"stem":19595,"__hash__":19596},"content\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Ffocus-visible-vs-focus-polyfill-alternatives\u002Findex.md"," vs :focus: Why It Replaced the Focus-Ring Polyfill",{"type":7,"value":18746,"toc":19570},[18747,18752,18770,18775,18794,18798,18820,18829,18895,18897,18906,19018,19307,19317,19319,19329,19333,19342,19404,19407,19463,19465,19480,19482,19497,19509,19521,19530,19532,19567],[10,18748,18750,18744],{"id":18749},"vs-focus-why-it-replaced-the-focus-ring-polyfill",[2321,18751],{},[14,18753,18754,18755,18757,18758,18761,18762,18764,18765,18767,18768,42],{},"For years the standard advice — \"never remove the focus outline\" — collided with a real design grievance: mouse users saw a ring snap around every button they clicked, and designers responded by stripping ",[18,18756,1717],{}," site-wide, quietly breaking keyboard navigation. The narrow problem this guide solves is showing a focus indicator ",[62,18759,18760],{},"only"," to people who need it (keyboard and other non-pointer users) without hiding it from anyone, and doing so with the native ",[18,18763,1495],{}," pseudo-class rather than the JavaScript focus-ring polyfill that used to be required. This page belongs to ",[27,18766,3345],{"href":3344},", and it builds directly on the focus-indicator work in ",[27,18769,6532],{"href":3349},[14,18771,18772],{},[62,18773,18774],{},"What this page settles:",[1393,18776,18777,18785,18788,18791],{},[1396,18778,18779,18780,18782,18783],{},"Exactly how the ",[18,18781,1495],{}," heuristic differs from ",[18,18784,1532],{},[1396,18786,18787],{},"Why the WICG focus-ring polyfill is now obsolete",[1396,18789,18790],{},"A modern fallback that degrades safely on legacy engines",[1396,18792,18793],{},"How to never trip WCAG 2.4.7 while pleasing designers",[50,18795,18797],{"id":18796},"the-old-problem-and-the-polyfill-that-filled-the-gap","The old problem and the polyfill that filled the gap",[14,18799,18800,18801,18803,18804,18806,18807,18809,18810,18812,18813,18815,18816,18819],{},"Before ",[18,18802,1495],{},", CSS had only ",[18,18805,1532],{},", which fires for any focus regardless of how it arrived. Click a button with a mouse and ",[18,18808,1532],{}," matches; tab to it with a keyboard and ",[18,18811,1532],{}," matches identically. Designers who disliked the click-ring removed it globally — and removed it for keyboard users too, the exact people the ring exists for. The community's answer was the WICG ",[18,18814,2321],{}," polyfill: a script that watched input modality, decided whether the most recent interaction was keyboard-like, and toggled a ",[18,18817,18818],{},".focus-visible"," class you could style. It worked, but it was JavaScript on the critical path solving a presentation problem, with a flash of unstyled focus before the script booted.",[14,18821,18822,18823,18825,18826,18828],{},"The platform absorbed that heuristic into the engine. ",[18,18824,1495],{}," is a pseudo-class the browser matches using the same modality reasoning the polyfill performed, but natively, synchronously, and with no script to load. The diagram below contrasts the two input paths and shows where ",[18,18827,1495],{}," chooses to match.",[133,18830,140,18832,140,18835,140,18838,140,18841,140,18843,140,18847,140,18850,140,18852,140,18855,140,18858,140,18860,140,18864,140,18868,140,18871,140,18873,140,18877,140,18880,140,18884,140,18886,140,18889,140,18891,140,18893],{"viewBox":2588,"role":136,"ariaLabel":18831,"xmlns":138,"style":139},"Pointer focus does not match focus-visible while keyboard focus does",[142,18833,18834],{},"Input modality routing to focus-visible",[146,18836,18837],{},"A pointer click focuses without matching focus-visible, while keyboard focus matches focus-visible and shows a ring.",[150,18839,18840],{"x":152,"y":2598,"style":1781},"When does :focus-visible match?",[171,18842],{"x":4146,"y":5144,"width":2615,"height":1788,"rx":176,"fill":177,"opacity":178},[150,18844,18846],{"x":5909,"y":18845,"style":1794},"94","mouse \u002F touch click",[150,18848,18849],{"x":5909,"y":5119,"style":1799},"pointer",[171,18851],{"x":4146,"y":174,"width":2615,"height":1788,"rx":176,"fill":177,"opacity":178},[150,18853,18854],{"x":5909,"y":191,"style":1794},"Tab \u002F arrow keys",[150,18856,18857],{"x":5909,"y":217,"style":1799},"keyboard",[171,18859],{"x":10283,"y":5909,"width":1787,"height":1788,"rx":176,"fill":72,"stroke":167,"strokeWidth":168},[150,18861,18863],{"x":8950,"y":18862,"style":1794},"159","engine heuristic",[150,18865,18867],{"x":8950,"y":18866,"style":1799},"179",":focus matches",[163,18869],{"x1":4190,"y1":18870,"x2":10283,"y2":1787,"stroke":167,"strokeWidth":168},"98",[163,18872],{"x1":4190,"y1":2633,"x2":10283,"y2":17572,"stroke":167,"strokeWidth":168},[171,18874],{"x":18875,"y":5144,"width":2615,"height":1788,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"strokeDashArray":18876},"500",[5911,249],[150,18878,18879],{"x":12469,"y":18845,"style":1794},"no ring",[150,18881,18883],{"x":12469,"y":5119,"style":18882},"text-anchor:middle;fill:currentColor;font:12px ui-monospace,monospace;opacity:0.7","not :focus-visible",[171,18885],{"x":18875,"y":174,"width":2615,"height":1788,"rx":176,"fill":177,"opacity":4173},[150,18887,18888],{"x":12469,"y":191,"style":1794},"ring shown",[150,18890,1495],{"x":12469,"y":217,"style":1799},[163,18892],{"x1":4194,"y1":1787,"x2":18875,"y2":165,"stroke":167,"strokeWidth":168},[163,18894],{"x1":4194,"y1":17572,"x2":18875,"y2":5904,"stroke":167,"strokeWidth":168},[50,18896,4203],{"id":4202},[14,18898,18899,18900,18902,18903,18905],{},"The pattern has three layers: kill the legacy click-ring, restore a strong ring for ",[18,18901,1495],{},", and leave a ",[18,18904,1532],{}," fallback for any engine that does not understand the newer pseudo-class. Order matters.",[281,18907,18909],{"className":283,"code":18908,"language":285,"meta":286,"style":286},"\u003Cnav class=\"bar\">\n  \u003Cbutton type=\"button\" class=\"btn\">Save\u003C\u002Fbutton>\n  \u003Ca class=\"btn\" href=\"#next\">Continue\u003C\u002Fa>\n  \u003Cinput class=\"field\" type=\"text\" aria-label=\"Search\">\n\u003C\u002Fnav>\n",[18,18910,18911,18927,18953,18979,19010],{"__ignoreMap":286},[290,18912,18913,18915,18918,18920,18922,18925],{"class":163,"line":292},[290,18914,296],{"class":295},[290,18916,18917],{"class":299},"nav",[290,18919,314],{"class":303},[290,18921,307],{"class":295},[290,18923,18924],{"class":310},"\"bar\"",[290,18926,327],{"class":295},[290,18928,18929,18931,18933,18935,18937,18939,18941,18943,18946,18949,18951],{"class":163,"line":330},[290,18930,367],{"class":295},[290,18932,300],{"class":299},[290,18934,393],{"class":303},[290,18936,307],{"class":295},[290,18938,398],{"class":310},[290,18940,314],{"class":303},[290,18942,307],{"class":295},[290,18944,18945],{"class":310},"\"btn\"",[290,18947,18948],{"class":295},">Save\u003C\u002F",[290,18950,300],{"class":299},[290,18952,327],{"class":295},[290,18954,18955,18957,18959,18961,18963,18965,18967,18969,18972,18975,18977],{"class":163,"line":337},[290,18956,367],{"class":295},[290,18958,27],{"class":299},[290,18960,314],{"class":303},[290,18962,307],{"class":295},[290,18964,18945],{"class":310},[290,18966,17687],{"class":303},[290,18968,307],{"class":295},[290,18970,18971],{"class":310},"\"#next\"",[290,18973,18974],{"class":295},">Continue\u003C\u002F",[290,18976,27],{"class":299},[290,18978,327],{"class":295},[290,18980,18981,18983,18986,18988,18990,18993,18995,18997,19000,19003,19005,19008],{"class":163,"line":364},[290,18982,367],{"class":295},[290,18984,18985],{"class":299},"input",[290,18987,314],{"class":303},[290,18989,307],{"class":295},[290,18991,18992],{"class":310},"\"field\"",[290,18994,393],{"class":303},[290,18996,307],{"class":295},[290,18998,18999],{"class":310},"\"text\"",[290,19001,19002],{"class":303}," aria-label",[290,19004,307],{"class":295},[290,19006,19007],{"class":310},"\"Search\"",[290,19009,327],{"class":295},[290,19011,19012,19014,19016],{"class":163,"line":386},[290,19013,431],{"class":295},[290,19015,18917],{"class":299},[290,19017,327],{"class":295},[281,19019,19021],{"className":438,"code":19020,"language":440,"meta":286,"style":286},":root {\n  --ring-color: #2d5bff;\n  --ring-width: 3px;\n}\n\n\u002F* 1. Fallback for engines without :focus-visible — keep SOME ring so we\n      never ship an outline-less keyboard experience to old browsers. *\u002F\n.btn:focus {\n  outline: var(--ring-width) solid var(--ring-color);\n  outline-offset: 2px;\n}\n\n\u002F* 2. In engines that DO support :focus-visible, remove the ring for the\n      cases the heuristic says don't need it (e.g. mouse clicks)... *\u002F\n.btn:focus:not(:focus-visible) {\n  outline: none;\n}\n\n\u002F* 3. ...and apply the strong ring only when the heuristic asks for it. *\u002F\n.btn:focus-visible {\n  outline: var(--ring-width) solid var(--ring-color);\n  outline-offset: 2px;\n  \u002F* A second offset ring guarantees contrast on any background. *\u002F\n  box-shadow: 0 0 0 calc(var(--ring-width) + 2px) #fff;\n}\n\n\u002F* Text inputs benefit from a visible ring even on click, which the\n   heuristic already grants — no special-casing needed. *\u002F\n.field:focus-visible {\n  outline: var(--ring-width) solid var(--ring-color);\n}\n",[18,19022,19023,19029,19040,19053,19057,19061,19066,19071,19078,19104,19116,19120,19124,19129,19134,19145,19155,19159,19163,19168,19175,19199,19211,19216,19254,19258,19262,19267,19272,19279,19303],{"__ignoreMap":286},[290,19024,19025,19027],{"class":163,"line":292},[290,19026,1554],{"class":303},[290,19028,450],{"class":295},[290,19030,19031,19034,19036,19038],{"class":163,"line":330},[290,19032,19033],{"class":1561},"  --ring-color",[290,19035,465],{"class":295},[290,19037,17819],{"class":461},[290,19039,471],{"class":295},[290,19041,19042,19045,19047,19049,19051],{"class":163,"line":337},[290,19043,19044],{"class":1561},"  --ring-width",[290,19046,465],{"class":295},[290,19048,1579],{"class":461},[290,19050,674],{"class":541},[290,19052,471],{"class":295},[290,19054,19055],{"class":163,"line":364},[290,19056,620],{"class":295},[290,19058,19059],{"class":163,"line":386},[290,19060,334],{"emptyLinePlaceholder":333},[290,19062,19063],{"class":163,"line":408},[290,19064,19065],{"class":455},"\u002F* 1. Fallback for engines without :focus-visible — keep SOME ring so we\n",[290,19067,19068],{"class":163,"line":428},[290,19069,19070],{"class":455},"      never ship an outline-less keyboard experience to old browsers. *\u002F\n",[290,19072,19073,19076],{"class":163,"line":517},[290,19074,19075],{"class":303},".btn:focus",[290,19077,450],{"class":295},[290,19079,19080,19082,19084,19086,19088,19091,19093,19095,19097,19099,19102],{"class":163,"line":523},[290,19081,1617],{"class":461},[290,19083,465],{"class":295},[290,19085,1622],{"class":461},[290,19087,484],{"class":295},[290,19089,19090],{"class":1561},"--ring-width",[290,19092,490],{"class":295},[290,19094,1632],{"class":461},[290,19096,1635],{"class":461},[290,19098,484],{"class":295},[290,19100,19101],{"class":1561},"--ring-color",[290,19103,500],{"class":295},[290,19105,19106,19108,19110,19112,19114],{"class":163,"line":532},[290,19107,1647],{"class":461},[290,19109,465],{"class":295},[290,19111,194],{"class":461},[290,19113,674],{"class":541},[290,19115,471],{"class":295},[290,19117,19118],{"class":163,"line":551},[290,19119,620],{"class":295},[290,19121,19122],{"class":163,"line":586},[290,19123,334],{"emptyLinePlaceholder":333},[290,19125,19126],{"class":163,"line":602},[290,19127,19128],{"class":455},"\u002F* 2. In engines that DO support :focus-visible, remove the ring for the\n",[290,19130,19131],{"class":163,"line":617},[290,19132,19133],{"class":455},"      cases the heuristic says don't need it (e.g. mouse clicks)... *\u002F\n",[290,19135,19136,19139,19141,19143],{"class":163,"line":623},[290,19137,19138],{"class":303},".btn:focus:not",[290,19140,484],{"class":295},[290,19142,1495],{"class":303},[290,19144,646],{"class":295},[290,19146,19147,19149,19151,19153],{"class":163,"line":628},[290,19148,1617],{"class":461},[290,19150,465],{"class":295},[290,19152,72],{"class":461},[290,19154,471],{"class":295},[290,19156,19157],{"class":163,"line":634},[290,19158,620],{"class":295},[290,19160,19161],{"class":163,"line":649},[290,19162,334],{"emptyLinePlaceholder":333},[290,19164,19165],{"class":163,"line":660},[290,19166,19167],{"class":455},"\u002F* 3. ...and apply the strong ring only when the heuristic asks for it. *\u002F\n",[290,19169,19170,19173],{"class":163,"line":688},[290,19171,19172],{"class":303},".btn:focus-visible",[290,19174,450],{"class":295},[290,19176,19177,19179,19181,19183,19185,19187,19189,19191,19193,19195,19197],{"class":163,"line":693},[290,19178,1617],{"class":461},[290,19180,465],{"class":295},[290,19182,1622],{"class":461},[290,19184,484],{"class":295},[290,19186,19090],{"class":1561},[290,19188,490],{"class":295},[290,19190,1632],{"class":461},[290,19192,1635],{"class":461},[290,19194,484],{"class":295},[290,19196,19101],{"class":1561},[290,19198,500],{"class":295},[290,19200,19201,19203,19205,19207,19209],{"class":163,"line":698},[290,19202,1647],{"class":461},[290,19204,465],{"class":295},[290,19206,194],{"class":461},[290,19208,674],{"class":541},[290,19210,471],{"class":295},[290,19212,19213],{"class":163,"line":704},[290,19214,19215],{"class":455},"  \u002F* A second offset ring guarantees contrast on any background. *\u002F\n",[290,19217,19218,19220,19222,19224,19226,19228,19231,19233,19235,19237,19239,19241,19244,19246,19248,19250,19252],{"class":163,"line":710},[290,19219,3207],{"class":461},[290,19221,465],{"class":295},[290,19223,487],{"class":461},[290,19225,1198],{"class":461},[290,19227,1198],{"class":461},[290,19229,19230],{"class":461}," calc",[290,19232,484],{"class":295},[290,19234,1622],{"class":461},[290,19236,484],{"class":295},[290,19238,19090],{"class":1561},[290,19240,490],{"class":295},[290,19242,19243],{"class":541},"+",[290,19245,3290],{"class":461},[290,19247,674],{"class":541},[290,19249,490],{"class":295},[290,19251,9138],{"class":461},[290,19253,471],{"class":295},[290,19255,19256],{"class":163,"line":717},[290,19257,620],{"class":295},[290,19259,19260],{"class":163,"line":730},[290,19261,334],{"emptyLinePlaceholder":333},[290,19263,19264],{"class":163,"line":742},[290,19265,19266],{"class":455},"\u002F* Text inputs benefit from a visible ring even on click, which the\n",[290,19268,19269],{"class":163,"line":768},[290,19270,19271],{"class":455},"   heuristic already grants — no special-casing needed. *\u002F\n",[290,19273,19274,19277],{"class":163,"line":774},[290,19275,19276],{"class":303},".field:focus-visible",[290,19278,450],{"class":295},[290,19280,19281,19283,19285,19287,19289,19291,19293,19295,19297,19299,19301],{"class":163,"line":779},[290,19282,1617],{"class":461},[290,19284,465],{"class":295},[290,19286,1622],{"class":461},[290,19288,484],{"class":295},[290,19290,19090],{"class":1561},[290,19292,490],{"class":295},[290,19294,1632],{"class":461},[290,19296,1635],{"class":461},[290,19298,484],{"class":295},[290,19300,19101],{"class":1561},[290,19302,500],{"class":295},[290,19304,19305],{"class":163,"line":784},[290,19306,620],{"class":295},[14,19308,19309,19310,19312,19313,19316],{},"The key is rule 2. A browser that does not recognise ",[18,19311,1495],{}," treats the whole selector ",[18,19314,19315],{},":focus:not(:focus-visible)"," as invalid and discards it, so the legacy ring from rule 1 stays. A browser that does recognise it evaluates the selector, suppresses the ring on pointer focus, and lets rule 3 paint the keyboard ring. You get correct behaviour on both classes of engine from one stylesheet.",[50,19318,4795],{"id":4794},[14,19320,19321,19322,19324,19325,19328],{},"The load-bearing selector is ",[18,19323,19315],{},". It means \"focused, but not in a way the engine considers worth indicating\" — precisely the mouse-click case designers object to. Removing the outline only inside that narrow selector is what lets you delete the click-ring without ever touching the keyboard ring. This is the entire reason the JavaScript polyfill became unnecessary: the modality detection that the polyfill computed in script is now expressed declaratively, and ",[18,19326,19327],{},":not()"," gives you a forward-compatible way to apply the cleanup only where the new pseudo-class exists.",[50,19330,19332],{"id":19331},"variation-high-contrast-and-forced-colors","Variation: high-contrast and forced-colors",[14,19334,19335,19336,19338,19339,19341],{},"Some users run a forced-colors mode (Windows High Contrast). In that mode the system overrides your colours, and an ",[18,19337,1502],{}," survives where a ",[18,19340,1506],{}," ring may not. Keep the outline as the primary indicator and let the system theme it.",[281,19343,19345],{"className":438,"code":19344,"language":440,"meta":286,"style":286},"@media (forced-colors: active) {\n  .btn:focus-visible {\n    \u002F* Use the system's highlight colour; box-shadow is ignored here. *\u002F\n    outline: var(--ring-width) solid Highlight;\n    box-shadow: none;\n  }\n}\n",[18,19346,19347,19353,19360,19365,19386,19396,19400],{"__ignoreMap":286},[290,19348,19349,19351],{"class":163,"line":292},[290,19350,874],{"class":541},[290,19352,2190],{"class":295},[290,19354,19355,19358],{"class":163,"line":330},[290,19356,19357],{"class":303},"  .btn:focus-visible",[290,19359,450],{"class":295},[290,19361,19362],{"class":163,"line":337},[290,19363,19364],{"class":455},"    \u002F* Use the system's highlight colour; box-shadow is ignored here. *\u002F\n",[290,19366,19367,19369,19371,19373,19375,19377,19379,19381,19384],{"class":163,"line":364},[290,19368,2136],{"class":461},[290,19370,465],{"class":295},[290,19372,1622],{"class":461},[290,19374,484],{"class":295},[290,19376,19090],{"class":1561},[290,19378,490],{"class":295},[290,19380,1632],{"class":461},[290,19382,19383],{"class":1216}," Highlight",[290,19385,471],{"class":295},[290,19387,19388,19390,19392,19394],{"class":163,"line":386},[290,19389,3279],{"class":461},[290,19391,465],{"class":295},[290,19393,72],{"class":461},[290,19395,471],{"class":295},[290,19397,19398],{"class":163,"line":408},[290,19399,771],{"class":295},[290,19401,19402],{"class":163,"line":428},[290,19403,620],{"class":295},[14,19405,19406],{},"For dark themes, swap the offset ring colour so the halo stays visible:",[281,19408,19410],{"className":438,"code":19409,"language":440,"meta":286,"style":286},"@media (prefers-color-scheme: dark) {\n  .btn:focus-visible { box-shadow: 0 0 0 calc(var(--ring-width) + 2px) #11151c; }\n}\n",[18,19411,19412,19418,19459],{"__ignoreMap":286},[290,19413,19414,19416],{"class":163,"line":292},[290,19415,874],{"class":541},[290,19417,12872],{"class":295},[290,19419,19420,19422,19424,19426,19428,19430,19432,19434,19436,19438,19440,19442,19444,19446,19448,19450,19452,19454,19457],{"class":163,"line":330},[290,19421,19357],{"class":303},[290,19423,790],{"class":295},[290,19425,1506],{"class":461},[290,19427,465],{"class":295},[290,19429,487],{"class":461},[290,19431,1198],{"class":461},[290,19433,1198],{"class":461},[290,19435,19230],{"class":461},[290,19437,484],{"class":295},[290,19439,1622],{"class":461},[290,19441,484],{"class":295},[290,19443,19090],{"class":1561},[290,19445,490],{"class":295},[290,19447,19243],{"class":541},[290,19449,3290],{"class":461},[290,19451,674],{"class":541},[290,19453,490],{"class":295},[290,19455,19456],{"class":461},"#11151c",[290,19458,809],{"class":295},[290,19460,19461],{"class":163,"line":337},[290,19462,620],{"class":295},[50,19464,4904],{"id":2247},[14,19466,19467,19469,19470,19472,19473,19475,19476,19479],{},[18,19468,1495],{}," is supported natively in Chrome 86+, Edge 86+, Firefox 85+, and Safari 15.4+, which has covered all evergreen browsers since 2021 or 2022. The ",[18,19471,1532],{}," fallback in the implementation handles anything older. Because the cleanup selector ",[18,19474,19315],{}," is parse-discarded by unsupporting engines, you do not need an ",[18,19477,19478],{},"@supports selector(:focus-visible)"," wrapper — the cascade handles the fallback for you.",[50,19481,1316],{"id":1315},[14,19483,19484,19491,19493,19494,19496],{},[62,19485,19486,19487,69,19489,5734],{},"What is the difference between ",[18,19488,1532],{},[18,19490,1495],{},[18,19492,1532],{}," matches any focused element regardless of input device, so it shows a ring even on mouse clicks. ",[18,19495,1495],{}," matches only when the browser heuristic decides a visible indicator is useful, which is essentially keyboard and other non-pointer focus.",[14,19498,19499,19502,19503,19505,19506,19508],{},[62,19500,19501],{},"Do I still need the focus-visible polyfill in 2026?","\nNo. Native ",[18,19504,1495],{}," is supported in every evergreen browser since 2021, so the WICG focus-visible JavaScript polyfill is obsolete. Use the native pseudo-class with a plain ",[18,19507,1532],{}," fallback for very old engines.",[14,19510,19511,19514,19515,19517,19518,19520],{},[62,19512,19513],{},"Should I ever remove the focus outline entirely?","\nNever remove a focus indicator without replacing it with an equally visible one. Removing ",[18,19516,1532],{}," outlines breaks keyboard navigation and fails WCAG 2.4.7. Scope your custom ring to ",[18,19519,1495],{}," instead.",[14,19522,19523,19529],{},[62,19524,19525,19526,19528],{},"Why does ",[18,19527,1495],{}," sometimes show a ring after a mouse click?","\nThe heuristic shows the ring when the element was focused programmatically or is a text input, since those benefit from a visible caret context. For ordinary buttons, a mouse click suppresses the ring.",[50,19531,1391],{"id":1390},[1393,19533,19534,19539,19544,19551,19556],{},[1396,19535,19536,19538],{},[27,19537,3345],{"href":3344}," — the parent guide on interactive state styling.",[1396,19540,19541,19543],{},[27,19542,6532],{"href":3349}," — contrast and sizing requirements for the ring itself.",[1396,19545,19546,19550],{},[27,19547,19549],{"href":19548},"\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Faccessible-css-only-tooltips\u002F","Accessible CSS-Only Tooltips"," — pairs focusable triggers with focus-revealed content.",[1396,19552,19553,19555],{},[27,19554,18535],{"href":18534}," — keeping hover and focus visuals in parity.",[1396,19557,19558,19562,19563,19566],{},[27,19559,19561],{"href":19560},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Ffeature-detection-with-supports\u002F","Feature Detection With @supports"," — the ",[18,19564,19565],{},"@supports selector()"," form for guarding newer selectors.",[1430,19568,19569],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":286,"searchDepth":330,"depth":330,"links":19571},[19572,19573,19574,19575,19576,19577,19578],{"id":18796,"depth":330,"text":18797},{"id":4202,"depth":330,"text":4203},{"id":4794,"depth":330,"text":4795},{"id":19331,"depth":330,"text":19332},{"id":2247,"depth":330,"text":4904},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Why :focus-visible replaced the focus-ring polyfill: how the heuristic differs from :focus, modern fallback patterns, and accessible keyboard-only outlines.",{"seoTitle":19581,"datePublished":1447,"dateModified":1447,"faq":19582},":focus-visible vs :focus & Polyfill Options",[19583,19586,19588,19590],{"q":19584,"a":19585},"What is the difference between :focus and :focus-visible?",":focus matches any focused element regardless of input device, so it shows a ring even on mouse clicks. :focus-visible matches only when the browser heuristic decides a visible indicator is useful, which is essentially keyboard and other non-pointer focus.",{"q":19501,"a":19587},"No. Native :focus-visible is supported in every evergreen browser since 2021, so the WICG focus-visible JavaScript polyfill is obsolete. Use the native pseudo-class with a plain :focus fallback for very old engines.",{"q":19513,"a":19589},"Never remove a focus indicator without replacing it with an equally visible one. Removing :focus outlines breaks keyboard navigation and fails WCAG 2.4.7. Scope your custom ring to :focus-visible instead.",{"q":19591,"a":19592},"Why does :focus-visible sometimes show a ring after a mouse click?","The heuristic shows the ring when the element was focused programmatically or is a text input, since those benefit from a visible caret context. For ordinary buttons, a mouse click suppresses the ring.","\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Ffocus-visible-vs-focus-polyfill-alternatives",{"title":18744,"description":19579},"css-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Ffocus-visible-vs-focus-polyfill-alternatives\u002Findex","FwPULdnujTXHjvP_92gNWtY-hp9dWxLEx2qfdLRkq4M",{"id":19598,"title":19599,"body":19600,"description":21656,"extension":1444,"meta":21657,"navigation":333,"path":21668,"seo":21669,"stem":21670,"__hash__":21671},"content\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Findex.md","Hover & Focus State Design: Spec-Compliant Patterns for Modern UIs",{"type":7,"value":19601,"toc":21637},[19602,19605,19611,19669,19674,19688,19690,19694,19697,19701,19704,20227,20232,20234,20238,20248,20252,20499,20516,20518,20522,20535,20539,20748,20757,20759,20761,20770,20774,20957,20959,21012,21014,21018,21021,21391,21409,21411,21415,21420,21449,21454,21548,21550,21552,21562,21573,21593,21602,21604,21606,21634],[10,19603,19599],{"id":19604},"hover-focus-state-design-spec-compliant-patterns-for-modern-uis",[14,19606,19607,19608,19610],{},"A comprehensive guide to architecting robust hover and focus states using modern, spec-compliant CSS. This blueprint bridges foundational ",[27,19609,1481],{"href":1480}," principles with production-ready component patterns. We’ll explore state isolation, transition orchestration, and WCAG-compliant focus indicators, ensuring your interfaces remain performant and accessible across input modalities.",[133,19612,140,19614,140,19617,140,19620,140,19623,140,19625,140,19630,140,19632,140,19636,140,19638,140,19640,140,19642,140,19644,140,19648,140,19650,140,19653,140,19655,140,19658,140,19661,140,19663,140,19666],{"viewBox":2588,"role":136,"ariaLabel":19613,"xmlns":138,"style":139},"Pointer and keyboard inputs routed to hover, focus, and focus-visible states",[142,19615,19616],{},"Input modality to state mapping",[146,19618,19619],{},"Diagram showing how pointer and keyboard inputs map to hover, focus, and focus-visible CSS pseudo-classes.",[150,19621,19622],{"x":152,"y":5891,"style":154},"Inputs map to state pseudo-classes",[171,19624],{"x":4146,"y":5144,"width":2602,"height":5139,"rx":176,"fill":177,"opacity":178},[150,19626,19629],{"x":182,"y":19627,"style":19628},"99","text-anchor:middle;fill:currentColor;font:14px sans-serif","Fine pointer",[171,19631],{"x":4146,"y":174,"width":2602,"height":5139,"rx":176,"fill":177,"opacity":178},[150,19633,19635],{"x":182,"y":19634,"style":19628},"229","Keyboard",[163,19637],{"x1":538,"y1":18845,"x2":5397,"y2":18845,"stroke":167,"strokeWidth":168},[163,19639],{"x1":538,"y1":191,"x2":5397,"y2":1830,"stroke":167,"strokeWidth":168},[163,19641],{"x1":538,"y1":191,"x2":5397,"y2":10283,"stroke":167,"strokeWidth":168},[171,19643],{"x":5397,"y":5144,"width":2602,"height":5139,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},[150,19645,3689],{"x":19646,"y":19627,"style":19647},"390","text-anchor:middle;fill:currentColor;font:13px ui-monospace,monospace",[171,19649],{"x":5397,"y":6708,"width":2602,"height":5139,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},[150,19651,1495],{"x":19646,"y":19652,"style":19647},"165",[171,19654],{"x":5397,"y":220,"width":2602,"height":5139,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},[150,19656,1532],{"x":19646,"y":19657,"style":19647},"295",[163,19659],{"x1":19660,"y1":1830,"x2":2619,"y2":1830,"stroke":167,"strokeWidth":168},"480",[171,19662],{"x":2619,"y":6708,"width":182,"height":5139,"rx":176,"fill":177,"opacity":4173},[150,19664,19665],{"x":1816,"y":19652,"style":19628},"Visible ring",[150,19667,19668],{"x":152,"y":6740,"style":1808},"Keyboard focus surfaces the ring; mouse focus stays quiet",[14,19670,19671],{},[62,19672,19673],{},"Key Implementation Principles:",[1393,19675,19676,19679,19682,19685],{},[1396,19677,19678],{},"State-driven architecture over event-driven JavaScript",[1396,19680,19681],{},"WCAG 2.2 focus visibility requirements (SC 2.4.11)",[1396,19683,19684],{},"Performance-safe transition properties (compositor-only)",[1396,19686,19687],{},"Component-scoped state variables for predictable theming",[47,19689],{},[50,19691,19693],{"id":19692},"the-architecture-of-interactive-states","The Architecture of Interactive States",[14,19695,19696],{},"State management in modern CSS relies on declarative boundaries and custom properties rather than imperative class toggling. By isolating interactive states at the component level, you eliminate cascade collisions and enable predictable progressive enhancement.",[2757,19698,19700],{"id":19699},"css-custom-property-state-tokens","CSS Custom Property State Tokens",[14,19702,19703],{},"Define state variables at the component root. This creates a single source of truth for interactive values and allows runtime theming without selector bloat.",[281,19705,19707],{"className":438,"code":19706,"language":440,"meta":286,"style":286},"\u002F* Interactive Component Architecture *\u002F\n.card {\n  \u002F* Base state tokens *\u002F\n  --card-bg: #ffffff;\n  --card-border: 1px solid #e2e8f0;\n  --card-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n\n  \u002F* Interactive state overrides *\u002F\n  --card-hover-bg: #f8fafc;\n  --card-hover-border: 1px solid #cbd5e1;\n  --card-hover-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);\n\n  \u002F* Focus state overrides *\u002F\n  --card-focus-ring: 0 0 0 3px rgba(59, 130, 246, 0.5);\n  --card-focus-offset: 2px;\n\n  background: var(--card-bg);\n  border: var(--card-border);\n  box-shadow: var(--card-shadow);\n  transition:\n    background 150ms ease,\n    box-shadow 150ms ease,\n    border-color 150ms ease;\n  border-radius: 8px;\n  cursor: pointer;\n}\n\n\u002F* State application via pseudo-classes *\u002F\n.card:hover {\n  background: var(--card-hover-bg);\n  border-color: var(--card-hover-border);\n  box-shadow: var(--card-hover-shadow);\n}\n\n.card:focus-visible {\n  \u002F* outline must be present for Windows High Contrast Mode (WCAG 2.4.11) *\u002F\n  outline: 3px solid rgba(59, 130, 246, 0.8);\n  outline-offset: var(--card-focus-offset);\n  box-shadow: var(--card-focus-ring);\n  transform: translateY(calc(var(--card-focus-offset) * -1));\n}\n",[18,19708,19709,19714,19720,19725,19737,19755,19793,19797,19802,19814,19832,19875,19879,19884,19923,19936,19940,19955,19970,19985,19991,20004,20016,20028,20040,20050,20054,20058,20063,20069,20084,20099,20114,20118,20122,20128,20133,20165,20180,20195,20223],{"__ignoreMap":286},[290,19710,19711],{"class":163,"line":292},[290,19712,19713],{"class":455},"\u002F* Interactive Component Architecture *\u002F\n",[290,19715,19716,19718],{"class":163,"line":330},[290,19717,11528],{"class":303},[290,19719,450],{"class":295},[290,19721,19722],{"class":163,"line":337},[290,19723,19724],{"class":455},"  \u002F* Base state tokens *\u002F\n",[290,19726,19727,19730,19732,19735],{"class":163,"line":364},[290,19728,19729],{"class":1561},"  --card-bg",[290,19731,465],{"class":295},[290,19733,19734],{"class":461},"#ffffff",[290,19736,471],{"class":295},[290,19738,19739,19742,19744,19746,19748,19750,19753],{"class":163,"line":386},[290,19740,19741],{"class":1561},"  --card-border",[290,19743,465],{"class":295},[290,19745,468],{"class":461},[290,19747,674],{"class":541},[290,19749,852],{"class":461},[290,19751,19752],{"class":461}," #e2e8f0",[290,19754,471],{"class":295},[290,19756,19757,19760,19762,19764,19766,19768,19771,19773,19775,19777,19779,19781,19783,19785,19787,19789,19791],{"class":163,"line":408},[290,19758,19759],{"class":1561},"  --card-shadow",[290,19761,465],{"class":295},[290,19763,487],{"class":461},[290,19765,804],{"class":461},[290,19767,674],{"class":541},[290,19769,19770],{"class":461}," 3",[290,19772,674],{"class":541},[290,19774,3224],{"class":461},[290,19776,484],{"class":295},[290,19778,487],{"class":461},[290,19780,569],{"class":295},[290,19782,487],{"class":461},[290,19784,569],{"class":295},[290,19786,487],{"class":461},[290,19788,569],{"class":295},[290,19790,3125],{"class":461},[290,19792,500],{"class":295},[290,19794,19795],{"class":163,"line":428},[290,19796,334],{"emptyLinePlaceholder":333},[290,19798,19799],{"class":163,"line":517},[290,19800,19801],{"class":455},"  \u002F* Interactive state overrides *\u002F\n",[290,19803,19804,19807,19809,19812],{"class":163,"line":523},[290,19805,19806],{"class":1561},"  --card-hover-bg",[290,19808,465],{"class":295},[290,19810,19811],{"class":461},"#f8fafc",[290,19813,471],{"class":295},[290,19815,19816,19819,19821,19823,19825,19827,19830],{"class":163,"line":532},[290,19817,19818],{"class":1561},"  --card-hover-border",[290,19820,465],{"class":295},[290,19822,468],{"class":461},[290,19824,674],{"class":541},[290,19826,852],{"class":461},[290,19828,19829],{"class":461}," #cbd5e1",[290,19831,471],{"class":295},[290,19833,19834,19837,19839,19841,19843,19845,19848,19850,19853,19855,19857,19859,19861,19863,19865,19867,19869,19871,19873],{"class":163,"line":551},[290,19835,19836],{"class":1561},"  --card-hover-shadow",[290,19838,465],{"class":295},[290,19840,487],{"class":461},[290,19842,12650],{"class":461},[290,19844,674],{"class":541},[290,19846,19847],{"class":461}," 6",[290,19849,674],{"class":541},[290,19851,19852],{"class":461}," -1",[290,19854,674],{"class":541},[290,19856,3224],{"class":461},[290,19858,484],{"class":295},[290,19860,487],{"class":461},[290,19862,569],{"class":295},[290,19864,487],{"class":461},[290,19866,569],{"class":295},[290,19868,487],{"class":461},[290,19870,569],{"class":295},[290,19872,3125],{"class":461},[290,19874,500],{"class":295},[290,19876,19877],{"class":163,"line":586},[290,19878,334],{"emptyLinePlaceholder":333},[290,19880,19881],{"class":163,"line":602},[290,19882,19883],{"class":455},"  \u002F* Focus state overrides *\u002F\n",[290,19885,19886,19889,19891,19893,19895,19897,19899,19901,19903,19905,19908,19910,19912,19914,19917,19919,19921],{"class":163,"line":617},[290,19887,19888],{"class":1561},"  --card-focus-ring",[290,19890,465],{"class":295},[290,19892,487],{"class":461},[290,19894,1198],{"class":461},[290,19896,1198],{"class":461},[290,19898,19770],{"class":461},[290,19900,674],{"class":541},[290,19902,3224],{"class":461},[290,19904,484],{"class":295},[290,19906,19907],{"class":461},"59",[290,19909,569],{"class":295},[290,19911,182],{"class":461},[290,19913,569],{"class":295},[290,19915,19916],{"class":461},"246",[290,19918,569],{"class":295},[290,19920,798],{"class":461},[290,19922,500],{"class":295},[290,19924,19925,19928,19930,19932,19934],{"class":163,"line":623},[290,19926,19927],{"class":1561},"  --card-focus-offset",[290,19929,465],{"class":295},[290,19931,194],{"class":461},[290,19933,674],{"class":541},[290,19935,471],{"class":295},[290,19937,19938],{"class":163,"line":628},[290,19939,334],{"emptyLinePlaceholder":333},[290,19941,19942,19944,19946,19948,19950,19953],{"class":163,"line":634},[290,19943,1186],{"class":461},[290,19945,465],{"class":295},[290,19947,1622],{"class":461},[290,19949,484],{"class":295},[290,19951,19952],{"class":1561},"--card-bg",[290,19954,500],{"class":295},[290,19956,19957,19959,19961,19963,19965,19968],{"class":163,"line":649},[290,19958,1948],{"class":461},[290,19960,465],{"class":295},[290,19962,1622],{"class":461},[290,19964,484],{"class":295},[290,19966,19967],{"class":1561},"--card-border",[290,19969,500],{"class":295},[290,19971,19972,19974,19976,19978,19980,19983],{"class":163,"line":660},[290,19973,3207],{"class":461},[290,19975,465],{"class":295},[290,19977,1622],{"class":461},[290,19979,484],{"class":295},[290,19981,19982],{"class":1561},"--card-shadow",[290,19984,500],{"class":295},[290,19986,19987,19989],{"class":163,"line":688},[290,19988,526],{"class":461},[290,19990,529],{"class":295},[290,19992,19993,19995,19998,20000,20002],{"class":163,"line":693},[290,19994,9124],{"class":1216},[290,19996,19997],{"class":461}," 150",[290,19999,542],{"class":541},[290,20001,545],{"class":461},[290,20003,548],{"class":295},[290,20005,20006,20008,20010,20012,20014],{"class":163,"line":698},[290,20007,3140],{"class":295},[290,20009,1787],{"class":461},[290,20011,542],{"class":541},[290,20013,545],{"class":461},[290,20015,548],{"class":295},[290,20017,20018,20020,20022,20024,20026],{"class":163,"line":704},[290,20019,1983],{"class":295},[290,20021,1787],{"class":461},[290,20023,542],{"class":541},[290,20025,545],{"class":461},[290,20027,471],{"class":295},[290,20029,20030,20032,20034,20036,20038],{"class":163,"line":710},[290,20031,1663],{"class":461},[290,20033,465],{"class":295},[290,20035,176],{"class":461},[290,20037,674],{"class":541},[290,20039,471],{"class":295},[290,20041,20042,20044,20046,20048],{"class":163,"line":717},[290,20043,17841],{"class":461},[290,20045,465],{"class":295},[290,20047,18849],{"class":461},[290,20049,471],{"class":295},[290,20051,20052],{"class":163,"line":730},[290,20053,620],{"class":295},[290,20055,20056],{"class":163,"line":742},[290,20057,334],{"emptyLinePlaceholder":333},[290,20059,20060],{"class":163,"line":768},[290,20061,20062],{"class":455},"\u002F* State application via pseudo-classes *\u002F\n",[290,20064,20065,20067],{"class":163,"line":774},[290,20066,11814],{"class":303},[290,20068,450],{"class":295},[290,20070,20071,20073,20075,20077,20079,20082],{"class":163,"line":779},[290,20072,1186],{"class":461},[290,20074,465],{"class":295},[290,20076,1622],{"class":461},[290,20078,484],{"class":295},[290,20080,20081],{"class":1561},"--card-hover-bg",[290,20083,500],{"class":295},[290,20085,20086,20088,20090,20092,20094,20097],{"class":163,"line":784},[290,20087,2034],{"class":461},[290,20089,465],{"class":295},[290,20091,1622],{"class":461},[290,20093,484],{"class":295},[290,20095,20096],{"class":1561},"--card-hover-border",[290,20098,500],{"class":295},[290,20100,20101,20103,20105,20107,20109,20112],{"class":163,"line":812},[290,20102,3207],{"class":461},[290,20104,465],{"class":295},[290,20106,1622],{"class":461},[290,20108,484],{"class":295},[290,20110,20111],{"class":1561},"--card-hover-shadow",[290,20113,500],{"class":295},[290,20115,20116],{"class":163,"line":860},[290,20117,620],{"class":295},[290,20119,20120],{"class":163,"line":865},[290,20121,334],{"emptyLinePlaceholder":333},[290,20123,20124,20126],{"class":163,"line":871},[290,20125,14263],{"class":303},[290,20127,450],{"class":295},[290,20129,20130],{"class":163,"line":880},[290,20131,20132],{"class":455},"  \u002F* outline must be present for Windows High Contrast Mode (WCAG 2.4.11) *\u002F\n",[290,20134,20135,20137,20139,20141,20143,20145,20147,20149,20151,20153,20155,20157,20159,20161,20163],{"class":163,"line":896},[290,20136,1617],{"class":461},[290,20138,465],{"class":295},[290,20140,1579],{"class":461},[290,20142,674],{"class":541},[290,20144,852],{"class":461},[290,20146,3224],{"class":461},[290,20148,484],{"class":295},[290,20150,19907],{"class":461},[290,20152,569],{"class":295},[290,20154,182],{"class":461},[290,20156,569],{"class":295},[290,20158,19916],{"class":461},[290,20160,569],{"class":295},[290,20162,572],{"class":461},[290,20164,500],{"class":295},[290,20166,20167,20169,20171,20173,20175,20178],{"class":163,"line":4734},[290,20168,1647],{"class":461},[290,20170,465],{"class":295},[290,20172,1622],{"class":461},[290,20174,484],{"class":295},[290,20176,20177],{"class":1561},"--card-focus-offset",[290,20179,500],{"class":295},[290,20181,20182,20184,20186,20188,20190,20193],{"class":163,"line":4742},[290,20183,3207],{"class":461},[290,20185,465],{"class":295},[290,20187,1622],{"class":461},[290,20189,484],{"class":295},[290,20191,20192],{"class":1561},"--card-focus-ring",[290,20194,500],{"class":295},[290,20196,20197,20199,20201,20203,20205,20207,20209,20211,20213,20215,20217,20219,20221],{"class":163,"line":4761},[290,20198,476],{"class":461},[290,20200,465],{"class":295},[290,20202,481],{"class":461},[290,20204,484],{"class":295},[290,20206,3556],{"class":461},[290,20208,484],{"class":295},[290,20210,1622],{"class":461},[290,20212,484],{"class":295},[290,20214,20177],{"class":1561},[290,20216,490],{"class":295},[290,20218,4415],{"class":541},[290,20220,19852],{"class":461},[290,20222,11616],{"class":295},[290,20224,20225],{"class":163,"line":4766},[290,20226,620],{"class":295},[14,20228,20229,20231],{},[62,20230,15215],{}," CSS Custom Properties for Cascading Variables Module Level 1. Custom properties inherit and cascade predictably, making them ideal for state-driven design systems.",[47,20233],{},[50,20235,20237],{"id":20236},"spec-compliant-hover-patterns","Spec-Compliant Hover Patterns",[14,20239,20240,20241,20243,20244,20247],{},"Hover states must respect user input capabilities. Applying hover effects indiscriminately causes accidental triggers on touch devices and violates progressive enhancement principles. Building on ",[27,20242,30],{"href":29},", we can conditionally apply hover logic using Media Queries Level 4. Hover-revealed content such as ",[27,20245,20246],{"href":19548},"accessible CSS-only tooltips"," needs the same guardrails, since touch users never receive a hover event to dismiss them.",[2757,20249,20251],{"id":20250},"media-query-hover-detection","Media Query Hover Detection",[281,20253,20255],{"className":438,"code":20254,"language":440,"meta":286,"style":286},"\u002F* Only apply hover states on devices with precise pointing mechanisms *\u002F\n@media (hover: hover) and (pointer: fine) {\n  .btn-primary {\n    transition:\n      transform 120ms cubic-bezier(0.4, 0, 0.2, 1),\n      background-color 120ms ease;\n  }\n\n  .btn-primary:hover {\n    transform: scale(1.02);\n    background-color: var(--btn-hover-bg);\n  }\n\n  \u002F* Debounce accidental cursor drift *\u002F\n  .btn-primary:active {\n    transition-duration: 50ms;\n    transform: scale(0.98);\n  }\n}\n\n\u002F* Fallback for touch-first or coarse-pointer devices *\u002F\n@media (hover: none) {\n  .btn-primary {\n    \u002F* Ensure touch targets remain visually stable *\u002F\n    min-height: 44px;\n    min-width: 44px;\n  }\n}\n",[18,20256,20257,20262,20281,20288,20294,20323,20336,20340,20344,20351,20365,20379,20383,20387,20392,20399,20411,20426,20430,20434,20438,20443,20454,20460,20465,20478,20491,20495],{"__ignoreMap":286},[290,20258,20259],{"class":163,"line":292},[290,20260,20261],{"class":455},"\u002F* Only apply hover states on devices with precise pointing mechanisms *\u002F\n",[290,20263,20264,20266,20268,20270,20272,20274,20276,20278],{"class":163,"line":330},[290,20265,874],{"class":541},[290,20267,3595],{"class":295},[290,20269,17440],{"class":461},[290,20271,465],{"class":295},[290,20273,17440],{"class":461},[290,20275,490],{"class":295},[290,20277,11985],{"class":541},[290,20279,20280],{"class":295}," (pointer: fine) {\n",[290,20282,20283,20286],{"class":163,"line":337},[290,20284,20285],{"class":303},"  .btn-primary",[290,20287,450],{"class":295},[290,20289,20290,20292],{"class":163,"line":364},[290,20291,4745],{"class":461},[290,20293,529],{"class":295},[290,20295,20296,20299,20301,20303,20305,20307,20309,20311,20313,20315,20317,20319,20321],{"class":163,"line":386},[290,20297,20298],{"class":295},"      transform ",[290,20300,4147],{"class":461},[290,20302,542],{"class":541},[290,20304,561],{"class":461},[290,20306,484],{"class":295},[290,20308,169],{"class":461},[290,20310,569],{"class":295},[290,20312,487],{"class":461},[290,20314,569],{"class":295},[290,20316,566],{"class":461},[290,20318,569],{"class":295},[290,20320,468],{"class":461},[290,20322,583],{"class":295},[290,20324,20325,20328,20330,20332,20334],{"class":163,"line":408},[290,20326,20327],{"class":295},"      background-color ",[290,20329,4147],{"class":461},[290,20331,542],{"class":541},[290,20333,545],{"class":461},[290,20335,471],{"class":295},[290,20337,20338],{"class":163,"line":428},[290,20339,771],{"class":295},[290,20341,20342],{"class":163,"line":517},[290,20343,334],{"emptyLinePlaceholder":333},[290,20345,20346,20349],{"class":163,"line":523},[290,20347,20348],{"class":303},"  .btn-primary:hover",[290,20350,450],{"class":295},[290,20352,20353,20355,20357,20359,20361,20363],{"class":163,"line":532},[290,20354,745],{"class":461},[290,20356,465],{"class":295},[290,20358,493],{"class":461},[290,20360,484],{"class":295},[290,20362,2057],{"class":461},[290,20364,500],{"class":295},[290,20366,20367,20369,20371,20373,20375,20377],{"class":163,"line":551},[290,20368,5350],{"class":461},[290,20370,465],{"class":295},[290,20372,1622],{"class":461},[290,20374,484],{"class":295},[290,20376,13053],{"class":1561},[290,20378,500],{"class":295},[290,20380,20381],{"class":163,"line":586},[290,20382,771],{"class":295},[290,20384,20385],{"class":163,"line":602},[290,20386,334],{"emptyLinePlaceholder":333},[290,20388,20389],{"class":163,"line":617},[290,20390,20391],{"class":455},"  \u002F* Debounce accidental cursor drift *\u002F\n",[290,20393,20394,20397],{"class":163,"line":623},[290,20395,20396],{"class":303},"  .btn-primary:active",[290,20398,450],{"class":295},[290,20400,20401,20403,20405,20407,20409],{"class":163,"line":628},[290,20402,2856],{"class":461},[290,20404,465],{"class":295},[290,20406,1831],{"class":461},[290,20408,542],{"class":541},[290,20410,471],{"class":295},[290,20412,20413,20415,20417,20419,20421,20424],{"class":163,"line":634},[290,20414,745],{"class":461},[290,20416,465],{"class":295},[290,20418,493],{"class":461},[290,20420,484],{"class":295},[290,20422,20423],{"class":461},"0.98",[290,20425,500],{"class":295},[290,20427,20428],{"class":163,"line":649},[290,20429,771],{"class":295},[290,20431,20432],{"class":163,"line":660},[290,20433,620],{"class":295},[290,20435,20436],{"class":163,"line":688},[290,20437,334],{"emptyLinePlaceholder":333},[290,20439,20440],{"class":163,"line":693},[290,20441,20442],{"class":455},"\u002F* Fallback for touch-first or coarse-pointer devices *\u002F\n",[290,20444,20445,20447,20449,20451],{"class":163,"line":698},[290,20446,874],{"class":541},[290,20448,3595],{"class":295},[290,20450,17440],{"class":461},[290,20452,20453],{"class":295},": none) {\n",[290,20455,20456,20458],{"class":163,"line":704},[290,20457,20285],{"class":303},[290,20459,450],{"class":295},[290,20461,20462],{"class":163,"line":710},[290,20463,20464],{"class":455},"    \u002F* Ensure touch targets remain visually stable *\u002F\n",[290,20466,20467,20470,20472,20474,20476],{"class":163,"line":717},[290,20468,20469],{"class":461},"    min-height",[290,20471,465],{"class":295},[290,20473,8879],{"class":461},[290,20475,674],{"class":541},[290,20477,471],{"class":295},[290,20479,20480,20483,20485,20487,20489],{"class":163,"line":730},[290,20481,20482],{"class":461},"    min-width",[290,20484,465],{"class":295},[290,20486,8879],{"class":461},[290,20488,674],{"class":541},[290,20490,471],{"class":295},[290,20492,20493],{"class":163,"line":742},[290,20494,771],{"class":295},[290,20496,20497],{"class":163,"line":768},[290,20498,620],{"class":295},[14,20500,20501,20504,20505,20507,20508,20511,20512,20515],{},[62,20502,20503],{},"Implementation Note:"," The ",[18,20506,15016],{}," property can simulate hover debouncing, but native ",[18,20509,20510],{},"@media (hover: hover)"," is more reliable for preventing touch-triggered hover states. Always pair hover effects with ",[18,20513,20514],{},":active"," states to provide immediate tactile feedback.",[47,20517],{},[50,20519,20521],{"id":20520},"accessible-focus-state-engineering","Accessible Focus State Engineering",[14,20523,20524,20525,20527,20528,20530,20531,20534],{},"Focus indicators are non-negotiable for keyboard navigation. The ",[18,20526,1532],{}," pseudo-class applies to all focus events (mouse, touch, keyboard), which often results in visual clutter. CSS UI Level 4 introduced ",[18,20529,1495],{}," to solve this. If you still support engines that predate it, the comparison of ",[27,20532,20533],{"href":1526},"focus-visible vs focus polyfill alternatives"," explains which heuristics a polyfill restores and where it falls short.",[2757,20536,20538],{"id":20537},"focus-visible-outline-system","Focus-Visible Outline System",[281,20540,20542],{"className":438,"code":20541,"language":440,"meta":286,"style":286},"\u002F* Apply focus ring ONLY during keyboard navigation.\n   Do NOT set outline: none globally — suppress the default only\n   where you are providing a replacement. *\u002F\n:focus-visible {\n  outline: 2px solid #2563eb;\n  outline-offset: 3px;\n  border-radius: 4px;\n}\n\n\u002F* Suppress the default outline only on elements that have :focus-visible\n   styling above — browsers that understand :focus-visible will use the\n   rule above; older browsers that don't will still show the default. *\u002F\n:focus:not(:focus-visible) {\n  outline: none;\n}\n\n\u002F* High-contrast fallback for dark mode *\u002F\n@media (prefers-color-scheme: dark) {\n  :focus-visible {\n    outline-color: #60a5fa;\n  }\n}\n\n\u002F* Ensure focus rings aren't clipped by parent containers *\u002F\n.parent-container {\n  \u002F* Avoid overflow: hidden on focusable parents *\u002F\n  overflow: visible;\n  \u002F* If clipping is necessary, use padding instead *\u002F\n  padding: 4px;\n}\n",[18,20543,20544,20549,20554,20559,20565,20582,20594,20606,20610,20614,20619,20624,20629,20640,20650,20654,20658,20663,20669,20676,20688,20692,20696,20700,20705,20712,20717,20727,20732,20744],{"__ignoreMap":286},[290,20545,20546],{"class":163,"line":292},[290,20547,20548],{"class":455},"\u002F* Apply focus ring ONLY during keyboard navigation.\n",[290,20550,20551],{"class":163,"line":330},[290,20552,20553],{"class":455},"   Do NOT set outline: none globally — suppress the default only\n",[290,20555,20556],{"class":163,"line":337},[290,20557,20558],{"class":455},"   where you are providing a replacement. *\u002F\n",[290,20560,20561,20563],{"class":163,"line":364},[290,20562,1495],{"class":303},[290,20564,450],{"class":295},[290,20566,20567,20569,20571,20573,20575,20577,20580],{"class":163,"line":386},[290,20568,1617],{"class":461},[290,20570,465],{"class":295},[290,20572,194],{"class":461},[290,20574,674],{"class":541},[290,20576,852],{"class":461},[290,20578,20579],{"class":461}," #2563eb",[290,20581,471],{"class":295},[290,20583,20584,20586,20588,20590,20592],{"class":163,"line":408},[290,20585,1647],{"class":461},[290,20587,465],{"class":295},[290,20589,1579],{"class":461},[290,20591,674],{"class":541},[290,20593,471],{"class":295},[290,20595,20596,20598,20600,20602,20604],{"class":163,"line":428},[290,20597,1663],{"class":461},[290,20599,465],{"class":295},[290,20601,249],{"class":461},[290,20603,674],{"class":541},[290,20605,471],{"class":295},[290,20607,20608],{"class":163,"line":517},[290,20609,620],{"class":295},[290,20611,20612],{"class":163,"line":523},[290,20613,334],{"emptyLinePlaceholder":333},[290,20615,20616],{"class":163,"line":532},[290,20617,20618],{"class":455},"\u002F* Suppress the default outline only on elements that have :focus-visible\n",[290,20620,20621],{"class":163,"line":551},[290,20622,20623],{"class":455},"   styling above — browsers that understand :focus-visible will use the\n",[290,20625,20626],{"class":163,"line":586},[290,20627,20628],{"class":455},"   rule above; older browsers that don't will still show the default. *\u002F\n",[290,20630,20631,20634,20636,20638],{"class":163,"line":602},[290,20632,20633],{"class":303},":focus:not",[290,20635,484],{"class":295},[290,20637,1495],{"class":303},[290,20639,646],{"class":295},[290,20641,20642,20644,20646,20648],{"class":163,"line":617},[290,20643,1617],{"class":461},[290,20645,465],{"class":295},[290,20647,72],{"class":461},[290,20649,471],{"class":295},[290,20651,20652],{"class":163,"line":623},[290,20653,620],{"class":295},[290,20655,20656],{"class":163,"line":628},[290,20657,334],{"emptyLinePlaceholder":333},[290,20659,20660],{"class":163,"line":634},[290,20661,20662],{"class":455},"\u002F* High-contrast fallback for dark mode *\u002F\n",[290,20664,20665,20667],{"class":163,"line":649},[290,20666,874],{"class":541},[290,20668,12872],{"class":295},[290,20670,20671,20674],{"class":163,"line":660},[290,20672,20673],{"class":303},"  :focus-visible",[290,20675,450],{"class":295},[290,20677,20678,20681,20683,20686],{"class":163,"line":688},[290,20679,20680],{"class":461},"    outline-color",[290,20682,465],{"class":295},[290,20684,20685],{"class":461},"#60a5fa",[290,20687,471],{"class":295},[290,20689,20690],{"class":163,"line":693},[290,20691,771],{"class":295},[290,20693,20694],{"class":163,"line":698},[290,20695,620],{"class":295},[290,20697,20698],{"class":163,"line":704},[290,20699,334],{"emptyLinePlaceholder":333},[290,20701,20702],{"class":163,"line":710},[290,20703,20704],{"class":455},"\u002F* Ensure focus rings aren't clipped by parent containers *\u002F\n",[290,20706,20707,20710],{"class":163,"line":717},[290,20708,20709],{"class":303},".parent-container",[290,20711,450],{"class":295},[290,20713,20714],{"class":163,"line":730},[290,20715,20716],{"class":455},"  \u002F* Avoid overflow: hidden on focusable parents *\u002F\n",[290,20718,20719,20721,20723,20725],{"class":163,"line":742},[290,20720,7803],{"class":461},[290,20722,465],{"class":295},[290,20724,18225],{"class":461},[290,20726,471],{"class":295},[290,20728,20729],{"class":163,"line":768},[290,20730,20731],{"class":455},"  \u002F* If clipping is necessary, use padding instead *\u002F\n",[290,20733,20734,20736,20738,20740,20742],{"class":163,"line":774},[290,20735,10433],{"class":461},[290,20737,465],{"class":295},[290,20739,249],{"class":461},[290,20741,674],{"class":541},[290,20743,471],{"class":295},[290,20745,20746],{"class":163,"line":779},[290,20747,620],{"class":295},[14,20749,20750,20753,20754,20756],{},[62,20751,20752],{},"WCAG 2.2 Compliance:"," Focus indicators must meet a 3:1 contrast ratio against adjacent colors and have a minimum thickness of 1 CSS pixel. Using ",[18,20755,1695],{}," prevents the ring from overlapping component borders, preserving visual hierarchy.",[47,20758],{},[50,20760,1736],{"id":1735},[14,20762,20763,20764,569,20766,8393,20768,42],{},"State transitions that trigger layout or paint will cause jank. To guarantee 60fps rendering, restrict transitions to compositor-friendly properties: ",[18,20765,103],{},[18,20767,76],{},[18,20769,6792],{},[2757,20771,20773],{"id":20772},"compositor-optimized-transition-block","Compositor-Optimized Transition Block",[281,20775,20777],{"className":438,"code":20776,"language":440,"meta":286,"style":286},".interactive-element {\n  \u002F* Force GPU layer promotion *\u002F\n  will-change: transform, opacity;\n\n  \u002F* Hardware-accelerated properties only *\u002F\n  transition:\n    transform 200ms ease-out,\n    opacity 200ms ease-out;\n  transform: translateZ(0); \u002F* Legacy Safari fallback for layer promotion *\u002F\n}\n\n.interactive-element:hover {\n  transform: translateY(-4px) scale(1.01);\n  opacity: 0.95;\n}\n\n\u002F* Respect user motion preferences *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .interactive-element {\n    transition: none;\n    will-change: auto;\n  }\n}\n",[18,20778,20779,20786,20791,20797,20801,20806,20812,20824,20836,20853,20857,20861,20868,20892,20902,20906,20910,20915,20921,20928,20938,20949,20953],{"__ignoreMap":286},[290,20780,20781,20784],{"class":163,"line":292},[290,20782,20783],{"class":303},".interactive-element",[290,20785,450],{"class":295},[290,20787,20788],{"class":163,"line":330},[290,20789,20790],{"class":455},"  \u002F* Force GPU layer promotion *\u002F\n",[290,20792,20793,20795],{"class":163,"line":337},[290,20794,3518],{"class":461},[290,20796,3521],{"class":295},[290,20798,20799],{"class":163,"line":364},[290,20800,334],{"emptyLinePlaceholder":333},[290,20802,20803],{"class":163,"line":386},[290,20804,20805],{"class":455},"  \u002F* Hardware-accelerated properties only *\u002F\n",[290,20807,20808,20810],{"class":163,"line":408},[290,20809,526],{"class":461},[290,20811,529],{"class":295},[290,20813,20814,20816,20818,20820,20822],{"class":163,"line":428},[290,20815,554],{"class":295},[290,20817,174],{"class":461},[290,20819,542],{"class":541},[290,20821,1889],{"class":461},[290,20823,548],{"class":295},[290,20825,20826,20828,20830,20832,20834],{"class":163,"line":517},[290,20827,535],{"class":295},[290,20829,174],{"class":461},[290,20831,542],{"class":541},[290,20833,1889],{"class":461},[290,20835,471],{"class":295},[290,20837,20838,20840,20842,20844,20846,20848,20850],{"class":163,"line":523},[290,20839,476],{"class":461},[290,20841,465],{"class":295},[290,20843,15490],{"class":461},[290,20845,484],{"class":295},[290,20847,487],{"class":461},[290,20849,14061],{"class":295},[290,20851,20852],{"class":455},"\u002F* Legacy Safari fallback for layer promotion *\u002F\n",[290,20854,20855],{"class":163,"line":532},[290,20856,620],{"class":295},[290,20858,20859],{"class":163,"line":551},[290,20860,334],{"emptyLinePlaceholder":333},[290,20862,20863,20866],{"class":163,"line":586},[290,20864,20865],{"class":303},".interactive-element:hover",[290,20867,450],{"class":295},[290,20869,20870,20872,20874,20876,20878,20880,20882,20884,20886,20888,20890],{"class":163,"line":602},[290,20871,476],{"class":461},[290,20873,465],{"class":295},[290,20875,481],{"class":461},[290,20877,484],{"class":295},[290,20879,3189],{"class":461},[290,20881,674],{"class":541},[290,20883,490],{"class":295},[290,20885,493],{"class":461},[290,20887,484],{"class":295},[290,20889,3200],{"class":461},[290,20891,500],{"class":295},[290,20893,20894,20896,20898,20900],{"class":163,"line":617},[290,20895,462],{"class":461},[290,20897,465],{"class":295},[290,20899,1119],{"class":461},[290,20901,471],{"class":295},[290,20903,20904],{"class":163,"line":623},[290,20905,620],{"class":295},[290,20907,20908],{"class":163,"line":628},[290,20909,334],{"emptyLinePlaceholder":333},[290,20911,20912],{"class":163,"line":634},[290,20913,20914],{"class":455},"\u002F* Respect user motion preferences *\u002F\n",[290,20916,20917,20919],{"class":163,"line":649},[290,20918,874],{"class":541},[290,20920,877],{"class":295},[290,20922,20923,20926],{"class":163,"line":660},[290,20924,20925],{"class":303},"  .interactive-element",[290,20927,450],{"class":295},[290,20929,20930,20932,20934,20936],{"class":163,"line":688},[290,20931,4745],{"class":461},[290,20933,465],{"class":295},[290,20935,72],{"class":461},[290,20937,471],{"class":295},[290,20939,20940,20943,20945,20947],{"class":163,"line":693},[290,20941,20942],{"class":461},"    will-change",[290,20944,465],{"class":295},[290,20946,250],{"class":461},[290,20948,471],{"class":295},[290,20950,20951],{"class":163,"line":698},[290,20952,771],{"class":295},[290,20954,20955],{"class":163,"line":704},[290,20956,620],{"class":295},[2757,20958,13396],{"id":8357},[3017,20960,20961,20967,20980,21000],{},[1396,20962,20963,20966],{},[62,20964,20965],{},"Open Performance Panel:"," Record a hover\u002Ffocus interaction.",[1396,20968,20969,20972,20973,20975,20976,2351,20978,42],{},[62,20970,20971],{},"Enable \"Paint Flashing\" & \"Layer Borders\":"," In the Rendering tab, verify that hover states only trigger ",[18,20974,16208],{}," frames, not ",[18,20977,13462],{},[18,20979,13465],{},[1396,20981,20982,20988,20989,20992,20993,20995,20996,2351,20998,19520],{},[62,20983,20984,20985,20987],{},"Check ",[18,20986,3797],{}," Overuse:"," If the timeline shows ",[18,20990,20991],{},"GPU memory"," spikes, remove ",[18,20994,3797],{}," from idle states. Apply it dynamically via ",[18,20997,3689],{},[18,20999,1495],{},[1396,21001,21002,21007,21008,21011],{},[62,21003,21004,21005,723],{},"Audit ",[18,21006,15861],{}," For off-screen interactive components, add ",[18,21009,21010],{},"content-visibility: auto; contain-intrinsic-size: 300px;"," to skip rendering until scrolled into view.",[47,21013],{},[50,21015,21017],{"id":21016},"advanced-micro-interaction-integration","Advanced Micro-Interaction Integration",[14,21019,21020],{},"Hover and focus states rarely exist in isolation. They serve as entry points for broader animation sequences. By chaining transitions with keyframes, you can orchestrate multi-step micro-interactions without JavaScript.",[281,21022,21024],{"className":438,"code":21023,"language":440,"meta":286,"style":286},"@keyframes pulse-ring {\n  0% {\n    transform: scale(0.95);\n    opacity: 0.7;\n  }\n  50% {\n    transform: scale(1.05);\n    opacity: 0.3;\n  }\n  100% {\n    transform: scale(0.95);\n    opacity: 0.7;\n  }\n}\n\n.complex-card {\n  position: relative;\n  transition: transform 300ms ease;\n}\n\n\u002F* Trigger keyframe sequence on hover *\u002F\n@media (hover: hover) {\n  .complex-card:hover {\n    transform: translateY(-6px);\n  }\n\n  .complex-card:hover::after {\n    content: \"\";\n    position: absolute;\n    inset: -4px;\n    border: 2px solid var(--brand-primary);\n    border-radius: inherit;\n    animation: pulse-ring 1.5s infinite ease-in-out;\n    pointer-events: none;\n  }\n}\n\n\u002F* Pause animations when focus is lost or reduced motion is preferred *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .complex-card::after {\n    animation: none;\n  }\n}\n",[18,21025,21026,21035,21042,21056,21067,21071,21078,21093,21103,21107,21114,21128,21138,21142,21146,21150,21157,21167,21181,21185,21189,21194,21208,21215,21231,21235,21239,21246,21257,21268,21281,21303,21313,21332,21343,21347,21351,21355,21360,21366,21373,21383,21387],{"__ignoreMap":286},[290,21027,21028,21030,21033],{"class":163,"line":292},[290,21029,3968],{"class":541},[290,21031,21032],{"class":1561}," pulse-ring",[290,21034,450],{"class":295},[290,21036,21037,21040],{"class":163,"line":330},[290,21038,21039],{"class":303},"  0%",[290,21041,450],{"class":295},[290,21043,21044,21046,21048,21050,21052,21054],{"class":163,"line":337},[290,21045,745],{"class":461},[290,21047,465],{"class":295},[290,21049,493],{"class":461},[290,21051,484],{"class":295},[290,21053,1119],{"class":461},[290,21055,500],{"class":295},[290,21057,21058,21060,21062,21065],{"class":163,"line":364},[290,21059,733],{"class":461},[290,21061,465],{"class":295},[290,21063,21064],{"class":461},"0.7",[290,21066,471],{"class":295},[290,21068,21069],{"class":163,"line":386},[290,21070,771],{"class":295},[290,21072,21073,21076],{"class":163,"line":408},[290,21074,21075],{"class":303},"  50%",[290,21077,450],{"class":295},[290,21079,21080,21082,21084,21086,21088,21091],{"class":163,"line":428},[290,21081,745],{"class":461},[290,21083,465],{"class":295},[290,21085,493],{"class":461},[290,21087,484],{"class":295},[290,21089,21090],{"class":461},"1.05",[290,21092,500],{"class":295},[290,21094,21095,21097,21099,21101],{"class":163,"line":517},[290,21096,733],{"class":461},[290,21098,465],{"class":295},[290,21100,199],{"class":461},[290,21102,471],{"class":295},[290,21104,21105],{"class":163,"line":523},[290,21106,771],{"class":295},[290,21108,21109,21112],{"class":163,"line":532},[290,21110,21111],{"class":303},"  100%",[290,21113,450],{"class":295},[290,21115,21116,21118,21120,21122,21124,21126],{"class":163,"line":551},[290,21117,745],{"class":461},[290,21119,465],{"class":295},[290,21121,493],{"class":461},[290,21123,484],{"class":295},[290,21125,1119],{"class":461},[290,21127,500],{"class":295},[290,21129,21130,21132,21134,21136],{"class":163,"line":586},[290,21131,733],{"class":461},[290,21133,465],{"class":295},[290,21135,21064],{"class":461},[290,21137,471],{"class":295},[290,21139,21140],{"class":163,"line":602},[290,21141,771],{"class":295},[290,21143,21144],{"class":163,"line":617},[290,21145,620],{"class":295},[290,21147,21148],{"class":163,"line":623},[290,21149,334],{"emptyLinePlaceholder":333},[290,21151,21152,21155],{"class":163,"line":628},[290,21153,21154],{"class":303},".complex-card",[290,21156,450],{"class":295},[290,21158,21159,21161,21163,21165],{"class":163,"line":634},[290,21160,1866],{"class":461},[290,21162,465],{"class":295},[290,21164,1871],{"class":461},[290,21166,471],{"class":295},[290,21168,21169,21171,21173,21175,21177,21179],{"class":163,"line":649},[290,21170,526],{"class":461},[290,21172,1880],{"class":295},[290,21174,5397],{"class":461},[290,21176,542],{"class":541},[290,21178,545],{"class":461},[290,21180,471],{"class":295},[290,21182,21183],{"class":163,"line":660},[290,21184,620],{"class":295},[290,21186,21187],{"class":163,"line":688},[290,21188,334],{"emptyLinePlaceholder":333},[290,21190,21191],{"class":163,"line":693},[290,21192,21193],{"class":455},"\u002F* Trigger keyframe sequence on hover *\u002F\n",[290,21195,21196,21198,21200,21202,21204,21206],{"class":163,"line":698},[290,21197,874],{"class":541},[290,21199,3595],{"class":295},[290,21201,17440],{"class":461},[290,21203,465],{"class":295},[290,21205,17440],{"class":461},[290,21207,646],{"class":295},[290,21209,21210,21213],{"class":163,"line":704},[290,21211,21212],{"class":303},"  .complex-card:hover",[290,21214,450],{"class":295},[290,21216,21217,21219,21221,21223,21225,21227,21229],{"class":163,"line":710},[290,21218,745],{"class":461},[290,21220,465],{"class":295},[290,21222,481],{"class":461},[290,21224,484],{"class":295},[290,21226,14283],{"class":461},[290,21228,674],{"class":541},[290,21230,500],{"class":295},[290,21232,21233],{"class":163,"line":717},[290,21234,771],{"class":295},[290,21236,21237],{"class":163,"line":730},[290,21238,334],{"emptyLinePlaceholder":333},[290,21240,21241,21244],{"class":163,"line":742},[290,21242,21243],{"class":303},"  .complex-card:hover::after",[290,21245,450],{"class":295},[290,21247,21248,21251,21253,21255],{"class":163,"line":768},[290,21249,21250],{"class":461},"    content",[290,21252,465],{"class":295},[290,21254,1916],{"class":310},[290,21256,471],{"class":295},[290,21258,21259,21262,21264,21266],{"class":163,"line":774},[290,21260,21261],{"class":461},"    position",[290,21263,465],{"class":295},[290,21265,1927],{"class":461},[290,21267,471],{"class":295},[290,21269,21270,21273,21275,21277,21279],{"class":163,"line":779},[290,21271,21272],{"class":461},"    inset",[290,21274,465],{"class":295},[290,21276,3189],{"class":461},[290,21278,674],{"class":541},[290,21280,471],{"class":295},[290,21282,21283,21286,21288,21290,21292,21294,21296,21298,21301],{"class":163,"line":784},[290,21284,21285],{"class":461},"    border",[290,21287,465],{"class":295},[290,21289,194],{"class":461},[290,21291,674],{"class":541},[290,21293,852],{"class":461},[290,21295,1635],{"class":461},[290,21297,484],{"class":295},[290,21299,21300],{"class":1561},"--brand-primary",[290,21302,500],{"class":295},[290,21304,21305,21307,21309,21311],{"class":163,"line":812},[290,21306,12759],{"class":461},[290,21308,465],{"class":295},[290,21310,1970],{"class":461},[290,21312,471],{"class":295},[290,21314,21315,21317,21320,21322,21324,21327,21330],{"class":163,"line":860},[290,21316,6152],{"class":461},[290,21318,21319],{"class":295},": pulse-ring ",[290,21321,168],{"class":461},[290,21323,1886],{"class":541},[290,21325,21326],{"class":461}," infinite",[290,21328,21329],{"class":461}," ease-in-out",[290,21331,471],{"class":295},[290,21333,21334,21337,21339,21341],{"class":163,"line":865},[290,21335,21336],{"class":461},"    pointer-events",[290,21338,465],{"class":295},[290,21340,72],{"class":461},[290,21342,471],{"class":295},[290,21344,21345],{"class":163,"line":871},[290,21346,771],{"class":295},[290,21348,21349],{"class":163,"line":880},[290,21350,620],{"class":295},[290,21352,21353],{"class":163,"line":896},[290,21354,334],{"emptyLinePlaceholder":333},[290,21356,21357],{"class":163,"line":4734},[290,21358,21359],{"class":455},"\u002F* Pause animations when focus is lost or reduced motion is preferred *\u002F\n",[290,21361,21362,21364],{"class":163,"line":4742},[290,21363,874],{"class":541},[290,21365,877],{"class":295},[290,21367,21368,21371],{"class":163,"line":4761},[290,21369,21370],{"class":303},"  .complex-card::after",[290,21372,450],{"class":295},[290,21374,21375,21377,21379,21381],{"class":163,"line":4766},[290,21376,6152],{"class":461},[290,21378,465],{"class":295},[290,21380,72],{"class":461},[290,21382,471],{"class":295},[290,21384,21385],{"class":163,"line":4786},[290,21386,771],{"class":295},[290,21388,21389],{"class":163,"line":9635},[290,21390,620],{"class":295},[14,21392,21393,21394,21396,21397,3942,21400,69,21402,21404,21405,21408],{},"For developers seeking deeper sequencing control, exploring ",[27,21395,1420],{"href":1419}," reveals how to synchronize ",[18,21398,21399],{},"animation-play-state",[18,21401,3689],{},[18,21403,1495],{}," for pause\u002Fresume behavior. When JavaScript is strictly necessary for state tracking, refer to ",[27,21406,21407],{"href":18534},"Smooth hover effects without JavaScript"," to maintain declarative, performant fallbacks.",[47,21410],{},[50,21412,21414],{"id":21413},"cross-browser-compatibility-common-pitfalls","Cross-Browser Compatibility & Common Pitfalls",[14,21416,21417],{},[62,21418,21419],{},"Browser Support Matrix:",[1393,21421,21422,21427,21432,21435],{},[1396,21423,21424,21426],{},[18,21425,1495],{},": Chrome 86+, Firefox 88+, Safari 15.4+",[1396,21428,21429,21431],{},[18,21430,20510],{},": Chrome 38+, Firefox 64+, Safari 9+",[1396,21433,21434],{},"CSS Custom Properties: All modern evergreen browsers",[1396,21436,21437,21439,21440,21442,21443,21445,21446,21448],{},[62,21438,5563],{}," IE11 requires ",[18,21441,1532],{}," polyfills and explicit ",[18,21444,1502],{}," declarations. Older Safari versions (\u003C15.4) need ",[18,21447,19315],{}," workarounds.",[14,21450,21451],{},[62,21452,21453],{},"Common Issues & Resolutions:",[2250,21455,21456,21466],{},[2253,21457,21458],{},[2256,21459,21460,21462,21464],{},[2259,21461,2338],{},[2259,21463,3876],{},[2259,21465,8563],{},[2269,21467,21468,21483,21503,21525],{},[2256,21469,21470,21473,21477],{},[2274,21471,21472],{},"Hover triggers on touch",[2274,21474,3930,21475],{},[18,21476,20510],{},[2274,21478,21479,21480,21482],{},"Wrap hover rules in media query; use ",[18,21481,20514],{}," for touch feedback",[2256,21484,21485,21488,21493],{},[2274,21486,21487],{},"Focus ring clipped",[2274,21489,21490,21491],{},"Parent has ",[18,21492,1731],{},[2274,21494,3920,21495,21497,21498,21500,21501],{},[18,21496,793],{},", or use ",[18,21499,1506],{}," inset instead of ",[18,21502,1502],{},[2256,21504,21505,21508,21518],{},[2274,21506,21507],{},"Janky transitions",[2274,21509,16092,21510,569,21512,569,21514,569,21516],{},[18,21511,1748],{},[18,21513,2722],{},[18,21515,2728],{},[18,21517,2731],{},[2274,21519,21520,21521,2351,21523],{},"Switch to ",[18,21522,2071],{},[18,21524,16108],{},[2256,21526,21527,21530,21537],{},[2274,21528,21529],{},"High repaint cost",[2274,21531,21532,21533,2351,21535],{},"Overusing ",[18,21534,1506],{},[18,21536,6792],{},[2274,21538,1499,21539,21541,21542,21544,21545,21547],{},[18,21540,76],{}," + ",[18,21543,103],{}," for depth; limit ",[18,21546,1506],{}," to final state",[47,21549],{},[50,21551,1316],{"id":1315},[14,21553,21554,21557,21558,21561],{},[62,21555,21556],{},"How do I prevent hover states from activating on touch devices?","\nWrap hover-specific styles in a ",[18,21559,21560],{},"@media (hover: hover) and (pointer: fine)"," query. This ensures CSS only applies hover effects to devices with precise pointing inputs, preventing accidental triggers on touchscreens.",[14,21563,21564,2474,21567,21569,21570,21572],{},[62,21565,21566],{},"What is the most accessible way to style focus states?",[18,21568,1495],{}," pseudo-class instead of ",[18,21571,1532],{},". It applies focus indicators only when keyboard navigation is detected, preserving clean UI for mouse users while meeting WCAG 2.2 contrast and visibility requirements.",[14,21574,21575,21578,21579,69,21581,21583,21584,569,21586,569,21588,1745,21590,21592],{},[62,21576,21577],{},"How can I ensure hover transitions don't cause layout shifts?","\nRestrict transitions to compositor-friendly properties like ",[18,21580,103],{},[18,21582,76],{},". Avoid animating ",[18,21585,1748],{},[18,21587,2722],{},[18,21589,2725],{},[18,21591,793],{},", which trigger expensive layout recalculations and degrade performance.",[14,21594,21595,21598,21599,21601],{},[62,21596,21597],{},"Should I use JavaScript for hover and focus states?","\nNo, modern CSS handles these states natively and more efficiently. For advanced sequencing, explore ",[27,21600,21407],{"href":18534}," to maintain declarative, performant code.",[47,21603],{},[50,21605,1391],{"id":1390},[1393,21607,21608,21612,21618,21624,21629],{},[1396,21609,21610,16307],{},[27,21611,1481],{"href":1480},[1396,21613,21614,21617],{},[27,21615,21616],{"href":19548},"Accessible CSS-only tooltips"," — reveal supplementary content on hover and focus without scripts.",[1396,21619,21620,21623],{},[27,21621,21622],{"href":1526},"Focus-visible vs focus polyfill alternatives"," — restore keyboard-only focus rings on older engines.",[1396,21625,21626,21628],{},[27,21627,21407],{"href":18534}," — performant declarative hover transitions.",[1396,21630,21631,21633],{},[27,21632,4037],{"href":4036}," — adapt interactive components to their container, not the viewport.",[1430,21635,21636],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":286,"searchDepth":330,"depth":330,"links":21638},[21639,21642,21645,21648,21652,21653,21654,21655],{"id":19692,"depth":330,"text":19693,"children":21640},[21641],{"id":19699,"depth":337,"text":19700},{"id":20236,"depth":330,"text":20237,"children":21643},[21644],{"id":20250,"depth":337,"text":20251},{"id":20520,"depth":330,"text":20521,"children":21646},[21647],{"id":20537,"depth":337,"text":20538},{"id":1735,"depth":330,"text":1736,"children":21649},[21650,21651],{"id":20772,"depth":337,"text":20773},{"id":8357,"depth":337,"text":13396},{"id":21016,"depth":330,"text":21017},{"id":21413,"depth":330,"text":21414},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Design accessible hover and focus states with CSS: state isolation, transition orchestration, WCAG-compliant indicators, and cross-input-modality patterns.",{"seoTitle":21658,"datePublished":1447,"dateModified":1447,"faq":21659},"Hover & Focus State Design with CSS",[21660,21662,21664,21666],{"q":21556,"a":21661},"Wrap hover-specific styles in a @media (hover: hover) and (pointer: fine) query so CSS only applies hover effects to devices with precise pointing inputs, preventing accidental triggers on touchscreens.",{"q":21566,"a":21663},"Use the :focus-visible pseudo-class instead of :focus. It applies focus indicators only when keyboard navigation is detected, preserving a clean UI for mouse users while meeting WCAG 2.2 contrast and visibility requirements.",{"q":21577,"a":21665},"Restrict transitions to compositor-friendly properties like transform and opacity. Avoid animating width, height, margin, or padding, which trigger expensive layout recalculations and degrade performance.",{"q":21597,"a":21667},"No, modern CSS handles these states natively and more efficiently. Reach for JavaScript only when you must track state that CSS selectors cannot express.","\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design",{"title":19599,"description":21656},"css-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Findex","EtUY5ML-jW2mop8J2EJpSc9mTh8p88LYw17VLwEHLL8",{"id":21673,"title":21674,"body":21675,"description":22463,"extension":1444,"meta":22464,"navigation":333,"path":22477,"seo":22478,"stem":22479,"__hash__":22480},"content\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fsmooth-hover-effects-without-javascript\u002Findex.md","Smooth Hover Effects Without JavaScript: CSS-Only Patterns for Modern UI",{"type":7,"value":21676,"toc":22453},[21677,21680,21693,21698,21720,21724,21741,21748,21789,21909,21962,21966,21975,22074,22098,22107,22111,22114,22185,22210,22221,22225,22231,22278,22280,22291,22295,22368,22370,22376,22387,22402,22422,22424,22450],[10,21678,21674],{"id":21679},"smooth-hover-effects-without-javascript-css-only-patterns-for-modern-ui",[14,21681,21682,21683,21686,21687,21689,21690,21692],{},"Implementing ",[62,21684,21685],{},"smooth hover effects without JavaScript"," requires strict adherence to compositor-friendly rendering paths. This page is part of the ",[27,21688,3345],{"href":3344}," guide, and when architecting broader ",[27,21691,1481],{"href":1480},", prioritizing properties that bypass the main thread ensures consistent 60fps rendering. This page eliminates layout thrashing and guarantees cross-device consistency by focusing on transition architecture, GPU compositing, and robust fallback strategies.",[14,21694,21695],{},[62,21696,21697],{},"Key Takeaways:",[1393,21699,21700,21708,21714,21717],{},[1396,21701,21702,21703,69,21705,21707],{},"Leverage ",[18,21704,103],{},[18,21706,76],{}," for compositor-only animations",[1396,21709,21710,21711,21713],{},"Implement ",[18,21712,2584],{}," media queries for accessibility",[1396,21715,21716],{},"Use CSS custom properties for maintainable easing curves",[1396,21718,21719],{},"Provide graceful fallbacks for non-hover environments",[50,21721,21723],{"id":21722},"the-compositor-first-approach-to-hover-transitions","The Compositor-First Approach to Hover Transitions",[14,21725,21726,21727,569,21729,569,21731,1745,21733,21735,21736,69,21738,21740],{},"Browsers recalculate layout, paint, and composite layers on every frame change. Animating properties like ",[18,21728,1748],{},[18,21730,2722],{},[18,21732,2728],{},[18,21734,2731],{}," forces synchronous reflows, causing visible jank. To guarantee fluidity, restrict transitions to ",[18,21737,103],{},[18,21739,76],{},". These properties are handled exclusively by the compositor thread, allowing the browser to batch GPU instructions without blocking the main thread.",[14,21742,1499,21743,21745,21746,42],{},[18,21744,2075],{}," proactively to promote the element to its own layer before interaction. This eliminates the initial frame drop when the hover state first triggers. Pair this with an exponential or custom cubic-bezier easing curve to mimic physical momentum. The same compositor-first rules apply when a hover reveals content, such as in ",[27,21747,20246],{"href":19548},[133,21749,140,21752,140,21755,140,21758,140,21761,140,21763,140,21766,140,21769,140,21772,140,21775,140,21777,140,21780,140,21783,140,21786],{"viewBox":21750,"role":136,"ariaLabel":21751,"xmlns":138,"style":139},"0 0 720 240","Comparison of a compositor-only hover path versus a main-thread layout path",[142,21753,21754],{},"Compositor path versus layout path on hover",[146,21756,21757],{},"Animating transform and opacity stays on the compositor thread, while animating width or top forces a main-thread layout and paint.",[150,21759,21760],{"x":152,"y":153,"style":1781},"Two ways a hover frame is produced",[171,21762],{"x":158,"y":4172,"width":2613,"height":1787,"rx":836,"fill":177,"opacity":1883,"stroke":167,"strokeWidth":1789},[150,21764,21765],{"x":6669,"y":1793,"style":5883},"Compositor path",[150,21767,21768],{"x":6669,"y":2614,"style":183},"transform \u002F opacity",[150,21770,21771],{"x":6669,"y":4195,"style":5915},"skips layout + paint",[150,21773,21774],{"x":6669,"y":4154,"style":5915},"stays at 60fps",[171,21776],{"x":10266,"y":4172,"width":2613,"height":1787,"rx":836,"fill":72,"stroke":167,"strokeWidth":1789},[150,21778,21779],{"x":10272,"y":1793,"style":5883},"Main-thread path",[150,21781,21782],{"x":10272,"y":2614,"style":183},"width \u002F top \u002F margin",[150,21784,21785],{"x":10272,"y":4195,"style":5915},"forces reflow + repaint",[150,21787,21788],{"x":10272,"y":4154,"style":5915},"visible jank",[281,21790,21792],{"className":438,"code":21791,"language":440,"meta":286,"style":286},"\u002F* Compositor-Optimized Card Hover *\u002F\n.card {\n  transition:\n    transform 0.3s cubic-bezier(0.25, 1, 0.5, 1),\n    opacity 0.3s ease;\n  will-change: transform;\n}\n\n.card:hover {\n  transform: translateY(-4px) scale(1.02);\n  opacity: 0.95;\n}\n",[18,21793,21794,21799,21805,21811,21839,21851,21857,21861,21865,21871,21895,21905],{"__ignoreMap":286},[290,21795,21796],{"class":163,"line":292},[290,21797,21798],{"class":455},"\u002F* Compositor-Optimized Card Hover *\u002F\n",[290,21800,21801,21803],{"class":163,"line":330},[290,21802,11528],{"class":303},[290,21804,450],{"class":295},[290,21806,21807,21809],{"class":163,"line":337},[290,21808,526],{"class":461},[290,21810,529],{"class":295},[290,21812,21813,21815,21817,21819,21821,21823,21825,21827,21829,21831,21833,21835,21837],{"class":163,"line":364},[290,21814,554],{"class":295},[290,21816,199],{"class":461},[290,21818,1886],{"class":541},[290,21820,561],{"class":461},[290,21822,484],{"class":295},[290,21824,1668],{"class":461},[290,21826,569],{"class":295},[290,21828,468],{"class":461},[290,21830,569],{"class":295},[290,21832,798],{"class":461},[290,21834,569],{"class":295},[290,21836,468],{"class":461},[290,21838,583],{"class":295},[290,21840,21841,21843,21845,21847,21849],{"class":163,"line":386},[290,21842,535],{"class":295},[290,21844,199],{"class":461},[290,21846,1886],{"class":541},[290,21848,545],{"class":461},[290,21850,471],{"class":295},[290,21852,21853,21855],{"class":163,"line":408},[290,21854,3518],{"class":461},[290,21856,15503],{"class":295},[290,21858,21859],{"class":163,"line":428},[290,21860,620],{"class":295},[290,21862,21863],{"class":163,"line":517},[290,21864,334],{"emptyLinePlaceholder":333},[290,21866,21867,21869],{"class":163,"line":523},[290,21868,11814],{"class":303},[290,21870,450],{"class":295},[290,21872,21873,21875,21877,21879,21881,21883,21885,21887,21889,21891,21893],{"class":163,"line":532},[290,21874,476],{"class":461},[290,21876,465],{"class":295},[290,21878,481],{"class":461},[290,21880,484],{"class":295},[290,21882,3189],{"class":461},[290,21884,674],{"class":541},[290,21886,490],{"class":295},[290,21888,493],{"class":461},[290,21890,484],{"class":295},[290,21892,2057],{"class":461},[290,21894,500],{"class":295},[290,21896,21897,21899,21901,21903],{"class":163,"line":551},[290,21898,462],{"class":461},[290,21900,465],{"class":295},[290,21902,1119],{"class":461},[290,21904,471],{"class":295},[290,21906,21907],{"class":163,"line":586},[290,21908,620],{"class":295},[281,21910,21912],{"className":283,"code":21911,"language":285,"meta":286,"style":286},"\u003Carticle class=\"card\">\n  \u003Ch3>Component\u003C\u002Fh3>\n  \u003Cp>Hover me for smooth lift.\u003C\u002Fp>\n\u003C\u002Farticle>\n",[18,21913,21914,21928,21941,21954],{"__ignoreMap":286},[290,21915,21916,21918,21920,21922,21924,21926],{"class":163,"line":292},[290,21917,296],{"class":295},[290,21919,11445],{"class":299},[290,21921,314],{"class":303},[290,21923,307],{"class":295},[290,21925,9295],{"class":310},[290,21927,327],{"class":295},[290,21929,21930,21932,21934,21937,21939],{"class":163,"line":330},[290,21931,367],{"class":295},[290,21933,2757],{"class":299},[290,21935,21936],{"class":295},">Component\u003C\u002F",[290,21938,2757],{"class":299},[290,21940,327],{"class":295},[290,21942,21943,21945,21947,21950,21952],{"class":163,"line":337},[290,21944,367],{"class":295},[290,21946,14],{"class":299},[290,21948,21949],{"class":295},">Hover me for smooth lift.\u003C\u002F",[290,21951,14],{"class":299},[290,21953,327],{"class":295},[290,21955,21956,21958,21960],{"class":163,"line":364},[290,21957,431],{"class":295},[290,21959,11445],{"class":299},[290,21961,327],{"class":295},[50,21963,21965],{"id":21964},"custom-properties-easing-architecture","Custom Properties & Easing Architecture",[14,21967,21968,21969,21971,21972,21974],{},"Hardcoding timing values creates maintenance debt. Centralize your easing and duration values at the ",[18,21970,1554],{}," level to establish a predictable design system. This architecture scales efficiently within any ",[27,21973,3345],{"href":3344}," system, enabling centralized easing curves and component-specific overrides without duplicating logic.",[281,21976,21978],{"className":438,"code":21977,"language":440,"meta":286,"style":286},"\u002F* Systematic Easing Variables *\u002F\n:root {\n  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);\n  --duration-fast: 200ms;\n}\n\n.btn {\n  transition: background-color var(--duration-fast) var(--ease-out-expo);\n}\n",[18,21979,21980,21985,21991,22019,22032,22036,22040,22046,22070],{"__ignoreMap":286},[290,21981,21982],{"class":163,"line":292},[290,21983,21984],{"class":455},"\u002F* Systematic Easing Variables *\u002F\n",[290,21986,21987,21989],{"class":163,"line":330},[290,21988,1554],{"class":303},[290,21990,450],{"class":295},[290,21992,21993,21996,21998,22000,22002,22005,22007,22009,22011,22013,22015,22017],{"class":163,"line":337},[290,21994,21995],{"class":1561},"  --ease-out-expo",[290,21997,465],{"class":295},[290,21999,11767],{"class":461},[290,22001,484],{"class":295},[290,22003,22004],{"class":461},"0.16",[290,22006,569],{"class":295},[290,22008,468],{"class":461},[290,22010,569],{"class":295},[290,22012,199],{"class":461},[290,22014,569],{"class":295},[290,22016,468],{"class":461},[290,22018,500],{"class":295},[290,22020,22021,22024,22026,22028,22030],{"class":163,"line":364},[290,22022,22023],{"class":1561},"  --duration-fast",[290,22025,465],{"class":295},[290,22027,174],{"class":461},[290,22029,542],{"class":541},[290,22031,471],{"class":295},[290,22033,22034],{"class":163,"line":386},[290,22035,620],{"class":295},[290,22037,22038],{"class":163,"line":408},[290,22039,334],{"emptyLinePlaceholder":333},[290,22041,22042,22044],{"class":163,"line":428},[290,22043,15250],{"class":303},[290,22045,450],{"class":295},[290,22047,22048,22050,22052,22054,22056,22059,22061,22063,22065,22068],{"class":163,"line":517},[290,22049,526],{"class":461},[290,22051,4670],{"class":295},[290,22053,1622],{"class":461},[290,22055,484],{"class":295},[290,22057,22058],{"class":1561},"--duration-fast",[290,22060,490],{"class":295},[290,22062,1622],{"class":461},[290,22064,484],{"class":295},[290,22066,22067],{"class":1561},"--ease-out-expo",[290,22069,500],{"class":295},[290,22071,22072],{"class":163,"line":523},[290,22073,620],{"class":295},[281,22075,22077],{"className":283,"code":22076,"language":285,"meta":286,"style":286},"\u003Cbutton class=\"btn\">Submit\u003C\u002Fbutton>\n",[18,22078,22079],{"__ignoreMap":286},[290,22080,22081,22083,22085,22087,22089,22091,22094,22096],{"class":163,"line":292},[290,22082,296],{"class":295},[290,22084,300],{"class":299},[290,22086,314],{"class":303},[290,22088,307],{"class":295},[290,22090,18945],{"class":310},[290,22092,22093],{"class":295},">Submit\u003C\u002F",[290,22095,300],{"class":299},[290,22097,327],{"class":295},[14,22099,22100,22103,22104,22106],{},[62,22101,22102],{},"Debugging Transition Timing:"," Open DevTools → Rendering → Enable \"Paint Flashing\" and \"Layer Borders\". If a hover triggers a green paint rectangle, you're animating a layout\u002Fpaint property. Switch to ",[18,22105,103],{}," immediately.",[50,22108,22110],{"id":22109},"touch-reduced-motion-fallbacks","Touch & Reduced-Motion Fallbacks",[14,22112,22113],{},"Pure CSS hover states fail gracefully on touch devices and violate accessibility standards if forced universally. Wrap interactive transitions in modern media queries to respect hardware capabilities and user preferences.",[281,22115,22117],{"className":438,"code":22116,"language":440,"meta":286,"style":286},"\u002F* Accessibility & Touch Fallback *\u002F\n@media (hover: hover) and (prefers-reduced-motion: no-preference) {\n  .interactive:hover {\n    transform: scale(1.05);\n    transition: transform 0.25s ease;\n  }\n}\n",[18,22118,22119,22124,22142,22149,22163,22177,22181],{"__ignoreMap":286},[290,22120,22121],{"class":163,"line":292},[290,22122,22123],{"class":455},"\u002F* Accessibility & Touch Fallback *\u002F\n",[290,22125,22126,22128,22130,22132,22134,22136,22138,22140],{"class":163,"line":330},[290,22127,874],{"class":541},[290,22129,3595],{"class":295},[290,22131,17440],{"class":461},[290,22133,465],{"class":295},[290,22135,17440],{"class":461},[290,22137,490],{"class":295},[290,22139,11985],{"class":541},[290,22141,16018],{"class":295},[290,22143,22144,22147],{"class":163,"line":337},[290,22145,22146],{"class":303},"  .interactive:hover",[290,22148,450],{"class":295},[290,22150,22151,22153,22155,22157,22159,22161],{"class":163,"line":364},[290,22152,745],{"class":461},[290,22154,465],{"class":295},[290,22156,493],{"class":461},[290,22158,484],{"class":295},[290,22160,21090],{"class":461},[290,22162,500],{"class":295},[290,22164,22165,22167,22169,22171,22173,22175],{"class":163,"line":386},[290,22166,4745],{"class":461},[290,22168,1880],{"class":295},[290,22170,1668],{"class":461},[290,22172,1886],{"class":541},[290,22174,545],{"class":461},[290,22176,471],{"class":295},[290,22178,22179],{"class":163,"line":408},[290,22180,771],{"class":295},[290,22182,22183],{"class":163,"line":428},[290,22184,620],{"class":295},[281,22186,22188],{"className":283,"code":22187,"language":285,"meta":286,"style":286},"\u003Cdiv class=\"interactive\">Safe Hover\u003C\u002Fdiv>\n",[18,22189,22190],{"__ignoreMap":286},[290,22191,22192,22194,22196,22198,22200,22203,22206,22208],{"class":163,"line":292},[290,22193,296],{"class":295},[290,22195,342],{"class":299},[290,22197,314],{"class":303},[290,22199,307],{"class":295},[290,22201,22202],{"class":310},"\"interactive\"",[290,22204,22205],{"class":295},">Safe Hover\u003C\u002F",[290,22207,342],{"class":299},[290,22209,327],{"class":295},[14,22211,22212,22213,22217,22218,42],{},"Always maintain focus parity. Keyboard users rely on ",[27,22214,22215],{"href":1526},[18,22216,1495],{}," for navigation feedback. Chain selectors to ensure identical visual states: ",[18,22219,22220],{},".el:hover, .el:focus-visible { ... }",[50,22222,22224],{"id":22223},"troubleshooting-jank-stutter","Troubleshooting Jank & Stutter",[14,22226,22227,22228,22230],{},"Even with ",[18,22229,3797],{},", performance degrades if misapplied. Follow these debugging steps:",[3017,22232,22233,22242,22259],{},[1396,22234,22235,22238,22239,22241],{},[62,22236,22237],{},"Layer Promotion Limits:"," Browsers cap GPU layers (~10-20 depending on viewport). Overusing ",[18,22240,3797],{}," on every element causes memory bloat. Apply it only to interactive elements entering the viewport.",[1396,22243,22244,22247,22248,22250,22251,22253,22254,69,22256,22258],{},[62,22245,22246],{},"Box-Shadow Anti-Patterns:"," Animating ",[18,22249,1506],{}," triggers expensive paint operations. Instead, use a pseudo-element (",[18,22252,2412],{},") with ",[18,22255,2071],{},[18,22257,76],{}," to simulate shadow expansion.",[1396,22260,22261,22264,22265,2351,22268,22271,22272,22274,22275,22277],{},[62,22262,22263],{},"Safe Hardware Acceleration:"," Avoid legacy ",[18,22266,22267],{},"translateZ(0)",[18,22269,22270],{},"transform: translate3d(0,0,0)"," hacks. They force compositing but can cause text rendering artifacts and increased memory consumption. Modern browsers auto-promote layers on ",[18,22273,887],{}," declaration. Use ",[18,22276,3797],{}," as a targeted hint, not a blanket fix.",[50,22279,2248],{"id":2247},[14,22281,22282,22283,69,22285,22287,22288,22290],{},"Full support in all evergreen browsers (Chrome 84+, Firefox 78+, Safari 13.1+, Edge 84+). ",[18,22284,20510],{},[18,22286,2584],{}," require modern browsers; legacy fallbacks default to instant state changes. IE11 lacks ",[18,22289,3797],{}," and reduced-motion support; graceful degradation is recommended.",[50,22292,22294],{"id":22293},"common-issues-direct-fixes","Common Issues & Direct Fixes",[2250,22296,22297,22305],{},[2253,22298,22299],{},[2256,22300,22301,22303],{},[2259,22302,2338],{},[2259,22304,2341],{},[2269,22306,22307,22320,22346,22357],{},[2256,22308,22309,22312],{},[2274,22310,22311],{},"Hover state sticks on mobile after tap",[2274,22313,22314,22315,2401,22317,22319],{},"Wrap hover rules in ",[18,22316,20510],{},[18,22318,20514],{}," for immediate tap feedback.",[2256,22321,22322,22327],{},[2274,22323,22324,22325],{},"Janky animation despite ",[18,22326,887],{},[2274,22328,22329,22330,3041,22332,22334,22335,569,22337,569,22339,1745,22341,22343,22344,42],{},"Verify only ",[18,22331,103],{},[18,22333,76],{}," are animated. Remove ",[18,22336,1506],{},[18,22338,1748],{},[18,22340,2722],{},[18,22342,2725],{}," transitions. Add ",[18,22345,2075],{},[2256,22347,22348,22351],{},[2274,22349,22350],{},"Focus states missing keyboard parity",[2274,22352,22353,22354,22356],{},"Chain selectors: ",[18,22355,22220],{}," to ensure identical visual feedback for mouse and keyboard.",[2256,22358,22359,22362],{},[2274,22360,22361],{},"Reduced-motion preference ignored",[2274,22363,21710,22364,22367],{},[18,22365,22366],{},"@media (prefers-reduced-motion: reduce) { .el { transition: none; } }"," at the end of the cascade to override all animations.",[50,22369,1316],{"id":1315},[14,22371,22372,22375],{},[62,22373,22374],{},"Can CSS-only hovers replace JavaScript event listeners for UI feedback?","\nYes, for state-based visual feedback (color, scale, opacity, position). CSS transitions are declarative, run on the compositor thread, and avoid main-thread blocking. Use JS only when hover state requires data fetching, complex sequencing, or DOM manipulation.",[14,22377,22378,22383,22384,22386],{},[62,22379,6487,22380,22382],{},[18,22381,3797],{}," still necessary for modern browsers?","\nIt is optional but recommended for hover-heavy interfaces. Modern browsers auto-promote layers on transition, but ",[18,22385,2075],{}," acts as a proactive hint to allocate GPU memory before the first interaction, eliminating the initial frame drop.",[14,22388,22389,22392,22393,22395,22396,22398,22399,22401],{},[62,22390,22391],{},"How do I prevent hover animations from triggering during rapid mouse movement?","\nCSS transitions inherently debounce rapid state changes by respecting the ",[18,22394,4873],{},". Avoid using ",[18,22397,3968],{}," for hover; stick to ",[18,22400,887],{},". If stutter persists, ensure the element isn't being reflowed by parent layout shifts.",[14,22403,22404,22412,22413,22415,22416,22418,22419,22421],{},[62,22405,2432,22406,2351,22409,22411],{},[18,22407,22408],{},"transform3d",[18,22410,22267],{}," for hardware acceleration?","\nAvoid legacy ",[18,22414,22267],{}," hacks. Use ",[18,22417,2075],{}," or simply rely on modern browser auto-compositing. ",[18,22420,22408],{}," is only necessary if you explicitly need 3D perspective contexts.",[50,22423,1391],{"id":1390},[1393,22425,22426,22431,22436,22445],{},[1396,22427,22428,22430],{},[27,22429,3345],{"href":3344}," — the parent guide for interactive state styling.",[1396,22432,22433,22435],{},[27,22434,19549],{"href":19548}," — apply these transition patterns to hover-revealed content.",[1396,22437,22438,22444],{},[27,22439,22440,1529,22442],{"href":1526},[18,22441,1495],{},[18,22443,1532],{}," — keep keyboard parity with the right modality selector.",[1396,22446,22447,22449],{},[27,22448,5778],{"href":5777}," — the responsive-layout area, where hover effects adapt per component size.",[1430,22451,22452],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":286,"searchDepth":330,"depth":330,"links":22454},[22455,22456,22457,22458,22459,22460,22461,22462],{"id":21722,"depth":330,"text":21723},{"id":21964,"depth":330,"text":21965},{"id":22109,"depth":330,"text":22110},{"id":22223,"depth":330,"text":22224},{"id":2247,"depth":330,"text":2248},{"id":22293,"depth":330,"text":22294},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Build smooth CSS hover effects without JavaScript: compositor-friendly transitions, 60fps rendering, layout-thrashing prevention, and cross-device consistency.",{"seoTitle":22465,"datePublished":1447,"dateModified":1447,"faq":22466},"Smooth CSS Hover Effects Without JavaScript",[22467,22469,22472,22474],{"q":22374,"a":22468},"Yes, for state-based visual feedback like color, scale, opacity, and position. CSS transitions are declarative, run on the compositor thread, and avoid main-thread blocking. Use JS only when hover requires data fetching, complex sequencing, or DOM manipulation.",{"q":22470,"a":22471},"Is will-change still necessary for modern browsers?","It is optional but recommended for hover-heavy interfaces. Modern browsers auto-promote layers on transition, but will-change: transform acts as a proactive hint to allocate GPU memory before the first interaction, eliminating the initial frame drop.",{"q":22391,"a":22473},"CSS transitions inherently debounce rapid state changes by respecting transition-duration. Avoid keyframes for hover and stick to transition. If stutter persists, ensure the element is not being reflowed by parent layout shifts.",{"q":22475,"a":22476},"Should I use transform3d or translateZ(0) for hardware acceleration?","Avoid legacy translateZ(0) hacks. Use will-change: transform or rely on modern browser auto-compositing. transform3d is only needed when you explicitly require 3D perspective contexts.","\u002Fcss-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fsmooth-hover-effects-without-javascript",{"title":21674,"description":22463},"css-only-micro-interactions-animations\u002Fhover-focus-state-design\u002Fsmooth-hover-effects-without-javascript\u002Findex","A-coSLWA8jM8cFlY7Sk78ihyx0Y_qsh5TJAJWp8A9_k",{"id":22482,"title":22483,"body":22484,"description":25288,"extension":1444,"meta":25289,"navigation":333,"path":25306,"seo":25307,"stem":25308,"__hash__":25309},"content\u002Fcss-only-micro-interactions-animations\u002Findex.md","CSS-Only Micro-Interactions & Animations: Architecture, Performance & Implementation",{"type":7,"value":22485,"toc":25261},[22486,22489,22499,22510,22513,22543,22614,22616,22620,22623,22639,22666,22683,22685,22695,22705,22711,22846,22872,22880,23110,23125,23256,23258,23262,23276,23536,23539,23541,23545,23556,23905,23911,23913,23917,23934,24163,24172,24174,24178,24181,24187,24202,24310,24316,24321,24374,24378,24387,24519,24525,24537,24539,24543,24546,24797,24802,24804,24808,24943,24963,24965,24969,25116,25118,25120,25126,25155,25166,25175,25192,25209,25211,25213,25258],[10,22487,22483],{"id":22488},"css-only-micro-interactions-animations-architecture-performance-implementation",[14,22490,22491,22492,22494,22495,22498],{},"Interface motion is no longer a decorative afterthought layered on with a JavaScript library. The modern browser ships a complete, hardware-accelerated motion engine that runs declaratively in the stylesheet, survives script failure, and is cheap enough to deploy across an entire design system. This guide for ",[62,22493,1481],{}," establishes the mental model and the architectural patterns needed to ship motion that stays at 60fps, degrades gracefully, and honours user preferences by default. It is the companion to the ",[27,22496,22497],{"href":5777},"container queries and responsive layouts guide"," — together they cover how a component adapts its shape and how it moves.",[14,22500,22501,22502,2351,22504,22506,22507,42],{},"CSS-only motion wins over scripted motion in the common case for three concrete reasons. It is declarative, so the browser owns scheduling and can run qualifying animations off the main thread. It is resilient, because a ",[18,22503,3689],{},[18,22505,8708],{}," transition still works when a script throws or has not yet loaded. And it is composable, because timing, easing, and duration live in custom properties that the whole system inherits. JavaScript and the Web Animations API earn their place only when you need runtime-computed values, fine playback control, or sequencing that no CSS state can express — a decision examined in the guide to ",[27,22508,22509],{"href":8822},"CSS animation versus the Web Animations API",[14,22511,22512],{},"By the end of this guide you will understand:",[1393,22514,22515,22518,22530,22533],{},[1396,22516,22517],{},"How the browser splits motion work between the main thread and the compositor thread, and why that split dictates which properties you may animate.",[1396,22519,22520,22521,569,22523,22525,22526,22529],{},"The canonical syntax for ",[18,22522,887],{},[18,22524,3968],{},", and the ",[18,22527,22528],{},"animation-*"," family, token by token.",[1396,22531,22532],{},"Three reusable architectural patterns — state-driven transitions, a token-driven motion scale, and orchestrated keyframe sequences — each as a complete, annotated block.",[1396,22534,22535,22536,569,22538,22540,22541,42],{},"How motion integrates with custom properties, ",[18,22537,10151],{},[18,22539,12681],{},", container and style queries, and ",[18,22542,2584],{},[133,22544,140,22546,140,22549,140,22552,140,22555,140,22557,140,22560,140,22562,140,22567,140,22569,140,22573,140,22576,140,22579,140,22582,140,22585,140,22588,140,22591,140,22595,140,22597,140,22600,140,22603,140,22606,140,22609],{"viewBox":6614,"role":136,"ariaLabel":22545,"xmlns":138,"style":139},"How the browser routes a CSS animation through the main thread and the compositor thread",[142,22547,22548],{},"The CSS motion pipeline: main thread versus compositor thread",[146,22550,22551],{},"Animating transform and opacity stays on the compositor thread and runs at 60fps, while animating width, height, or top forces layout and paint back onto the main thread.",[150,22553,22554],{"x":152,"y":2598,"style":154},"Where a CSS animation runs",[171,22556],{"x":158,"y":4172,"width":6740,"height":5893,"rx":5894,"fill":72,"stroke":167,"strokeWidth":168,"opacity":6041},[150,22558,22559],{"x":2602,"y":159,"style":10244},"Main thread (expensive)",[171,22561],{"x":1788,"y":4180,"width":2637,"height":4146,"rx":5901,"fill":177,"opacity":178},[150,22563,22566],{"x":2602,"y":22564,"style":22565},"129","text-anchor:middle;fill:currentColor;font:12px sans-serif;opacity:0.9","Style → Layout → Paint",[171,22568],{"x":1788,"y":8929,"width":2637,"height":4146,"rx":5901,"fill":72,"stroke":167,"strokeWidth":1789,"opacity":798},[150,22570,22572],{"x":2602,"y":22571,"style":12455},"183","width \u002F height \u002F top",[150,22574,22575],{"x":2602,"y":5904,"style":10252},"re-runs every frame",[150,22577,22578],{"x":2602,"y":19916,"style":10252},"competes with scripts",[150,22580,22581],{"x":2602,"y":5397,"style":2606},"risk: dropped frames",[171,22583],{"x":22584,"y":4172,"width":6740,"height":5893,"rx":5894,"fill":72,"stroke":177,"strokeWidth":194},"384",[150,22586,22587],{"x":5887,"y":159,"style":10244},"Compositor thread (cheap)",[171,22589],{"x":22590,"y":4180,"width":2637,"height":4146,"rx":5901,"fill":177,"opacity":4173},"416",[150,22592,22594],{"x":5887,"y":22564,"style":22593},"text-anchor:middle;fill:currentColor;font:12px sans-serif;opacity:0.95","Composite layers only",[171,22596],{"x":22590,"y":8929,"width":2637,"height":4146,"rx":5901,"fill":72,"stroke":177,"strokeWidth":1789},[150,22598,21768],{"x":5887,"y":22571,"style":22599},"text-anchor:middle;fill:currentColor;font:12px ui-monospace,monospace;opacity:0.95",[150,22601,22602],{"x":5887,"y":5904,"style":10252},"no layout, no paint",[150,22604,22605],{"x":5887,"y":19916,"style":10252},"runs while JS is busy",[150,22607,22608],{"x":5887,"y":5397,"style":2606},"steady 60fps",[150,22610,22613],{"x":152,"y":22611,"style":22612},"343","text-anchor:middle;fill:currentColor;font:11px sans-serif;opacity:0.6","Pick the right column and most jank disappears before you profile.",[47,22615],{},[50,22617,22619],{"id":22618},"the-core-mental-model-two-threads-one-frame","The Core Mental Model: Two Threads, One Frame",[14,22621,22622],{},"Every smooth interface decision flows from one fact about how browsers render: motion can run on the main thread or on the compositor thread, and only one of those keeps up under load. The main thread is where the browser parses CSS, recalculates styles, runs layout, paints pixels, and executes your JavaScript. The compositor thread runs in parallel on the GPU and does one job — it takes already-painted layers and stitches them into a frame, optionally transformed and faded.",[14,22624,22625,22626,569,22628,569,22630,569,22632,569,22634,569,22636,22638],{},"When you animate a property that changes an element's geometry — ",[18,22627,1748],{},[18,22629,2722],{},[18,22631,2728],{},[18,22633,2731],{},[18,22635,2725],{},[18,22637,793],{}," — the browser must re-run layout for that element and often its siblings, then repaint the affected region, on every single frame. All of that happens on the main thread, in direct competition with any script that is running. When the main thread is busy parsing JSON or hydrating a component, the animation simply skips frames.",[14,22640,16092,22641,69,22643,22645,22646,22654,22655,22657,22658,22662,22663,42],{},[18,22642,103],{},[18,22644,76],{}," is fundamentally different. If the element sits on its own compositor layer, the browser paints it once, hands the layer to the compositor, and from then on each frame is just a matrix multiply and an alpha blend on the GPU. No layout, no paint, no main-thread involvement. This is why the single most important rule of CSS motion is: ",[62,22647,22648,22649,69,22651,22653],{},"animate ",[18,22650,103],{},[18,22652,76],{},", not box-model properties."," The deeper mechanics of layer promotion and the ",[18,22656,3797],{}," hint are covered in the ",[27,22659,22661],{"href":22660},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002F","performance and GPU acceleration guide",", with a focused treatment of ",[27,22664,22665],{"href":8756},"60fps optimisation",[14,22667,22668,22669,22671,22672,569,22674,22676,22677,22679,22680,22682],{},"A layer is created when an element is promoted — by an active ",[18,22670,103],{}," animation, ",[18,22673,2075],{},[18,22675,76],{}," below 1 with compositing, certain ",[18,22678,6792],{}," values, or an explicit ",[18,22681,22267],{},". Promotion costs GPU memory, so it is a budget, not a free win. The art is promoting exactly the elements that animate, for exactly as long as they animate, and no more.",[47,22684],{},[50,22686,22688,22689,569,22691,8393,22693],{"id":22687},"syntax-reference-transition-keyframes-and-animation","Syntax Reference: ",[18,22690,887],{},[18,22692,3968],{},[18,22694,22528],{},[14,22696,22697,22698,22700,22701,22704],{},"CSS exposes two motion primitives. A ",[62,22699,887],{}," interpolates a property when its value changes in response to a state. A ",[62,22702,22703],{},"keyframe animation"," plays an authored timeline that can loop, run on load, and pass through intermediate stops. They share an easing and timing vocabulary.",[2757,22706,1517,22708,22710],{"id":22707},"the-transition-shorthand",[18,22709,887],{}," shorthand",[281,22712,22714],{"className":438,"code":22713,"language":440,"meta":286,"style":286},".target {\n  \u002F* property | duration | timing-function | delay *\u002F\n  transition: transform 240ms cubic-bezier(0.16, 1, 0.3, 1) 0ms;\n\n  \u002F* Or expanded, which is clearer for multiple properties *\u002F\n  transition-property: transform, opacity;   \u002F* never leave this as `all` *\u002F\n  transition-duration: 240ms;                 \u002F* time per property *\u002F\n  transition-timing-function: ease-out;       \u002F* the easing curve *\u002F\n  transition-delay: 0ms;                       \u002F* wait before starting *\u002F\n  transition-behavior: allow-discrete;         \u002F* opt discrete props in *\u002F\n}\n",[18,22715,22716,22723,22728,22764,22768,22773,22784,22801,22815,22831,22842],{"__ignoreMap":286},[290,22717,22718,22721],{"class":163,"line":292},[290,22719,22720],{"class":303},".target",[290,22722,450],{"class":295},[290,22724,22725],{"class":163,"line":330},[290,22726,22727],{"class":455},"  \u002F* property | duration | timing-function | delay *\u002F\n",[290,22729,22730,22732,22734,22736,22738,22740,22742,22744,22746,22748,22750,22752,22754,22756,22758,22760,22762],{"class":163,"line":337},[290,22731,526],{"class":461},[290,22733,1880],{"class":295},[290,22735,8947],{"class":461},[290,22737,542],{"class":541},[290,22739,561],{"class":461},[290,22741,484],{"class":295},[290,22743,22004],{"class":461},[290,22745,569],{"class":295},[290,22747,468],{"class":461},[290,22749,569],{"class":295},[290,22751,199],{"class":461},[290,22753,569],{"class":295},[290,22755,468],{"class":461},[290,22757,490],{"class":295},[290,22759,487],{"class":461},[290,22761,542],{"class":541},[290,22763,471],{"class":295},[290,22765,22766],{"class":163,"line":364},[290,22767,334],{"emptyLinePlaceholder":333},[290,22769,22770],{"class":163,"line":386},[290,22771,22772],{"class":455},"  \u002F* Or expanded, which is clearer for multiple properties *\u002F\n",[290,22774,22775,22778,22781],{"class":163,"line":408},[290,22776,22777],{"class":461},"  transition-property",[290,22779,22780],{"class":295},": transform, opacity;   ",[290,22782,22783],{"class":455},"\u002F* never leave this as `all` *\u002F\n",[290,22785,22786,22789,22791,22793,22795,22798],{"class":163,"line":428},[290,22787,22788],{"class":461},"  transition-duration",[290,22790,465],{"class":295},[290,22792,8947],{"class":461},[290,22794,542],{"class":541},[290,22796,22797],{"class":295},";                 ",[290,22799,22800],{"class":455},"\u002F* time per property *\u002F\n",[290,22802,22803,22805,22807,22809,22812],{"class":163,"line":517},[290,22804,14437],{"class":461},[290,22806,465],{"class":295},[290,22808,5411],{"class":461},[290,22810,22811],{"class":295},";       ",[290,22813,22814],{"class":455},"\u002F* the easing curve *\u002F\n",[290,22816,22817,22819,22821,22823,22825,22828],{"class":163,"line":523},[290,22818,18246],{"class":461},[290,22820,465],{"class":295},[290,22822,487],{"class":461},[290,22824,542],{"class":541},[290,22826,22827],{"class":295},";                       ",[290,22829,22830],{"class":455},"\u002F* wait before starting *\u002F\n",[290,22832,22833,22836,22839],{"class":163,"line":532},[290,22834,22835],{"class":461},"  transition-behavior",[290,22837,22838],{"class":295},": allow-discrete;         ",[290,22840,22841],{"class":455},"\u002F* opt discrete props in *\u002F\n",[290,22843,22844],{"class":163,"line":551},[290,22845,620],{"class":295},[14,22847,22848,22849,22851,22852,22854,22855,569,22857,569,22859,22861,22862,22864,22865,22868,22869,42],{},"Listing ",[18,22850,15034],{}," explicitly matters: the default of ",[18,22853,15067],{}," makes the browser watch every animatable property for change, which interpolates things you never intended and wastes recalculation. The full grammar of curves — ",[18,22856,6770],{},[18,22858,5456],{},[18,22860,6773],{},", and the underrated ",[18,22863,3971],{}," — is unpacked in ",[27,22866,22867],{"href":29},"CSS transition fundamentals"," and its companion on ",[27,22870,22871],{"href":1405},"transition timing functions",[2757,22873,22875,6815,22877,22879],{"id":22874},"keyframes-and-the-animation-family",[18,22876,3968],{},[18,22878,22528],{}," family",[281,22881,22883],{"className":438,"code":22882,"language":440,"meta":286,"style":286},"@keyframes rise {\n  from { opacity: 0; transform: translateY(8px); }\n  to   { opacity: 1; transform: translateY(0); }\n}\n\n.entrance {\n  \u002F* name | duration | easing | delay | count | direction | fill *\u002F\n  animation: rise 320ms ease-out 0ms 1 normal both;\n\n  \u002F* Expanded form, one line per dimension of control *\u002F\n  animation-name: rise;\n  animation-duration: 320ms;\n  animation-timing-function: ease-out;\n  animation-delay: 0ms;\n  animation-iteration-count: 1;     \u002F* a number, or `infinite` *\u002F\n  animation-direction: normal;       \u002F* normal | reverse | alternate *\u002F\n  animation-fill-mode: both;         \u002F* hold first\u002Flast frame values *\u002F\n  animation-play-state: running;     \u002F* running | paused *\u002F\n}\n",[18,22884,22885,22893,22921,22947,22951,22955,22962,22967,22993,22997,23002,23010,23023,23034,23047,23062,23076,23091,23106],{"__ignoreMap":286},[290,22886,22887,22889,22891],{"class":163,"line":292},[290,22888,3968],{"class":541},[290,22890,7195],{"class":1561},[290,22892,450],{"class":295},[290,22894,22895,22897,22899,22901,22903,22905,22907,22909,22911,22913,22915,22917,22919],{"class":163,"line":330},[290,22896,6191],{"class":303},[290,22898,790],{"class":295},[290,22900,76],{"class":461},[290,22902,465],{"class":295},[290,22904,487],{"class":461},[290,22906,828],{"class":295},[290,22908,103],{"class":461},[290,22910,465],{"class":295},[290,22912,481],{"class":461},[290,22914,484],{"class":295},[290,22916,176],{"class":461},[290,22918,674],{"class":541},[290,22920,1122],{"class":295},[290,22922,22923,22925,22927,22929,22931,22933,22935,22937,22939,22941,22943,22945],{"class":163,"line":337},[290,22924,6072],{"class":303},[290,22926,6208],{"class":295},[290,22928,76],{"class":461},[290,22930,465],{"class":295},[290,22932,468],{"class":461},[290,22934,828],{"class":295},[290,22936,103],{"class":461},[290,22938,465],{"class":295},[290,22940,481],{"class":461},[290,22942,484],{"class":295},[290,22944,487],{"class":461},[290,22946,1122],{"class":295},[290,22948,22949],{"class":163,"line":364},[290,22950,620],{"class":295},[290,22952,22953],{"class":163,"line":386},[290,22954,334],{"emptyLinePlaceholder":333},[290,22956,22957,22960],{"class":163,"line":408},[290,22958,22959],{"class":303},".entrance",[290,22961,450],{"class":295},[290,22963,22964],{"class":163,"line":428},[290,22965,22966],{"class":455},"  \u002F* name | duration | easing | delay | count | direction | fill *\u002F\n",[290,22968,22969,22971,22973,22975,22977,22979,22981,22983,22985,22988,22991],{"class":163,"line":517},[290,22970,6035],{"class":461},[290,22972,7152],{"class":295},[290,22974,2613],{"class":461},[290,22976,542],{"class":541},[290,22978,1889],{"class":461},[290,22980,1198],{"class":461},[290,22982,542],{"class":541},[290,22984,804],{"class":461},[290,22986,22987],{"class":461}," normal",[290,22989,22990],{"class":461}," both",[290,22992,471],{"class":295},[290,22994,22995],{"class":163,"line":523},[290,22996,334],{"emptyLinePlaceholder":333},[290,22998,22999],{"class":163,"line":532},[290,23000,23001],{"class":455},"  \u002F* Expanded form, one line per dimension of control *\u002F\n",[290,23003,23004,23007],{"class":163,"line":551},[290,23005,23006],{"class":461},"  animation-name",[290,23008,23009],{"class":295},": rise;\n",[290,23011,23012,23015,23017,23019,23021],{"class":163,"line":586},[290,23013,23014],{"class":461},"  animation-duration",[290,23016,465],{"class":295},[290,23018,2613],{"class":461},[290,23020,542],{"class":541},[290,23022,471],{"class":295},[290,23024,23025,23028,23030,23032],{"class":163,"line":602},[290,23026,23027],{"class":461},"  animation-timing-function",[290,23029,465],{"class":295},[290,23031,5411],{"class":461},[290,23033,471],{"class":295},[290,23035,23036,23039,23041,23043,23045],{"class":163,"line":617},[290,23037,23038],{"class":461},"  animation-delay",[290,23040,465],{"class":295},[290,23042,487],{"class":461},[290,23044,542],{"class":541},[290,23046,471],{"class":295},[290,23048,23049,23052,23054,23056,23059],{"class":163,"line":623},[290,23050,23051],{"class":461},"  animation-iteration-count",[290,23053,465],{"class":295},[290,23055,468],{"class":461},[290,23057,23058],{"class":295},";     ",[290,23060,23061],{"class":455},"\u002F* a number, or `infinite` *\u002F\n",[290,23063,23064,23067,23069,23071,23073],{"class":163,"line":628},[290,23065,23066],{"class":461},"  animation-direction",[290,23068,465],{"class":295},[290,23070,7032],{"class":461},[290,23072,22811],{"class":295},[290,23074,23075],{"class":455},"\u002F* normal | reverse | alternate *\u002F\n",[290,23077,23078,23081,23083,23085,23088],{"class":163,"line":634},[290,23079,23080],{"class":461},"  animation-fill-mode",[290,23082,465],{"class":295},[290,23084,7069],{"class":461},[290,23086,23087],{"class":295},";         ",[290,23089,23090],{"class":455},"\u002F* hold first\u002Flast frame values *\u002F\n",[290,23092,23093,23096,23098,23101,23103],{"class":163,"line":649},[290,23094,23095],{"class":461},"  animation-play-state",[290,23097,465],{"class":295},[290,23099,23100],{"class":461},"running",[290,23102,23058],{"class":295},[290,23104,23105],{"class":455},"\u002F* running | paused *\u002F\n",[290,23107,23108],{"class":163,"line":660},[290,23109,620],{"class":295},[14,23111,23112,23113,23115,23116,23118,23119,23121,23122,23124],{},"The token most often misunderstood is ",[18,23114,6779],{},". Without it, an element snaps back to its un-animated style the instant the timeline ends. ",[18,23117,7063],{}," holds the final keyframe, ",[18,23120,7066],{}," applies the first keyframe during any delay, and ",[18,23123,7069],{}," does both — almost always what you want for an entrance that should stay put.",[2250,23126,23127,23138],{},[2253,23128,23129],{},[2256,23130,23131,23134,23136],{},[2259,23132,23133],{},"Token",[2259,23135,6882],{},[2259,23137,6885],{},[2269,23139,23140,23160,23184,23200,23220,23240],{},[2256,23141,23142,23146,23156],{},[2274,23143,23144],{},[18,23145,4950],{},[2274,23147,23148,6924,23150,569,23153,3914],{},[18,23149,6923],{},[18,23151,23152],{},"320ms",[18,23154,23155],{},"0.32s",[2274,23157,23158],{},[18,23159,6935],{},[2256,23161,23162,23166,23180],{},[2274,23163,23164],{},[18,23165,6952],{},[2274,23167,23168,23170,23171,23170,23173,23170,23176,23170,23178],{},[18,23169,6770],{}," · ",[18,23172,5456],{},[18,23174,23175],{},"ease-in\u002Fout",[18,23177,6773],{},[18,23179,3971],{},[2274,23181,23182],{},[18,23183,6770],{},[2256,23185,23186,23190,23196],{},[2274,23187,23188],{},[18,23189,7000],{},[2274,23191,23192,23170,23194],{},[18,23193,7005],{},[18,23195,7008],{},[2274,23197,23198],{},[18,23199,468],{},[2256,23201,23202,23206,23216],{},[2274,23203,23204],{},[18,23205,7027],{},[2274,23207,23208,23170,23210,23170,23212,23170,23214],{},[18,23209,7032],{},[18,23211,7035],{},[18,23213,7038],{},[18,23215,7041],{},[2274,23217,23218],{},[18,23219,7032],{},[2256,23221,23222,23226,23236],{},[2274,23223,23224],{},[18,23225,6779],{},[2274,23227,23228,23170,23230,23170,23232,23170,23234],{},[18,23229,72],{},[18,23231,7063],{},[18,23233,7066],{},[18,23235,7069],{},[2274,23237,23238],{},[18,23239,72],{},[2256,23241,23242,23246,23252],{},[2274,23243,23244],{},[18,23245,939],{},[2274,23247,23248,23170,23250],{},[18,23249,7032],{},[18,23251,916],{},[2274,23253,23254],{},[18,23255,7032],{},[47,23257],{},[50,23259,23261],{"id":23260},"architectural-pattern-1-state-driven-transitions","Architectural Pattern 1: State-Driven Transitions",[14,23263,23264,23265,3942,23267,23269,23270,23273,23274,42],{},"The first pattern is the backbone of CSS-only interactivity: bind motion to a native pseudo-class so the browser, not a script, owns the state. No event listeners, no class toggling, and the feedback survives a JavaScript failure entirely. Pair ",[18,23266,3689],{},[18,23268,1495],{}," so keyboard users receive identical feedback, and neutralise hover on coarse pointers so a tap does not strand a card in its hovered state. State-driven design across input modalities is the subject of the ",[27,23271,23272],{"href":3344},"hover and focus state design guide"," and its walkthrough of ",[27,23275,21685],{"href":18534},[281,23277,23279],{"className":438,"code":23278,"language":440,"meta":286,"style":286},".card {\n  \u002F* Only compositor-friendly properties are listed — never `all`. *\u002F\n  transition:\n    transform 240ms var(--ease-out, ease-out),\n    box-shadow 240ms var(--ease-out, ease-out);\n  transform: translateY(0);\n  box-shadow: 0 1px 3px rgb(0 0 0 \u002F 0.12);\n}\n\n\u002F* Hover and keyboard focus share one rule, guaranteeing parity. *\u002F\n.card:hover,\n.card:focus-visible {\n  transform: translateY(-4px);\n  box-shadow: 0 10px 24px rgb(0 0 0 \u002F 0.18);\n}\n\n\u002F* On touch devices :hover can stick after a tap — opt out cleanly. *\u002F\n@media (hover: none) and (pointer: coarse) {\n  .card:hover { transform: none; box-shadow: 0 1px 3px rgb(0 0 0 \u002F 0.12); }\n}\n",[18,23280,23281,23287,23292,23298,23319,23339,23353,23385,23389,23393,23398,23404,23410,23426,23458,23462,23466,23471,23487,23532],{"__ignoreMap":286},[290,23282,23283,23285],{"class":163,"line":292},[290,23284,11528],{"class":303},[290,23286,450],{"class":295},[290,23288,23289],{"class":163,"line":330},[290,23290,23291],{"class":455},"  \u002F* Only compositor-friendly properties are listed — never `all`. *\u002F\n",[290,23293,23294,23296],{"class":163,"line":337},[290,23295,526],{"class":461},[290,23297,529],{"class":295},[290,23299,23300,23302,23304,23306,23308,23310,23313,23315,23317],{"class":163,"line":364},[290,23301,554],{"class":295},[290,23303,8947],{"class":461},[290,23305,542],{"class":541},[290,23307,1635],{"class":461},[290,23309,484],{"class":295},[290,23311,23312],{"class":1561},"--ease-out",[290,23314,569],{"class":295},[290,23316,5411],{"class":461},[290,23318,583],{"class":295},[290,23320,23321,23323,23325,23327,23329,23331,23333,23335,23337],{"class":163,"line":386},[290,23322,3140],{"class":295},[290,23324,8947],{"class":461},[290,23326,542],{"class":541},[290,23328,1635],{"class":461},[290,23330,484],{"class":295},[290,23332,23312],{"class":1561},[290,23334,569],{"class":295},[290,23336,5411],{"class":461},[290,23338,500],{"class":295},[290,23340,23341,23343,23345,23347,23349,23351],{"class":163,"line":408},[290,23342,476],{"class":461},[290,23344,465],{"class":295},[290,23346,481],{"class":461},[290,23348,484],{"class":295},[290,23350,487],{"class":461},[290,23352,500],{"class":295},[290,23354,23355,23357,23359,23361,23363,23365,23367,23369,23371,23373,23375,23377,23379,23381,23383],{"class":163,"line":428},[290,23356,3207],{"class":461},[290,23358,465],{"class":295},[290,23360,487],{"class":461},[290,23362,804],{"class":461},[290,23364,674],{"class":541},[290,23366,19770],{"class":461},[290,23368,674],{"class":541},[290,23370,11872],{"class":461},[290,23372,484],{"class":295},[290,23374,487],{"class":461},[290,23376,1198],{"class":461},[290,23378,1198],{"class":461},[290,23380,1203],{"class":295},[290,23382,3241],{"class":461},[290,23384,500],{"class":295},[290,23386,23387],{"class":163,"line":517},[290,23388,620],{"class":295},[290,23390,23391],{"class":163,"line":523},[290,23392,334],{"emptyLinePlaceholder":333},[290,23394,23395],{"class":163,"line":532},[290,23396,23397],{"class":455},"\u002F* Hover and keyboard focus share one rule, guaranteeing parity. *\u002F\n",[290,23399,23400,23402],{"class":163,"line":551},[290,23401,11814],{"class":303},[290,23403,548],{"class":295},[290,23405,23406,23408],{"class":163,"line":586},[290,23407,14263],{"class":303},[290,23409,450],{"class":295},[290,23411,23412,23414,23416,23418,23420,23422,23424],{"class":163,"line":602},[290,23413,476],{"class":461},[290,23415,465],{"class":295},[290,23417,481],{"class":461},[290,23419,484],{"class":295},[290,23421,3189],{"class":461},[290,23423,674],{"class":541},[290,23425,500],{"class":295},[290,23427,23428,23430,23432,23434,23436,23438,23440,23442,23444,23446,23448,23450,23452,23454,23456],{"class":163,"line":617},[290,23429,3207],{"class":461},[290,23431,465],{"class":295},[290,23433,487],{"class":461},[290,23435,11862],{"class":461},[290,23437,674],{"class":541},[290,23439,3219],{"class":461},[290,23441,674],{"class":541},[290,23443,11872],{"class":461},[290,23445,484],{"class":295},[290,23447,487],{"class":461},[290,23449,1198],{"class":461},[290,23451,1198],{"class":461},[290,23453,1203],{"class":295},[290,23455,178],{"class":461},[290,23457,500],{"class":295},[290,23459,23460],{"class":163,"line":623},[290,23461,620],{"class":295},[290,23463,23464],{"class":163,"line":628},[290,23465,334],{"emptyLinePlaceholder":333},[290,23467,23468],{"class":163,"line":634},[290,23469,23470],{"class":455},"\u002F* On touch devices :hover can stick after a tap — opt out cleanly. *\u002F\n",[290,23472,23473,23475,23477,23479,23482,23484],{"class":163,"line":649},[290,23474,874],{"class":541},[290,23476,3595],{"class":295},[290,23478,17440],{"class":461},[290,23480,23481],{"class":295},": none) ",[290,23483,11985],{"class":541},[290,23485,23486],{"class":295}," (pointer: coarse) {\n",[290,23488,23489,23492,23494,23496,23498,23500,23502,23504,23506,23508,23510,23512,23514,23516,23518,23520,23522,23524,23526,23528,23530],{"class":163,"line":660},[290,23490,23491],{"class":303},"  .card:hover",[290,23493,790],{"class":295},[290,23495,103],{"class":461},[290,23497,465],{"class":295},[290,23499,72],{"class":461},[290,23501,828],{"class":295},[290,23503,1506],{"class":461},[290,23505,465],{"class":295},[290,23507,487],{"class":461},[290,23509,804],{"class":461},[290,23511,674],{"class":541},[290,23513,19770],{"class":461},[290,23515,674],{"class":541},[290,23517,11872],{"class":461},[290,23519,484],{"class":295},[290,23521,487],{"class":461},[290,23523,1198],{"class":461},[290,23525,1198],{"class":461},[290,23527,1203],{"class":295},[290,23529,3241],{"class":461},[290,23531,1122],{"class":295},[290,23533,23534],{"class":163,"line":688},[290,23535,620],{"class":295},[14,23537,23538],{},"The state lives in the DOM, so the transition is reversible for free: when the pointer leaves or focus moves on, the same curve plays in reverse back to the base value. This bidirectionality is something scripted toggles must re-implement by hand.",[47,23540],{},[50,23542,23544],{"id":23543},"architectural-pattern-2-token-driven-motion-scale","Architectural Pattern 2: Token-Driven Motion Scale",[14,23546,23547,23548,23551,23552,23555],{},"A design system that lets every component invent its own durations and curves drifts into incoherence. The remedy is to centralise motion in custom properties — a single source of truth for duration, easing, and distance — and have components consume the tokens rather than literal values. This is the same discipline that governs spacing and colour, applied to time, and it is detailed in the ",[27,23549,23550],{"href":10147},"CSS custom properties architecture guide",". Because a token is just a value, a duration token can even be derived from the fluid spacing scale described in the ",[27,23553,23554],{"href":11312},"container queries pillar's fluid typography guide",", tying how far an element travels to how big it is.",[281,23557,23559],{"className":438,"code":23558,"language":440,"meta":286,"style":286},":root {\n  \u002F* One motion language for the whole system. *\u002F\n  --motion-fast: 150ms;\n  --motion-base: 240ms;\n  --motion-slow: 400ms;\n\n  --ease-standard: cubic-bezier(0.2, 0, 0, 1);\n  --ease-emphasized: cubic-bezier(0.16, 1, 0.3, 1);\n\n  --lift-sm: -2px;\n  --lift-md: -4px;\n}\n\n.button {\n  transition: transform var(--motion-fast) var(--ease-standard);\n}\n.button:hover,\n.button:focus-visible { transform: translateY(var(--lift-sm)); }\n\n.panel {\n  transition: transform var(--motion-base) var(--ease-emphasized);\n}\n.panel[data-open] { transform: translateY(var(--lift-md)); }\n\n\u002F* A single global switch dials the entire system to near-instant. *\u002F\n@media (prefers-reduced-motion: reduce) {\n  :root {\n    --motion-fast: 1ms;\n    --motion-base: 1ms;\n    --motion-slow: 1ms;\n  }\n}\n",[18,23560,23561,23567,23572,23585,23598,23611,23615,23641,23668,23672,23685,23698,23702,23706,23713,23736,23740,23747,23771,23775,23781,23805,23809,23837,23841,23846,23852,23858,23871,23884,23897,23901],{"__ignoreMap":286},[290,23562,23563,23565],{"class":163,"line":292},[290,23564,1554],{"class":303},[290,23566,450],{"class":295},[290,23568,23569],{"class":163,"line":330},[290,23570,23571],{"class":455},"  \u002F* One motion language for the whole system. *\u002F\n",[290,23573,23574,23577,23579,23581,23583],{"class":163,"line":337},[290,23575,23576],{"class":1561},"  --motion-fast",[290,23578,465],{"class":295},[290,23580,1787],{"class":461},[290,23582,542],{"class":541},[290,23584,471],{"class":295},[290,23586,23587,23590,23592,23594,23596],{"class":163,"line":364},[290,23588,23589],{"class":1561},"  --motion-base",[290,23591,465],{"class":295},[290,23593,8947],{"class":461},[290,23595,542],{"class":541},[290,23597,471],{"class":295},[290,23599,23600,23603,23605,23607,23609],{"class":163,"line":386},[290,23601,23602],{"class":1561},"  --motion-slow",[290,23604,465],{"class":295},[290,23606,2618],{"class":461},[290,23608,542],{"class":541},[290,23610,471],{"class":295},[290,23612,23613],{"class":163,"line":408},[290,23614,334],{"emptyLinePlaceholder":333},[290,23616,23617,23619,23621,23623,23625,23627,23629,23631,23633,23635,23637,23639],{"class":163,"line":428},[290,23618,13944],{"class":1561},[290,23620,465],{"class":295},[290,23622,11767],{"class":461},[290,23624,484],{"class":295},[290,23626,566],{"class":461},[290,23628,569],{"class":295},[290,23630,487],{"class":461},[290,23632,569],{"class":295},[290,23634,487],{"class":461},[290,23636,569],{"class":295},[290,23638,468],{"class":461},[290,23640,500],{"class":295},[290,23642,23643,23646,23648,23650,23652,23654,23656,23658,23660,23662,23664,23666],{"class":163,"line":517},[290,23644,23645],{"class":1561},"  --ease-emphasized",[290,23647,465],{"class":295},[290,23649,11767],{"class":461},[290,23651,484],{"class":295},[290,23653,22004],{"class":461},[290,23655,569],{"class":295},[290,23657,468],{"class":461},[290,23659,569],{"class":295},[290,23661,199],{"class":461},[290,23663,569],{"class":295},[290,23665,468],{"class":461},[290,23667,500],{"class":295},[290,23669,23670],{"class":163,"line":523},[290,23671,334],{"emptyLinePlaceholder":333},[290,23673,23674,23677,23679,23681,23683],{"class":163,"line":532},[290,23675,23676],{"class":1561},"  --lift-sm",[290,23678,465],{"class":295},[290,23680,3561],{"class":461},[290,23682,674],{"class":541},[290,23684,471],{"class":295},[290,23686,23687,23690,23692,23694,23696],{"class":163,"line":551},[290,23688,23689],{"class":1561},"  --lift-md",[290,23691,465],{"class":295},[290,23693,3189],{"class":461},[290,23695,674],{"class":541},[290,23697,471],{"class":295},[290,23699,23700],{"class":163,"line":586},[290,23701,620],{"class":295},[290,23703,23704],{"class":163,"line":602},[290,23705,334],{"emptyLinePlaceholder":333},[290,23707,23708,23711],{"class":163,"line":617},[290,23709,23710],{"class":303},".button",[290,23712,450],{"class":295},[290,23714,23715,23717,23719,23721,23723,23726,23728,23730,23732,23734],{"class":163,"line":623},[290,23716,526],{"class":461},[290,23718,1880],{"class":295},[290,23720,1622],{"class":461},[290,23722,484],{"class":295},[290,23724,23725],{"class":1561},"--motion-fast",[290,23727,490],{"class":295},[290,23729,1622],{"class":461},[290,23731,484],{"class":295},[290,23733,14242],{"class":1561},[290,23735,500],{"class":295},[290,23737,23738],{"class":163,"line":628},[290,23739,620],{"class":295},[290,23741,23742,23745],{"class":163,"line":634},[290,23743,23744],{"class":303},".button:hover",[290,23746,548],{"class":295},[290,23748,23749,23752,23754,23756,23758,23760,23762,23764,23766,23769],{"class":163,"line":649},[290,23750,23751],{"class":303},".button:focus-visible",[290,23753,790],{"class":295},[290,23755,103],{"class":461},[290,23757,465],{"class":295},[290,23759,481],{"class":461},[290,23761,484],{"class":295},[290,23763,1622],{"class":461},[290,23765,484],{"class":295},[290,23767,23768],{"class":1561},"--lift-sm",[290,23770,4894],{"class":295},[290,23772,23773],{"class":163,"line":660},[290,23774,334],{"emptyLinePlaceholder":333},[290,23776,23777,23779],{"class":163,"line":688},[290,23778,6369],{"class":303},[290,23780,450],{"class":295},[290,23782,23783,23785,23787,23789,23791,23794,23796,23798,23800,23803],{"class":163,"line":693},[290,23784,526],{"class":461},[290,23786,1880],{"class":295},[290,23788,1622],{"class":461},[290,23790,484],{"class":295},[290,23792,23793],{"class":1561},"--motion-base",[290,23795,490],{"class":295},[290,23797,1622],{"class":461},[290,23799,484],{"class":295},[290,23801,23802],{"class":1561},"--ease-emphasized",[290,23804,500],{"class":295},[290,23806,23807],{"class":163,"line":698},[290,23808,620],{"class":295},[290,23810,23811,23813,23815,23818,23820,23822,23824,23826,23828,23830,23832,23835],{"class":163,"line":704},[290,23812,6369],{"class":303},[290,23814,1140],{"class":295},[290,23816,23817],{"class":303},"data-open",[290,23819,1145],{"class":295},[290,23821,103],{"class":461},[290,23823,465],{"class":295},[290,23825,481],{"class":461},[290,23827,484],{"class":295},[290,23829,1622],{"class":461},[290,23831,484],{"class":295},[290,23833,23834],{"class":1561},"--lift-md",[290,23836,4894],{"class":295},[290,23838,23839],{"class":163,"line":710},[290,23840,334],{"emptyLinePlaceholder":333},[290,23842,23843],{"class":163,"line":717},[290,23844,23845],{"class":455},"\u002F* A single global switch dials the entire system to near-instant. *\u002F\n",[290,23847,23848,23850],{"class":163,"line":730},[290,23849,874],{"class":541},[290,23851,877],{"class":295},[290,23853,23854,23856],{"class":163,"line":742},[290,23855,3430],{"class":303},[290,23857,450],{"class":295},[290,23859,23860,23863,23865,23867,23869],{"class":163,"line":768},[290,23861,23862],{"class":1561},"    --motion-fast",[290,23864,465],{"class":295},[290,23866,468],{"class":461},[290,23868,542],{"class":541},[290,23870,471],{"class":295},[290,23872,23873,23876,23878,23880,23882],{"class":163,"line":774},[290,23874,23875],{"class":1561},"    --motion-base",[290,23877,465],{"class":295},[290,23879,468],{"class":461},[290,23881,542],{"class":541},[290,23883,471],{"class":295},[290,23885,23886,23889,23891,23893,23895],{"class":163,"line":779},[290,23887,23888],{"class":1561},"    --motion-slow",[290,23890,465],{"class":295},[290,23892,468],{"class":461},[290,23894,542],{"class":541},[290,23896,471],{"class":295},[290,23898,23899],{"class":163,"line":784},[290,23900,771],{"class":295},[290,23902,23903],{"class":163,"line":812},[290,23904,620],{"class":295},[14,23906,23907,23908,23910],{},"The reduced-motion override is the quiet payoff: because every component reads the duration tokens, redefining them once at the ",[18,23909,1554],{}," calms the entire interface without touching a single component rule.",[47,23912],{},[50,23914,23916],{"id":23915},"architectural-pattern-3-orchestrated-keyframe-sequences","Architectural Pattern 3: Orchestrated Keyframe Sequences",[14,23918,23919,23920,23922,23923,23925,23926,23929,23930,42],{},"When motion is multi-step or self-running — a loader, a skeleton shimmer, a staggered list reveal — transitions are not enough and authored keyframes take over. The orchestration technique is to keep one shared ",[18,23921,3968],{}," definition and vary only the per-item ",[18,23924,6980],{}," through a custom property, so the cascade does the staggering. The full taxonomy of these patterns lives in the ",[27,23927,23928],{"href":1419},"keyframe animation patterns guide",", alongside dedicated builds such as ",[27,23931,23933],{"href":23932},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-toggle-switches-and-checkboxes\u002F","CSS-only toggle switches and checkboxes",[281,23935,23937],{"className":438,"code":23936,"language":440,"meta":286,"style":286},"@keyframes rise-in {\n  from { opacity: 0; transform: translateY(12px); }\n  to   { opacity: 1; transform: translateY(0); }\n}\n\n.stagger > * {\n  \u002F* `both` holds the start frame during the delay and the end frame after. *\u002F\n  animation: rise-in var(--motion-base, 240ms) var(--ease-emphasized, ease-out) both;\n  \u002F* Each item reads its index from an inline custom property. *\u002F\n  animation-delay: calc(var(--i, 0) * 60ms);\n}\n\n\u002F* In markup: \u003Cli style=\"--i:0\">…\u003C\u002Fli> \u003Cli style=\"--i:1\">…\u003C\u002Fli> … *\u002F\n\n\u002F* Honour reduced motion by collapsing the stagger to a single frame. *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .stagger > * { animation-duration: 1ms; animation-delay: 0ms; }\n}\n",[18,23938,23939,23948,23976,24002,24006,24010,24022,24027,24064,24069,24100,24104,24108,24113,24117,24122,24128,24159],{"__ignoreMap":286},[290,23940,23941,23943,23946],{"class":163,"line":292},[290,23942,3968],{"class":541},[290,23944,23945],{"class":1561}," rise-in",[290,23947,450],{"class":295},[290,23949,23950,23952,23954,23956,23958,23960,23962,23964,23966,23968,23970,23972,23974],{"class":163,"line":330},[290,23951,6191],{"class":303},[290,23953,790],{"class":295},[290,23955,76],{"class":461},[290,23957,465],{"class":295},[290,23959,487],{"class":461},[290,23961,828],{"class":295},[290,23963,103],{"class":461},[290,23965,465],{"class":295},[290,23967,481],{"class":461},[290,23969,484],{"class":295},[290,23971,5894],{"class":461},[290,23973,674],{"class":541},[290,23975,1122],{"class":295},[290,23977,23978,23980,23982,23984,23986,23988,23990,23992,23994,23996,23998,24000],{"class":163,"line":337},[290,23979,6072],{"class":303},[290,23981,6208],{"class":295},[290,23983,76],{"class":461},[290,23985,465],{"class":295},[290,23987,468],{"class":461},[290,23989,828],{"class":295},[290,23991,103],{"class":461},[290,23993,465],{"class":295},[290,23995,481],{"class":461},[290,23997,484],{"class":295},[290,23999,487],{"class":461},[290,24001,1122],{"class":295},[290,24003,24004],{"class":163,"line":364},[290,24005,620],{"class":295},[290,24007,24008],{"class":163,"line":386},[290,24009,334],{"emptyLinePlaceholder":333},[290,24011,24012,24015,24018,24020],{"class":163,"line":408},[290,24013,24014],{"class":303},".stagger",[290,24016,24017],{"class":541}," >",[290,24019,3566],{"class":299},[290,24021,450],{"class":295},[290,24023,24024],{"class":163,"line":428},[290,24025,24026],{"class":455},"  \u002F* `both` holds the start frame during the delay and the end frame after. *\u002F\n",[290,24028,24029,24031,24034,24036,24038,24040,24042,24044,24046,24048,24050,24052,24054,24056,24058,24060,24062],{"class":163,"line":517},[290,24030,6035],{"class":461},[290,24032,24033],{"class":295},": rise-in ",[290,24035,1622],{"class":461},[290,24037,484],{"class":295},[290,24039,23793],{"class":1561},[290,24041,569],{"class":295},[290,24043,8947],{"class":461},[290,24045,542],{"class":541},[290,24047,490],{"class":295},[290,24049,1622],{"class":461},[290,24051,484],{"class":295},[290,24053,23802],{"class":1561},[290,24055,569],{"class":295},[290,24057,5411],{"class":461},[290,24059,490],{"class":295},[290,24061,7069],{"class":461},[290,24063,471],{"class":295},[290,24065,24066],{"class":163,"line":523},[290,24067,24068],{"class":455},"  \u002F* Each item reads its index from an inline custom property. *\u002F\n",[290,24070,24071,24073,24075,24077,24079,24081,24083,24086,24088,24090,24092,24094,24096,24098],{"class":163,"line":532},[290,24072,23038],{"class":461},[290,24074,465],{"class":295},[290,24076,3556],{"class":461},[290,24078,484],{"class":295},[290,24080,1622],{"class":461},[290,24082,484],{"class":295},[290,24084,24085],{"class":1561},"--i",[290,24087,569],{"class":295},[290,24089,487],{"class":461},[290,24091,490],{"class":295},[290,24093,4415],{"class":541},[290,24095,11021],{"class":461},[290,24097,542],{"class":541},[290,24099,500],{"class":295},[290,24101,24102],{"class":163,"line":551},[290,24103,620],{"class":295},[290,24105,24106],{"class":163,"line":586},[290,24107,334],{"emptyLinePlaceholder":333},[290,24109,24110],{"class":163,"line":602},[290,24111,24112],{"class":455},"\u002F* In markup: \u003Cli style=\"--i:0\">…\u003C\u002Fli> \u003Cli style=\"--i:1\">…\u003C\u002Fli> … *\u002F\n",[290,24114,24115],{"class":163,"line":617},[290,24116,334],{"emptyLinePlaceholder":333},[290,24118,24119],{"class":163,"line":623},[290,24120,24121],{"class":455},"\u002F* Honour reduced motion by collapsing the stagger to a single frame. *\u002F\n",[290,24123,24124,24126],{"class":163,"line":628},[290,24125,874],{"class":541},[290,24127,877],{"class":295},[290,24129,24130,24133,24135,24137,24139,24141,24143,24145,24147,24149,24151,24153,24155,24157],{"class":163,"line":634},[290,24131,24132],{"class":303},"  .stagger",[290,24134,24017],{"class":541},[290,24136,3566],{"class":299},[290,24138,790],{"class":295},[290,24140,4950],{"class":461},[290,24142,465],{"class":295},[290,24144,468],{"class":461},[290,24146,542],{"class":541},[290,24148,828],{"class":295},[290,24150,6980],{"class":461},[290,24152,465],{"class":295},[290,24154,487],{"class":461},[290,24156,542],{"class":541},[290,24158,809],{"class":295},[290,24160,24161],{"class":163,"line":649},[290,24162,620],{"class":295},[14,24164,24165,24166,24168,24169,42],{},"Because the index is just a number on each element, the same sequence scales from three items to thirty with no extra rules — the ",[18,24167,3693],{}," does the arithmetic the cascade would otherwise force you to write by hand with ",[18,24170,24171],{},":nth-child()",[47,24173],{},[50,24175,24177],{"id":24176},"integration-with-adjacent-css","Integration with Adjacent CSS",[14,24179,24180],{},"Motion rarely lives alone. Its real power emerges when it composes with the rest of the modern stylesheet.",[2757,24182,24184,24185],{"id":24183},"custom-properties-and-property","Custom properties and ",[18,24186,10151],{},[14,24188,24189,24190,24192,24193,80,24195,24198,24199,24201],{},"A plain custom property is an untyped string, so the browser cannot interpolate it — animating ",[18,24191,10302],{}," from ",[18,24194,10800],{},[18,24196,24197],{},"180deg"," snaps instantly. Registering it with ",[18,24200,10151],{}," gives it a type, an initial value, and inheritance rules, which unlocks smooth interpolation of gradients, angles, and colours that previously demanded JavaScript.",[281,24203,24205],{"className":438,"code":24204,"language":440,"meta":286,"style":286},"@property --angle {\n  syntax: \"\u003Cangle>\";\n  initial-value: 0deg;\n  inherits: false;\n}\n\n.swatch {\n  background: linear-gradient(var(--angle), #3b82f6, #8b5cf6);\n  transition: --angle 600ms ease;\n}\n.swatch:hover { --angle: 180deg; }\n",[18,24206,24207,24213,24221,24227,24231,24235,24239,24246,24275,24289,24293],{"__ignoreMap":286},[290,24208,24209,24211],{"class":163,"line":292},[290,24210,10151],{"class":541},[290,24212,10356],{"class":295},[290,24214,24215,24217,24219],{"class":163,"line":330},[290,24216,10361],{"class":295},[290,24218,10364],{"class":541},[290,24220,10890],{"class":295},[290,24222,24223,24225],{"class":163,"line":337},[290,24224,10383],{"class":299},[290,24226,10932],{"class":295},[290,24228,24229],{"class":163,"line":364},[290,24230,10895],{"class":295},[290,24232,24233],{"class":163,"line":386},[290,24234,620],{"class":295},[290,24236,24237],{"class":163,"line":408},[290,24238,334],{"emptyLinePlaceholder":333},[290,24240,24241,24244],{"class":163,"line":428},[290,24242,24243],{"class":303},".swatch",[290,24245,450],{"class":295},[290,24247,24248,24250,24252,24254,24256,24258,24260,24262,24265,24268,24270,24273],{"class":163,"line":517},[290,24249,1186],{"class":461},[290,24251,465],{"class":295},[290,24253,13130],{"class":461},[290,24255,484],{"class":295},[290,24257,1622],{"class":461},[290,24259,484],{"class":295},[290,24261,10302],{"class":1561},[290,24263,24264],{"class":295},"), ",[290,24266,24267],{"class":461},"#3b82f6",[290,24269,569],{"class":295},[290,24271,24272],{"class":461},"#8b5cf6",[290,24274,500],{"class":295},[290,24276,24277,24279,24281,24283,24285,24287],{"class":163,"line":523},[290,24278,526],{"class":461},[290,24280,10503],{"class":295},[290,24282,4176],{"class":461},[290,24284,542],{"class":541},[290,24286,545],{"class":461},[290,24288,471],{"class":295},[290,24290,24291],{"class":163,"line":532},[290,24292,620],{"class":295},[290,24294,24295,24298,24300,24302,24304,24306,24308],{"class":163,"line":551},[290,24296,24297],{"class":303},".swatch:hover",[290,24299,790],{"class":295},[290,24301,10302],{"class":1561},[290,24303,465],{"class":295},[290,24305,2602],{"class":461},[290,24307,10416],{"class":541},[290,24309,809],{"class":295},[2757,24311,24313,24315],{"id":24312},"layer-for-predictable-overrides",[18,24314,12681],{}," for predictable overrides",[14,24317,24318,24319,42],{},"Place motion rules in a named cascade layer so a third-party component library cannot silently win a specificity battle and break a transition. A layer keeps your motion authoritative without resorting to ",[18,24320,3900],{},[281,24322,24324],{"className":438,"code":24323,"language":440,"meta":286,"style":286},"@layer base, components, motion;\n\n@layer motion {\n  .card { transition: transform var(--motion-base) var(--ease-standard); }\n}\n",[18,24325,24326,24333,24337,24344,24370],{"__ignoreMap":286},[290,24327,24328,24330],{"class":163,"line":292},[290,24329,12681],{"class":541},[290,24331,24332],{"class":295}," base, components, motion;\n",[290,24334,24335],{"class":163,"line":330},[290,24336,334],{"emptyLinePlaceholder":333},[290,24338,24339,24341],{"class":163,"line":337},[290,24340,12681],{"class":541},[290,24342,24343],{"class":295}," motion {\n",[290,24345,24346,24348,24350,24352,24354,24356,24358,24360,24362,24364,24366,24368],{"class":163,"line":364},[290,24347,9083],{"class":303},[290,24349,790],{"class":295},[290,24351,887],{"class":461},[290,24353,1880],{"class":295},[290,24355,1622],{"class":461},[290,24357,484],{"class":295},[290,24359,23793],{"class":1561},[290,24361,490],{"class":295},[290,24363,1622],{"class":461},[290,24365,484],{"class":295},[290,24367,14242],{"class":1561},[290,24369,1122],{"class":295},[290,24371,24372],{"class":163,"line":386},[290,24373,620],{"class":295},[2757,24375,24377],{"id":24376},"container-and-style-queries","Container and style queries",[14,24379,24380,24381,24383,24384,24386],{},"A component can change ",[86,24382,10826],{}," it moves based on the space it occupies, not the viewport — the bridge between this guide and the ",[27,24385,22497],{"href":5777},". A style query can also gate an animation on a custom property's computed value, letting a state token start or stop motion with no script.",[281,24388,24390],{"className":438,"code":24389,"language":440,"meta":286,"style":286},".media { container-type: inline-size; }\n\n@container (min-width: 480px) {\n  .media__caption {\n    transition: transform var(--motion-base) var(--ease-emphasized);\n  }\n  .media:hover .media__caption { transform: translateY(-6px); }\n}\n\n\u002F* Start a loop only when a token says the component is busy. *\u002F\n@container style(--state: loading) {\n  .media__spinner { animation: spin 800ms linear infinite; }\n}\n",[18,24391,24392,24405,24409,24416,24423,24445,24449,24473,24477,24481,24486,24493,24515],{"__ignoreMap":286},[290,24393,24394,24397,24399,24402],{"class":163,"line":292},[290,24395,24396],{"class":303},".media",[290,24398,790],{"class":295},[290,24400,24401],{"class":461},"container-type",[290,24403,24404],{"class":295},": inline-size; }\n",[290,24406,24407],{"class":163,"line":330},[290,24408,334],{"emptyLinePlaceholder":333},[290,24410,24411,24413],{"class":163,"line":337},[290,24412,12001],{"class":541},[290,24414,24415],{"class":295}," (min-width: 480px) {\n",[290,24417,24418,24421],{"class":163,"line":364},[290,24419,24420],{"class":303},"  .media__caption",[290,24422,450],{"class":295},[290,24424,24425,24427,24429,24431,24433,24435,24437,24439,24441,24443],{"class":163,"line":386},[290,24426,4745],{"class":461},[290,24428,1880],{"class":295},[290,24430,1622],{"class":461},[290,24432,484],{"class":295},[290,24434,23793],{"class":1561},[290,24436,490],{"class":295},[290,24438,1622],{"class":461},[290,24440,484],{"class":295},[290,24442,23802],{"class":1561},[290,24444,500],{"class":295},[290,24446,24447],{"class":163,"line":408},[290,24448,771],{"class":295},[290,24450,24451,24454,24457,24459,24461,24463,24465,24467,24469,24471],{"class":163,"line":428},[290,24452,24453],{"class":303},"  .media:hover",[290,24455,24456],{"class":303}," .media__caption",[290,24458,790],{"class":295},[290,24460,103],{"class":461},[290,24462,465],{"class":295},[290,24464,481],{"class":461},[290,24466,484],{"class":295},[290,24468,14283],{"class":461},[290,24470,674],{"class":541},[290,24472,1122],{"class":295},[290,24474,24475],{"class":163,"line":517},[290,24476,620],{"class":295},[290,24478,24479],{"class":163,"line":523},[290,24480,334],{"emptyLinePlaceholder":333},[290,24482,24483],{"class":163,"line":532},[290,24484,24485],{"class":455},"\u002F* Start a loop only when a token says the component is busy. *\u002F\n",[290,24487,24488,24490],{"class":163,"line":551},[290,24489,12001],{"class":541},[290,24491,24492],{"class":295}," style(--state: loading) {\n",[290,24494,24495,24498,24500,24502,24505,24507,24509,24511,24513],{"class":163,"line":586},[290,24496,24497],{"class":303},"  .media__spinner",[290,24499,790],{"class":295},[290,24501,5178],{"class":461},[290,24503,24504],{"class":295},": spin ",[290,24506,10506],{"class":461},[290,24508,542],{"class":541},[290,24510,3159],{"class":461},[290,24512,21326],{"class":461},[290,24514,809],{"class":295},[290,24516,24517],{"class":163,"line":602},[290,24518,620],{"class":295},[2757,24520,24522,24524],{"id":24521},"prefers-reduced-motion-as-a-first-class-input",[18,24523,2584],{}," as a first-class input",[14,24526,24527,24528,24531,24532,69,24534,42],{},"Reduced motion is not a fallback bolted on at the end — it is an environment signal as legitimate as viewport width, and it belongs in the architecture from the start. The accessibility dimension is treated in full in the ",[27,24529,24530],{"href":1475},"accessibility in CSS animations guide",", including ",[27,24533,5174],{"href":1412},[27,24535,24536],{"href":3349},"creating accessible focus indicators",[47,24538],{},[50,24540,24542],{"id":24541},"progressive-enhancement-strategy","Progressive Enhancement Strategy",[14,24544,24545],{},"Treat motion as an enhancement layer over a fully functional static interface. The base styles must read and operate with no animation at all; motion is then added behind feature and preference queries so unsupported or motion-averse environments simply receive the calm baseline.",[281,24547,24549],{"className":438,"code":24548,"language":440,"meta":286,"style":286},"\u002F* 1. Baseline: fully usable with zero motion. *\u002F\n.dialog {\n  opacity: 1;\n  transform: none;\n}\n\n\u002F* 2. Enhancement: motion only where it is both supported and welcome. *\u002F\n@media (prefers-reduced-motion: no-preference) {\n  .dialog {\n    transition:\n      opacity var(--motion-base) var(--ease-standard),\n      transform var(--motion-base) var(--ease-standard),\n      overlay var(--motion-base) allow-discrete,\n      display var(--motion-base) allow-discrete;\n  }\n\n  \u002F* Animate in from a defined entry state (Chrome\u002FEdge 117+, Safari 17.4+, FF 129+). *\u002F\n  @starting-style {\n    .dialog[open] { opacity: 0; transform: translateY(8px); }\n  }\n}\n\n\u002F* 3. Guard newer features behind @supports so old engines skip them. *\u002F\n@supports not (transition-behavior: allow-discrete) {\n  .dialog { transition: opacity var(--motion-base) var(--ease-standard); }\n}\n",[18,24550,24551,24556,24563,24573,24583,24587,24591,24596,24602,24609,24615,24636,24656,24670,24684,24688,24692,24697,24704,24737,24741,24745,24749,24754,24767,24793],{"__ignoreMap":286},[290,24552,24553],{"class":163,"line":292},[290,24554,24555],{"class":455},"\u002F* 1. Baseline: fully usable with zero motion. *\u002F\n",[290,24557,24558,24561],{"class":163,"line":330},[290,24559,24560],{"class":303},".dialog",[290,24562,450],{"class":295},[290,24564,24565,24567,24569,24571],{"class":163,"line":337},[290,24566,462],{"class":461},[290,24568,465],{"class":295},[290,24570,468],{"class":461},[290,24572,471],{"class":295},[290,24574,24575,24577,24579,24581],{"class":163,"line":364},[290,24576,476],{"class":461},[290,24578,465],{"class":295},[290,24580,72],{"class":461},[290,24582,471],{"class":295},[290,24584,24585],{"class":163,"line":386},[290,24586,620],{"class":295},[290,24588,24589],{"class":163,"line":408},[290,24590,334],{"emptyLinePlaceholder":333},[290,24592,24593],{"class":163,"line":428},[290,24594,24595],{"class":455},"\u002F* 2. Enhancement: motion only where it is both supported and welcome. *\u002F\n",[290,24597,24598,24600],{"class":163,"line":517},[290,24599,874],{"class":541},[290,24601,16018],{"class":295},[290,24603,24604,24607],{"class":163,"line":523},[290,24605,24606],{"class":303},"  .dialog",[290,24608,450],{"class":295},[290,24610,24611,24613],{"class":163,"line":532},[290,24612,4745],{"class":461},[290,24614,529],{"class":295},[290,24616,24617,24620,24622,24624,24626,24628,24630,24632,24634],{"class":163,"line":551},[290,24618,24619],{"class":295},"      opacity ",[290,24621,1622],{"class":461},[290,24623,484],{"class":295},[290,24625,23793],{"class":1561},[290,24627,490],{"class":295},[290,24629,1622],{"class":461},[290,24631,484],{"class":295},[290,24633,14242],{"class":1561},[290,24635,583],{"class":295},[290,24637,24638,24640,24642,24644,24646,24648,24650,24652,24654],{"class":163,"line":586},[290,24639,20298],{"class":295},[290,24641,1622],{"class":461},[290,24643,484],{"class":295},[290,24645,23793],{"class":1561},[290,24647,490],{"class":295},[290,24649,1622],{"class":461},[290,24651,484],{"class":295},[290,24653,14242],{"class":1561},[290,24655,583],{"class":295},[290,24657,24658,24661,24663,24665,24667],{"class":163,"line":602},[290,24659,24660],{"class":461},"      overlay",[290,24662,1635],{"class":461},[290,24664,484],{"class":295},[290,24666,23793],{"class":1561},[290,24668,24669],{"class":295},") allow-discrete,\n",[290,24671,24672,24675,24677,24679,24681],{"class":163,"line":617},[290,24673,24674],{"class":295},"      display ",[290,24676,1622],{"class":461},[290,24678,484],{"class":295},[290,24680,23793],{"class":1561},[290,24682,24683],{"class":295},") allow-discrete;\n",[290,24685,24686],{"class":163,"line":623},[290,24687,771],{"class":295},[290,24689,24690],{"class":163,"line":628},[290,24691,334],{"emptyLinePlaceholder":333},[290,24693,24694],{"class":163,"line":634},[290,24695,24696],{"class":455},"  \u002F* Animate in from a defined entry state (Chrome\u002FEdge 117+, Safari 17.4+, FF 129+). *\u002F\n",[290,24698,24699,24702],{"class":163,"line":649},[290,24700,24701],{"class":541},"  @starting-style",[290,24703,450],{"class":295},[290,24705,24706,24709,24711,24713,24715,24717,24719,24721,24723,24725,24727,24729,24731,24733,24735],{"class":163,"line":660},[290,24707,24708],{"class":303},"    .dialog",[290,24710,1140],{"class":295},[290,24712,1097],{"class":303},[290,24714,1145],{"class":295},[290,24716,76],{"class":461},[290,24718,465],{"class":295},[290,24720,487],{"class":461},[290,24722,828],{"class":295},[290,24724,103],{"class":461},[290,24726,465],{"class":295},[290,24728,481],{"class":461},[290,24730,484],{"class":295},[290,24732,176],{"class":461},[290,24734,674],{"class":541},[290,24736,1122],{"class":295},[290,24738,24739],{"class":163,"line":688},[290,24740,771],{"class":295},[290,24742,24743],{"class":163,"line":693},[290,24744,620],{"class":295},[290,24746,24747],{"class":163,"line":698},[290,24748,334],{"emptyLinePlaceholder":333},[290,24750,24751],{"class":163,"line":704},[290,24752,24753],{"class":455},"\u002F* 3. Guard newer features behind @supports so old engines skip them. *\u002F\n",[290,24755,24756,24758,24760,24762,24764],{"class":163,"line":710},[290,24757,2086],{"class":541},[290,24759,2116],{"class":541},[290,24761,3595],{"class":295},[290,24763,939],{"class":461},[290,24765,24766],{"class":295},": allow-discrete) {\n",[290,24768,24769,24771,24773,24775,24777,24779,24781,24783,24785,24787,24789,24791],{"class":163,"line":717},[290,24770,24606],{"class":303},[290,24772,790],{"class":295},[290,24774,887],{"class":461},[290,24776,6384],{"class":295},[290,24778,1622],{"class":461},[290,24780,484],{"class":295},[290,24782,23793],{"class":1561},[290,24784,490],{"class":295},[290,24786,1622],{"class":461},[290,24788,484],{"class":295},[290,24790,14242],{"class":1561},[290,24792,1122],{"class":295},[290,24794,24795],{"class":163,"line":730},[290,24796,620],{"class":295},[14,24798,24799,24800,42],{},"This three-tier structure — static base, preference-gated enhancement, feature-gated cutting edge — means a 2019 browser, a current browser, and a user with vestibular sensitivity each get the best experience their environment can support. The discrete-transition technique itself is covered in ",[27,24801,10210],{"href":10209},[47,24803],{},[50,24805,24807],{"id":24806},"browser-support-specification-status","Browser Support & Specification Status",[2250,24809,24810,24823],{},[2253,24811,24812],{},[2256,24813,24814,24816,24819,24821],{},[2259,24815,3737],{},[2259,24817,24818],{},"Chrome\u002FEdge",[2259,24820,2297],{},[2259,24822,2287],{},[2269,24824,24825,24842,24857,24869,24881,24894,24911,24926],{},[2256,24826,24827,24835,24838,24840],{},[2274,24828,24829,1203,24831,1203,24833],{},[18,24830,887],{},[18,24832,3968],{},[18,24834,22528],{},[2274,24836,24837],{},"26+",[2274,24839,8462],{},[2274,24841,8465],{},[2256,24843,24844,24851,24853,24855],{},[2274,24845,24846,1203,24848,24850],{},[18,24847,103],{},[18,24849,76],{}," compositing",[2274,24852,3800],{},[2274,24854,8462],{},[2274,24856,8465],{},[2256,24858,24859,24863,24865,24867],{},[2274,24860,24861],{},[18,24862,2584],{},[2274,24864,3758],{},[2274,24866,3764],{},[2274,24868,3761],{},[2256,24870,24871,24875,24877,24879],{},[2274,24872,24873],{},[18,24874,1495],{},[2274,24876,2279],{},[2274,24878,2300],{},[2274,24880,3781],{},[2256,24882,24883,24888,24890,24892],{},[2274,24884,24885,24887],{},[18,24886,10151],{}," typed custom properties",[2274,24889,3781],{},[2274,24891,13306],{},[2274,24893,13303],{},[2256,24895,24896,24902,24905,24908],{},[2274,24897,24898,21541,24900],{},[18,24899,34],{},[18,24901,38],{},[2274,24903,24904],{},"117+",[2274,24906,24907],{},"17.4+",[2274,24909,24910],{},"129+",[2256,24912,24913,24918,24921,24923],{},[2274,24914,24915,24916],{},"Scroll-driven ",[18,24917,8833],{},[2274,24919,24920],{},"115+",[2274,24922,24837],{},[2274,24924,24925],{},"Behind a flag",[2256,24927,24928,24934,24937,24940],{},[2274,24929,24930,24931],{},"Style queries ",[18,24932,24933],{},"@container style()",[2274,24935,24936],{},"111+",[2274,24938,24939],{},"18+",[2274,24941,24942],{},"Not yet stable",[14,24944,24945,24946,24951,24952,24954,24955,8393,24957,24959,24960,24962],{},"Verify current support against ",[27,24947,24950],{"href":24948,"rel":24949},"https:\u002F\u002Fcaniuse.com\u002F",[13489],"caniuse.com"," before shipping, and wrap any feature without universal support in ",[18,24953,2086],{},". The baseline primitives — transitions, keyframes, ",[18,24956,103],{},[18,24958,76],{}," — are safe everywhere; treat ",[18,24961,10151],{},", discrete transitions, and scroll-driven animations as enhancements.",[47,24964],{},[50,24966,24968],{"id":24967},"common-issues-mitigations","Common Issues & Mitigations",[2250,24970,24971,24982],{},[2253,24972,24973],{},[2256,24974,24975,24977,24979],{},[2259,24976,2338],{},[2259,24978,8560],{},[2259,24980,24981],{},"Mitigation",[2269,24983,24984,25016,25032,25054,25075,25094],{},[2256,24985,24986,24991,25002],{},[2274,24987,24988],{},[62,24989,24990],{},"Animation drops frames under load",[2274,24992,16092,24993,569,24995,569,24997,1745,24999,25001],{},[18,24994,1748],{},[18,24996,2722],{},[18,24998,2728],{},[18,25000,2725],{}," forces layout and paint on the main thread every frame.",[2274,25003,25004,25005,69,25007,25009,25010,25013,25014,42],{},"Animate ",[18,25006,103],{},[18,25008,76],{}," only. Replace size changes with ",[18,25011,25012],{},"scale()"," and position changes with ",[18,25015,16108],{},[2256,25017,25018,25023,25026],{},[2274,25019,25020],{},[62,25021,25022],{},"One-frame hitch when an animation starts",[2274,25024,25025],{},"The element is promoted to a compositor layer only at the first frame.",[2274,25027,25028,25029,25031],{},"Add ",[18,25030,2075],{}," just before the animation begins and remove it when finished — never leave it on permanently.",[2256,25033,25034,25039,25042],{},[2274,25035,25036],{},[62,25037,25038],{},"Custom property animation snaps instead of easing",[2274,25040,25041],{},"An unregistered custom property is an untyped string the browser cannot interpolate.",[2274,25043,25044,25045,11170,25047,11173,25049,2351,25051,42],{},"Register it with ",[18,25046,10151],{},[18,25048,10226],{},[18,25050,11176],{},[18,25052,25053],{},"\u003Ccolor>",[2256,25055,25056,25061,25067],{},[2274,25057,25058],{},[62,25059,25060],{},"Focus ring vanishes during a transition",[2274,25062,25063,25064,25066],{},"A blanket ",[18,25065,1717],{}," or a transition that fades the outline removes the visible focus indicator.",[2274,25068,25069,25070,25072,25073,42],{},"Keep a visible ",[18,25071,1495],{}," outline and never transition it out; preserve ",[18,25074,1695],{},[2256,25076,25077,25082,25088],{},[2274,25078,25079],{},[62,25080,25081],{},"Hover state sticks after a tap on mobile",[2274,25083,25084,25085,25087],{},"Touch devices emulate ",[18,25086,3689],{},", which latches until the next interaction.",[2274,25089,25090,25091,42],{},"Neutralise hover under ",[18,25092,25093],{},"@media (hover: none) and (pointer: coarse)",[2256,25095,25096,25101,25108],{},[2274,25097,25098],{},[62,25099,25100],{},"Element jumps back at animation end",[2274,25102,25103,15064,25105,25107],{},[18,25104,6779],{},[18,25106,72],{},", discarding the final keyframe.",[2274,25109,25110,25111,2351,25113,25115],{},"Set ",[18,25112,5544],{},[18,25114,7069],{}," to hold the end state.",[47,25117],{},[50,25119,16218],{"id":16217},[14,25121,25122,25125],{},[62,25123,25124],{},"When should I reach for CSS instead of JavaScript for an animation?","\nUse CSS when motion is declarative and state-driven: hover, focus, toggles, loaders, and entrance reveals. Reach for JavaScript and the Web Animations API only when you need runtime-computed values, complex sequencing with playback control, or animations driven by data that does not map cleanly to a CSS state.",[14,25127,25128,25141,69,25143,25145,25146,569,25148,569,25150,1745,25152,25154],{},[62,25129,25130,25131,69,25133,25135,25136,69,25138,25140],{},"Why do ",[18,25132,103],{},[18,25134,76],{}," animate smoothly while ",[18,25137,1748],{},[18,25139,2728],{}," stutter?",[18,25142,103],{},[18,25144,76],{}," can be handled entirely on the compositor thread without re-running layout or paint, so they animate even when the main thread is busy. Animating ",[18,25147,1748],{},[18,25149,2722],{},[18,25151,2728],{},[18,25153,2731],{}," forces layout and paint on every frame, which competes with JavaScript on the main thread and drops frames.",[14,25156,25157,25162,25163,25165],{},[62,25158,5714,25159,25161],{},[18,25160,3797],{}," make my animations faster?","\nOnly when used sparingly. ",[18,25164,3797],{}," promotes an element to its own compositor layer ahead of time, which avoids a one-frame hitch at animation start. Applying it to many elements wastes GPU memory and can slow the page, so add it just before an animation and remove it when finished.",[14,25167,25168,25171,25172,25174],{},[62,25169,25170],{},"How do I respect users who prefer reduced motion?","\nWrap non-essential motion so it is disabled under ",[18,25173,4126],{},". Replace movement with an instant state change or a short opacity fade rather than removing the feedback entirely, and never gate essential information behind an animation.",[14,25176,25177,25180,25181,25183,25184,569,25187,1745,25189,25191],{},[62,25178,25179],{},"Can I animate a CSS custom property smoothly?","\nOnly if you register it with ",[18,25182,10151],{}," and give it a typed syntax such as ",[18,25185,25186],{},"\u003Clength>",[18,25188,25053],{},[18,25190,7005],{},". An unregistered custom property is treated as a plain string, so the browser cannot interpolate between values and the change snaps instantly.",[14,25193,25194,25199,25200,25202,25203,25205,25206,25208],{},[62,25195,25196,25197,5734],{},"How do I animate an element to and from ",[18,25198,20],{},"\nAdd ",[18,25201,34],{}," and pair it with ",[18,25204,38],{}," to define the entry values. This lets ",[18,25207,59],{}," and other discrete properties participate in a transition, supported in Chrome and Edge 117+, Safari 17.4+, and Firefox 129+.",[47,25210],{},[50,25212,1391],{"id":1390},[1393,25214,25215,25220,25225,25230,25235,25240,25248,25253],{},[1396,25216,25217,25219],{},[27,25218,30],{"href":29}," — the easing, duration, and property grammar behind every state change.",[1396,25221,25222,25224],{},[27,25223,1420],{"href":1419}," — authored timelines for loaders, shimmers, and staggered reveals.",[1396,25226,25227,25229],{},[27,25228,3345],{"href":3344}," — pseudo-class-driven feedback with input-modality parity.",[1396,25231,25232,25234],{},[27,25233,1476],{"href":1475}," — reduced-motion, focus visibility, and WCAG-aligned motion.",[1396,25236,25237,25239],{},[27,25238,3355],{"href":10147}," — centralising duration and easing tokens for one motion language.",[1396,25241,25242,25244,25245,25247],{},[27,25243,1736],{"href":22660}," — compositor layers, ",[18,25246,3797],{},", and holding 60fps.",[1396,25249,25250,25252],{},[27,25251,8823],{"href":8822}," — when declarative CSS gives way to scripted control.",[1396,25254,25255,25257],{},[27,25256,11296],{"href":5777}," — the companion guide on how components adapt their shape to available space.",[1430,25259,25260],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":286,"searchDepth":330,"depth":330,"links":25262},[25263,25264,25271,25272,25273,25274,25283,25284,25285,25286,25287],{"id":22618,"depth":330,"text":22619},{"id":22687,"depth":330,"text":25265,"children":25266},"Syntax Reference: transition, @keyframes, and animation-*",[25267,25269],{"id":22707,"depth":337,"text":25268},"The transition shorthand",{"id":22874,"depth":337,"text":25270},"@keyframes and the animation-* family",{"id":23260,"depth":330,"text":23261},{"id":23543,"depth":330,"text":23544},{"id":23915,"depth":330,"text":23916},{"id":24176,"depth":330,"text":24177,"children":25275},[25276,25278,25280,25281],{"id":24183,"depth":337,"text":25277},"Custom properties and @property",{"id":24312,"depth":337,"text":25279},"@layer for predictable overrides",{"id":24376,"depth":337,"text":24377},{"id":24521,"depth":337,"text":25282},"prefers-reduced-motion as a first-class input",{"id":24541,"depth":330,"text":24542},{"id":24806,"depth":330,"text":24807},{"id":24967,"depth":330,"text":24968},{"id":16217,"depth":330,"text":16218},{"id":1390,"depth":330,"text":1391},"Master CSS-only micro-interactions and animations: the compositor thread, transition and keyframe syntax, token-driven motion, accessible reduced-motion design.",{"seoTitle":1481,"datePublished":1447,"dateModified":1447,"faq":25290},[25291,25293,25296,25299,25301,25303],{"q":25124,"a":25292},"Use CSS when motion is declarative and state-driven: hover, focus, toggles, loaders, and entrance reveals. Reach for JavaScript and the Web Animations API only when you need runtime-computed values, complex sequencing with playback control, or animations driven by data that does not map cleanly to a CSS state.",{"q":25294,"a":25295},"Why do transform and opacity animate smoothly while width and top stutter?","transform and opacity can be handled entirely on the compositor thread without re-running layout or paint, so they animate even when the main thread is busy. Animating width, height, top, or left forces layout and paint on every frame, which competes with JavaScript on the main thread and drops frames.",{"q":25297,"a":25298},"Does will-change make my animations faster?","Only when used sparingly. will-change promotes an element to its own compositor layer ahead of time, which avoids a one-frame hitch at animation start. Applying it to many elements wastes GPU memory and can slow the page, so add it just before an animation and remove it when finished.",{"q":25170,"a":25300},"Wrap non-essential motion so it is disabled under @media (prefers-reduced-motion: reduce). Replace movement with an instant state change or a short opacity fade rather than removing the feedback entirely, and never gate essential information behind an animation.",{"q":25179,"a":25302},"Only if you register it with @property and give it a typed syntax such as \u003Clength>, \u003Ccolor>, or \u003Cnumber>. An unregistered custom property is treated as a plain string, so the browser cannot interpolate between values and the change snaps instantly.",{"q":25304,"a":25305},"How do I animate an element to and from display: none?","Add transition-behavior: allow-discrete and pair it with @starting-style to define the entry values. This lets display and other discrete properties participate in a transition, supported in Chrome and Edge 117+, Safari 17.4+, and Firefox 129+.","\u002Fcss-only-micro-interactions-animations",{"title":22483,"description":25288},"css-only-micro-interactions-animations\u002Findex","Iqyhgir-NKfQmnrgbaL8Ukfv4AE0BTZVPti2LYMame0",{"id":25311,"title":25312,"body":25313,"description":26310,"extension":1444,"meta":26311,"navigation":333,"path":26322,"seo":26323,"stem":26324,"__hash__":26325},"content\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcontainer-query-triggered-keyframe-animations\u002Findex.md","Container-Query-Triggered Keyframe Animations: Animate Differently per Available Space",{"type":7,"value":25314,"toc":26301},[25315,25318,25343,25348,25366,25368,25372,25385,25399,25401,25404,25415,25461,25542,25994,26014,26016,26020,26061,26063,26067,26077,26183,26191,26193,26195,26214,26216,26218,26227,26239,26253,26262,26264,26266,26298],[10,25316,25312],{"id":25317},"container-query-triggered-keyframe-animations-animate-differently-per-available-space",[14,25319,25320,25321,2694,25324,25326,25327,25330,25331,25333,25334,25337,25338,25342],{},"A reusable component does not know where it will be placed — the same card might sit in a wide hero, a three-column grid, or a narrow sidebar. The narrow problem this guide solves: make a component run ",[86,25322,25323],{},"one",[18,25325,3968],{}," animation when it has room and a ",[86,25328,25329],{},"different, calmer"," one when it is cramped, deciding entirely from the component's own container size with ",[18,25332,12001],{},", plus style queries for state-driven switches. No viewport media queries, no JavaScript measuring. This page is part of the ",[27,25335,25336],{"href":1419},"keyframe animation patterns"," guide and deliberately bridges into the responsive-layout side of the site: the mechanics build directly on the ",[27,25339,25341],{"href":25340},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002F","container query syntax basics"," guide.",[14,25344,25345],{},[62,25346,25347],{},"What this technique enables:",[1393,25349,25350,25357,25360,25363],{},[1396,25351,25352,25353,25356],{},"Swap ",[18,25354,25355],{},"animation-name"," per container width — a wide card slides in, a narrow one fades.",[1396,25358,25359],{},"Use style queries to switch animations by a custom-property state, not just size.",[1396,25361,25362],{},"Keep components context-agnostic; placement decides the motion.",[1396,25364,25365],{},"Layer reduced-motion safety on top, independent of container size.",[47,25367],{},[50,25369,25371],{"id":25370},"why-drive-animation-from-the-container-not-the-viewport","Why Drive Animation from the Container, Not the Viewport",[14,25373,25374,25375,25378,25379,25381,25382,25384],{},"Viewport media queries answer \"how big is the screen\", which is the wrong question for a component dropped into an unknown slot. A card in a sidebar is narrow even on a 4K display. Animation is a presentation decision that should track ",[86,25376,25377],{},"the space the component actually occupies",", and that is exactly what a size ",[18,25380,12001],{}," query measures. Switching ",[18,25383,25355],{}," inside a container query means the component carries its own responsive motion logic and behaves correctly in any layout, no parent coordination required.",[14,25386,25387,25388,25391,25392,25395,25396,25398],{},"The reason to do this in CSS rather than a ",[18,25389,25390],{},"ResizeObserver"," plus JavaScript is resilience and cost: the browser already tracks container sizes for layout, so reacting to them in CSS is essentially free, runs without script, and cannot desync the way an observer callback can during heavy main-thread work. The tradeoff is that container queries only ",[86,25393,25394],{},"select"," animations — they cannot interpolate based on size the way a script could. And crucially, choosing an animation by size says nothing about whether the user wants motion at all; reduced-motion handling is a separate, mandatory layer that must override the container's choice. Treat container size as \"which animation is appropriate here\" and ",[18,25397,2584],{}," as \"is any animation allowed at all\".",[47,25400],{},[50,25402,25403],{"id":4202},"Complete Working Implementation",[14,25405,25406,25407,25410,25411,25414],{},"The card lives in a container declared with ",[18,25408,25409],{},"container-type: inline-size",". Below a width threshold it fades in gently; at or above it, it slides and scales in with more flourish. A style query then lets a ",[18,25412,25413],{},"--state"," custom property on the container trigger an attention pulse independent of size.",[133,25416,140,25419,140,25422,140,25425,140,25428,140,25431,140,25434,140,25438,140,25440,140,25444,140,25448,140,25450,140,25455,140,25458],{"viewBox":25417,"role":136,"ariaLabel":25418,"xmlns":138,"style":139},"0 0 720 330","A width threshold deciding which keyframe animation a card runs: a narrow container picks a fade, a wide container picks a slide and scale",[142,25420,25421],{},"Container width threshold selecting an animation",[146,25423,25424],{},"A horizontal width axis with a threshold at 400px. To the left, narrow containers map to a fade-in animation; to the right, wide containers map to a slide-and-scale animation.",[150,25426,25427],{"x":152,"y":2598,"style":1781},"@container width decides the animation",[163,25429],{"x1":4146,"y1":1822,"x2":25430,"y2":1822,"stroke":167,"strokeWidth":168,"opacity":169},"680",[163,25432],{"x1":152,"y1":9105,"x2":152,"y2":12480,"stroke":177,"strokeWidth":194,"strokeDashArray":25433},[5901,5911],[150,25435,25437],{"x":152,"y":5892,"style":25436},"text-anchor:middle;fill:#7aa2ff;font:600 13px ui-monospace,monospace","min-width: 400px",[171,25439],{"x":5144,"y":5159,"width":174,"height":1786,"rx":836,"fill":167,"opacity":3125},[150,25441,25443],{"x":1822,"y":5909,"style":25442},"text-anchor:middle;fill:currentColor;font:13px sans-serif;opacity:0.85","narrow container",[150,25445,25447],{"x":1822,"y":174,"style":25446},"text-anchor:middle;fill:#7aa2ff;font:13px ui-monospace,monospace","animation: card-fade",[171,25449],{"x":8897,"y":165,"width":2622,"height":9105,"rx":836,"fill":177,"opacity":178},[150,25451,25454],{"x":25452,"y":25453,"style":25442},"555","128","wide container",[150,25456,25457],{"x":25452,"y":2622,"style":25446},"animation: card-slide",[150,25459,25460],{"x":152,"y":5397,"style":1839},"same component, motion chosen by the space it occupies",[281,25462,25464],{"className":283,"code":25463,"language":285,"meta":286,"style":286},"\u003C!-- The grid cell is the query container; the card is the queried child. -->\n\u003Cdiv class=\"slot\">\n  \u003Carticle class=\"card\">\n    \u003Ch3>Project Atlas\u003C\u002Fh3>\n    \u003Cp>Quarterly status and next milestones.\u003C\u002Fp>\n  \u003C\u002Farticle>\n\u003C\u002Fdiv>\n",[18,25465,25466,25471,25486,25500,25513,25526,25534],{"__ignoreMap":286},[290,25467,25468],{"class":163,"line":292},[290,25469,25470],{"class":455},"\u003C!-- The grid cell is the query container; the card is the queried child. -->\n",[290,25472,25473,25475,25477,25479,25481,25484],{"class":163,"line":330},[290,25474,296],{"class":295},[290,25476,342],{"class":299},[290,25478,314],{"class":303},[290,25480,307],{"class":295},[290,25482,25483],{"class":310},"\"slot\"",[290,25485,327],{"class":295},[290,25487,25488,25490,25492,25494,25496,25498],{"class":163,"line":337},[290,25489,367],{"class":295},[290,25491,11445],{"class":299},[290,25493,314],{"class":303},[290,25495,307],{"class":295},[290,25497,9295],{"class":310},[290,25499,327],{"class":295},[290,25501,25502,25504,25506,25509,25511],{"class":163,"line":364},[290,25503,4290],{"class":295},[290,25505,2757],{"class":299},[290,25507,25508],{"class":295},">Project Atlas\u003C\u002F",[290,25510,2757],{"class":299},[290,25512,327],{"class":295},[290,25514,25515,25517,25519,25522,25524],{"class":163,"line":386},[290,25516,4290],{"class":295},[290,25518,14],{"class":299},[290,25520,25521],{"class":295},">Quarterly status and next milestones.\u003C\u002F",[290,25523,14],{"class":299},[290,25525,327],{"class":295},[290,25527,25528,25530,25532],{"class":163,"line":408},[290,25529,4315],{"class":295},[290,25531,11445],{"class":299},[290,25533,327],{"class":295},[290,25535,25536,25538,25540],{"class":163,"line":428},[290,25537,431],{"class":295},[290,25539,342],{"class":299},[290,25541,327],{"class":295},[281,25543,25545],{"className":438,"code":25544,"language":440,"meta":286,"style":286},"\u002F* Establish the container. The child can now query this width. *\u002F\n.slot {\n  container-type: inline-size;\n  \u002F* `container-name` is optional but lets nested queries target it. *\u002F\n  container-name: slot;\n}\n\n\u002F* Default \u002F narrow behaviour: a calm fade. *\u002F\n.card {\n  animation: card-fade 0.4s ease both;\n}\n\n@keyframes card-fade {\n  from { opacity: 0; }\n  to   { opacity: 1; }\n}\n\n\u002F* When the CONTAINER (not the viewport) is wide enough, swap the\n   animation-name to a richer slide-and-scale entrance. *\u002F\n@container slot (min-width: 400px) {\n  .card {\n    animation-name: card-slide;\n    animation-duration: 0.5s;\n    animation-timing-function: cubic-bezier(0.2, 0, 0, 1);\n  }\n}\n\n@keyframes card-slide {\n  from { opacity: 0; transform: translateY(24px) scale(0.96); }\n  to   { opacity: 1; transform: translateY(0)    scale(1); }\n}\n\n\u002F* Style query: react to a custom property STATE on the container,\n   independent of its size. Pulse the card when --state: alert. *\u002F\n@container slot style(--state: alert) {\n  .card {\n    animation: card-pulse 1s ease-in-out infinite;\n  }\n}\n\n@keyframes card-pulse {\n  0%, 100% { transform: scale(1); }\n  50%      { transform: scale(1.02); }\n}\n\n\u002F* Mandatory: container size never overrides the user's motion choice. *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .card { animation: none; opacity: 1; transform: none; }\n}\n",[18,25546,25547,25552,25559,25566,25571,25579,25583,25587,25592,25598,25615,25619,25623,25632,25646,25660,25664,25668,25673,25678,25685,25691,25699,25711,25738,25742,25746,25750,25759,25795,25830,25834,25838,25843,25848,25855,25861,25878,25882,25886,25890,25899,25922,25941,25945,25949,25954,25960,25990],{"__ignoreMap":286},[290,25548,25549],{"class":163,"line":292},[290,25550,25551],{"class":455},"\u002F* Establish the container. The child can now query this width. *\u002F\n",[290,25553,25554,25557],{"class":163,"line":330},[290,25555,25556],{"class":303},".slot",[290,25558,450],{"class":295},[290,25560,25561,25563],{"class":163,"line":337},[290,25562,11509],{"class":461},[290,25564,25565],{"class":295},": inline-size;\n",[290,25567,25568],{"class":163,"line":364},[290,25569,25570],{"class":455},"  \u002F* `container-name` is optional but lets nested queries target it. *\u002F\n",[290,25572,25573,25576],{"class":163,"line":386},[290,25574,25575],{"class":461},"  container-name",[290,25577,25578],{"class":295},": slot;\n",[290,25580,25581],{"class":163,"line":408},[290,25582,620],{"class":295},[290,25584,25585],{"class":163,"line":428},[290,25586,334],{"emptyLinePlaceholder":333},[290,25588,25589],{"class":163,"line":517},[290,25590,25591],{"class":455},"\u002F* Default \u002F narrow behaviour: a calm fade. *\u002F\n",[290,25593,25594,25596],{"class":163,"line":523},[290,25595,11528],{"class":303},[290,25597,450],{"class":295},[290,25599,25600,25602,25605,25607,25609,25611,25613],{"class":163,"line":532},[290,25601,6035],{"class":461},[290,25603,25604],{"class":295},": card-fade ",[290,25606,169],{"class":461},[290,25608,1886],{"class":541},[290,25610,545],{"class":461},[290,25612,22990],{"class":461},[290,25614,471],{"class":295},[290,25616,25617],{"class":163,"line":551},[290,25618,620],{"class":295},[290,25620,25621],{"class":163,"line":586},[290,25622,334],{"emptyLinePlaceholder":333},[290,25624,25625,25627,25630],{"class":163,"line":602},[290,25626,3968],{"class":541},[290,25628,25629],{"class":1561}," card-fade",[290,25631,450],{"class":295},[290,25633,25634,25636,25638,25640,25642,25644],{"class":163,"line":617},[290,25635,6191],{"class":303},[290,25637,790],{"class":295},[290,25639,76],{"class":461},[290,25641,465],{"class":295},[290,25643,487],{"class":461},[290,25645,809],{"class":295},[290,25647,25648,25650,25652,25654,25656,25658],{"class":163,"line":623},[290,25649,6072],{"class":303},[290,25651,6208],{"class":295},[290,25653,76],{"class":461},[290,25655,465],{"class":295},[290,25657,468],{"class":461},[290,25659,809],{"class":295},[290,25661,25662],{"class":163,"line":628},[290,25663,620],{"class":295},[290,25665,25666],{"class":163,"line":634},[290,25667,334],{"emptyLinePlaceholder":333},[290,25669,25670],{"class":163,"line":649},[290,25671,25672],{"class":455},"\u002F* When the CONTAINER (not the viewport) is wide enough, swap the\n",[290,25674,25675],{"class":163,"line":660},[290,25676,25677],{"class":455},"   animation-name to a richer slide-and-scale entrance. *\u002F\n",[290,25679,25680,25682],{"class":163,"line":688},[290,25681,12001],{"class":541},[290,25683,25684],{"class":295}," slot (min-width: 400px) {\n",[290,25686,25687,25689],{"class":163,"line":693},[290,25688,9083],{"class":303},[290,25690,450],{"class":295},[290,25692,25693,25696],{"class":163,"line":698},[290,25694,25695],{"class":461},"    animation-name",[290,25697,25698],{"class":295},": card-slide;\n",[290,25700,25701,25703,25705,25707,25709],{"class":163,"line":704},[290,25702,2826],{"class":461},[290,25704,465],{"class":295},[290,25706,798],{"class":461},[290,25708,1886],{"class":541},[290,25710,471],{"class":295},[290,25712,25713,25716,25718,25720,25722,25724,25726,25728,25730,25732,25734,25736],{"class":163,"line":710},[290,25714,25715],{"class":461},"    animation-timing-function",[290,25717,465],{"class":295},[290,25719,11767],{"class":461},[290,25721,484],{"class":295},[290,25723,566],{"class":461},[290,25725,569],{"class":295},[290,25727,487],{"class":461},[290,25729,569],{"class":295},[290,25731,487],{"class":461},[290,25733,569],{"class":295},[290,25735,468],{"class":461},[290,25737,500],{"class":295},[290,25739,25740],{"class":163,"line":717},[290,25741,771],{"class":295},[290,25743,25744],{"class":163,"line":730},[290,25745,620],{"class":295},[290,25747,25748],{"class":163,"line":742},[290,25749,334],{"emptyLinePlaceholder":333},[290,25751,25752,25754,25757],{"class":163,"line":768},[290,25753,3968],{"class":541},[290,25755,25756],{"class":1561}," card-slide",[290,25758,450],{"class":295},[290,25760,25761,25763,25765,25767,25769,25771,25773,25775,25777,25779,25781,25783,25785,25787,25789,25791,25793],{"class":163,"line":774},[290,25762,6191],{"class":303},[290,25764,790],{"class":295},[290,25766,76],{"class":461},[290,25768,465],{"class":295},[290,25770,487],{"class":461},[290,25772,828],{"class":295},[290,25774,103],{"class":461},[290,25776,465],{"class":295},[290,25778,481],{"class":461},[290,25780,484],{"class":295},[290,25782,158],{"class":461},[290,25784,674],{"class":541},[290,25786,490],{"class":295},[290,25788,493],{"class":461},[290,25790,484],{"class":295},[290,25792,683],{"class":461},[290,25794,1122],{"class":295},[290,25796,25797,25799,25801,25803,25805,25807,25809,25811,25813,25815,25817,25819,25822,25824,25826,25828],{"class":163,"line":779},[290,25798,6072],{"class":303},[290,25800,6208],{"class":295},[290,25802,76],{"class":461},[290,25804,465],{"class":295},[290,25806,468],{"class":461},[290,25808,828],{"class":295},[290,25810,103],{"class":461},[290,25812,465],{"class":295},[290,25814,481],{"class":461},[290,25816,484],{"class":295},[290,25818,487],{"class":461},[290,25820,25821],{"class":295},")    ",[290,25823,493],{"class":461},[290,25825,484],{"class":295},[290,25827,468],{"class":461},[290,25829,1122],{"class":295},[290,25831,25832],{"class":163,"line":784},[290,25833,620],{"class":295},[290,25835,25836],{"class":163,"line":812},[290,25837,334],{"emptyLinePlaceholder":333},[290,25839,25840],{"class":163,"line":860},[290,25841,25842],{"class":455},"\u002F* Style query: react to a custom property STATE on the container,\n",[290,25844,25845],{"class":163,"line":865},[290,25846,25847],{"class":455},"   independent of its size. Pulse the card when --state: alert. *\u002F\n",[290,25849,25850,25852],{"class":163,"line":871},[290,25851,12001],{"class":541},[290,25853,25854],{"class":295}," slot style(--state: alert) {\n",[290,25856,25857,25859],{"class":163,"line":880},[290,25858,9083],{"class":303},[290,25860,450],{"class":295},[290,25862,25863,25865,25868,25870,25872,25874,25876],{"class":163,"line":896},[290,25864,6152],{"class":461},[290,25866,25867],{"class":295},": card-pulse ",[290,25869,468],{"class":461},[290,25871,1886],{"class":541},[290,25873,21329],{"class":461},[290,25875,21326],{"class":461},[290,25877,471],{"class":295},[290,25879,25880],{"class":163,"line":4734},[290,25881,771],{"class":295},[290,25883,25884],{"class":163,"line":4742},[290,25885,620],{"class":295},[290,25887,25888],{"class":163,"line":4761},[290,25889,334],{"emptyLinePlaceholder":333},[290,25891,25892,25894,25897],{"class":163,"line":4766},[290,25893,3968],{"class":541},[290,25895,25896],{"class":1561}," card-pulse",[290,25898,450],{"class":295},[290,25900,25901,25903,25905,25908,25910,25912,25914,25916,25918,25920],{"class":163,"line":4786},[290,25902,21039],{"class":303},[290,25904,569],{"class":295},[290,25906,25907],{"class":303},"100%",[290,25909,790],{"class":295},[290,25911,103],{"class":461},[290,25913,465],{"class":295},[290,25915,493],{"class":461},[290,25917,484],{"class":295},[290,25919,468],{"class":461},[290,25921,1122],{"class":295},[290,25923,25924,25926,25929,25931,25933,25935,25937,25939],{"class":163,"line":9635},[290,25925,21075],{"class":303},[290,25927,25928],{"class":295},"      { ",[290,25930,103],{"class":461},[290,25932,465],{"class":295},[290,25934,493],{"class":461},[290,25936,484],{"class":295},[290,25938,2057],{"class":461},[290,25940,1122],{"class":295},[290,25942,25943],{"class":163,"line":9641},[290,25944,620],{"class":295},[290,25946,25947],{"class":163,"line":9647},[290,25948,334],{"emptyLinePlaceholder":333},[290,25950,25951],{"class":163,"line":9665},[290,25952,25953],{"class":455},"\u002F* Mandatory: container size never overrides the user's motion choice. *\u002F\n",[290,25955,25956,25958],{"class":163,"line":9683},[290,25957,874],{"class":541},[290,25959,877],{"class":295},[290,25961,25962,25964,25966,25968,25970,25972,25974,25976,25978,25980,25982,25984,25986,25988],{"class":163,"line":9688},[290,25963,9083],{"class":303},[290,25965,790],{"class":295},[290,25967,5178],{"class":461},[290,25969,465],{"class":295},[290,25971,72],{"class":461},[290,25973,828],{"class":295},[290,25975,76],{"class":461},[290,25977,465],{"class":295},[290,25979,468],{"class":461},[290,25981,828],{"class":295},[290,25983,103],{"class":461},[290,25985,465],{"class":295},[290,25987,72],{"class":461},[290,25989,809],{"class":295},[290,25991,25992],{"class":163,"line":9694},[290,25993,620],{"class":295},[14,25995,25110,25996,25999,26000,26002,26003,26006,26007,26010,26011,25342],{},[18,25997,25998],{},"--state: alert"," on the ",[18,26001,25556],{}," (for example ",[18,26004,26005],{},"\u003Cdiv class=\"slot\" style=\"--state:alert\">",") to trigger the style-query pulse. Because the pulse rule lives inside a style query, the same card stays calm everywhere else without any extra class plumbing. The widths used here are container units conceptually; for sizing ",[86,26008,26009],{},"inside"," the card relative to its container, reach for the ",[27,26012,26013],{"href":11248},"container query units cqi and cqb",[47,26015],{},[50,26017,26019],{"id":26018},"the-key-technique-reassigning-animation-name-inside-container","The Key Technique: Reassigning animation-name Inside @container",[14,26021,26022,26023,26025,26026,26028,26029,26032,26033,2694,26036,26038,26039,26041,26042,26045,26046,26049,26050,26053,26054,465,26057,26060],{},"The mechanism is subtle but small: the card always declares an ",[18,26024,5178],{}," shorthand, so it animates by default. The ",[18,26027,12001],{}," block then does not start a ",[86,26030,26031],{},"new"," animation — it ",[86,26034,26035],{},"reassigns",[18,26037,25355],{}," (and tweaks duration and easing) on the same element. The cascade resolves which ",[18,26040,25355],{}," wins based on whether the container query matches, and the browser runs whichever name is in effect. Because the query condition is the container's measured ",[18,26043,26044],{},"inline-size",", the very same component picks ",[18,26047,26048],{},"card-fade"," in a 300px slot and ",[18,26051,26052],{},"card-slide"," in a 500px slot, with the decision made at layout time and re-evaluated automatically when the slot resizes. Style queries extend this from dimensions to ",[86,26055,26056],{},"state",[18,26058,26059],{},"@container style(--state: alert)"," matches on the computed value of a custom property on the container, letting a parent flip a child's animation by setting one variable — no class toggling, no JavaScript.",[47,26062],{},[50,26064,26066],{"id":26065},"variation-named-containers-and-a-fallback-for-no-query-support","Variation: Named Containers and a Fallback for No Query Support",[14,26068,26069,26070,26073,26074,26076],{},"In real layouts a card may be nested inside several containers. Naming the container with ",[18,26071,26072],{},"container-name"," makes the query unambiguous, and an ",[18,26075,2086],{}," guard lets you ship a sensible default to engines without container-query support.",[281,26078,26080],{"className":438,"code":26079,"language":440,"meta":286,"style":286},"\u002F* Without container-query support, fall back to a viewport breakpoint\n   so wide screens still get the richer entrance. *\u002F\n@supports not (container-type: inline-size) {\n  @media (min-width: 700px) {\n    .card { animation-name: card-slide; }\n  }\n}\n\n\u002F* With support, the named query is authoritative regardless of viewport. *\u002F\n@supports (container-type: inline-size) {\n  .slot { container: slot \u002F inline-size; } \u002F* shorthand: name \u002F type *\u002F\n}\n",[18,26081,26082,26087,26092,26105,26124,26136,26140,26144,26148,26153,26163,26179],{"__ignoreMap":286},[290,26083,26084],{"class":163,"line":292},[290,26085,26086],{"class":455},"\u002F* Without container-query support, fall back to a viewport breakpoint\n",[290,26088,26089],{"class":163,"line":330},[290,26090,26091],{"class":455},"   so wide screens still get the richer entrance. *\u002F\n",[290,26093,26094,26096,26098,26100,26102],{"class":163,"line":337},[290,26095,2086],{"class":541},[290,26097,2116],{"class":541},[290,26099,3595],{"class":295},[290,26101,24401],{"class":461},[290,26103,26104],{"class":295},": inline-size) {\n",[290,26106,26107,26110,26112,26115,26117,26120,26122],{"class":163,"line":364},[290,26108,26109],{"class":541},"  @media",[290,26111,3595],{"class":295},[290,26113,26114],{"class":461},"min-width",[290,26116,465],{"class":295},[290,26118,26119],{"class":461},"700",[290,26121,674],{"class":541},[290,26123,646],{"class":295},[290,26125,26126,26129,26131,26133],{"class":163,"line":386},[290,26127,26128],{"class":303},"    .card",[290,26130,790],{"class":295},[290,26132,25355],{"class":461},[290,26134,26135],{"class":295},": card-slide; }\n",[290,26137,26138],{"class":163,"line":408},[290,26139,771],{"class":295},[290,26141,26142],{"class":163,"line":428},[290,26143,620],{"class":295},[290,26145,26146],{"class":163,"line":517},[290,26147,334],{"emptyLinePlaceholder":333},[290,26149,26150],{"class":163,"line":523},[290,26151,26152],{"class":455},"\u002F* With support, the named query is authoritative regardless of viewport. *\u002F\n",[290,26154,26155,26157,26159,26161],{"class":163,"line":532},[290,26156,2086],{"class":541},[290,26158,3595],{"class":295},[290,26160,24401],{"class":461},[290,26162,26104],{"class":295},[290,26164,26165,26168,26170,26173,26176],{"class":163,"line":551},[290,26166,26167],{"class":303},"  .slot",[290,26169,790],{"class":295},[290,26171,26172],{"class":461},"container",[290,26174,26175],{"class":295},": slot \u002F inline-size; } ",[290,26177,26178],{"class":455},"\u002F* shorthand: name \u002F type *\u002F\n",[290,26180,26181],{"class":163,"line":586},[290,26182,620],{"class":295},[14,26184,26185,26186,26190],{},"This mirrors the progressive-enhancement approach in the ",[27,26187,26189],{"href":26188},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fhandling-container-query-fallbacks-for-older-browsers\u002F","handling container query fallbacks for older browsers"," guide: detect support, prefer the container query, and degrade to a viewport breakpoint only where necessary.",[47,26192],{},[50,26194,2248],{"id":2247},[14,26196,26197,26198,19562,26201,26204,26205,26208,26209,8393,26211,26213],{},"Size container queries are baseline-supported: Chrome\u002FEdge 105+, Safari 16+, and Firefox 110+, available across the board since early 2023. Style queries for ",[86,26199,26200],{},"custom properties",[18,26202,26203],{},"@container style(--state: …)"," form used above — landed later: Chrome\u002FEdge 111+ and Safari 18+, with Firefox support arriving more recently; use ",[18,26206,26207],{},"@supports (container-type: inline-size)"," to gate container-query CSS, and treat the style-query pulse as an enhancement that simply does nothing where unsupported. The keyframes, ",[18,26210,103],{},[18,26212,2584],{}," pieces are universally supported.",[47,26215],{},[50,26217,1316],{"id":1315},[14,26219,26220,26223,26224,26226],{},[62,26221,26222],{},"Can @container change which keyframe animation runs?","\nYes. A size container query can set a different ",[18,26225,25355],{}," (or different animation properties) inside its rule block, so the same component runs one animation when narrow and another when wide, with no JavaScript.",[14,26228,26229,26232,26233,26235,26236,26238],{},[62,26230,26231],{},"Why does my @container animation never trigger?","\nAlmost always because no ancestor has ",[18,26234,24401],{}," set. A component cannot query itself; the parent must declare ",[18,26237,25409],{}," so the child can react to its width.",[14,26240,26241,26244,26245,26248,26249,26252],{},[62,26242,26243],{},"What is a style query and how does it differ from a size query?","\nA size query (",[18,26246,26247],{},"@container (min-width)",") reacts to the container's measured dimensions. A style query (",[18,26250,26251],{},"@container style(--var: value)",") reacts to the computed value of a custom property on the container, letting you switch animations by state rather than size.",[14,26254,26255,26258,26259,26261],{},[62,26256,26257],{},"Do container-query animations respect reduced motion?","\nOnly if you write it. Container queries select which animation runs; you still need a ",[18,26260,5557],{}," block to disable or simplify motion regardless of container size.",[47,26263],{},[50,26265,1391],{"id":1390},[1393,26267,26268,26273,26280,26286,26292],{},[1396,26269,26270,26272],{},[27,26271,1420],{"href":1419}," — the parent guide to CSS animation patterns.",[1396,26274,26275,26279],{},[27,26276,26278],{"href":26277},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fstaggered-list-animations-with-custom-properties\u002F","Staggered List Animations with Custom Properties"," — sibling pattern combining well with per-container entrances.",[1396,26281,26282,26285],{},[27,26283,26284],{"href":25340},"Container Query Syntax Basics"," — the core @container mechanics this page builds on.",[1396,26287,26288,26291],{},[27,26289,26290],{"href":11248},"Container Query Units cqi and cqb Explained"," — sizing inside the same containers you query.",[1396,26293,26294,26297],{},[27,26295,26296],{"href":26188},"Handling Container Query Fallbacks for Older Browsers"," — the progressive-enhancement pattern used in the variation.",[1430,26299,26300],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":286,"searchDepth":330,"depth":330,"links":26302},[26303,26304,26305,26306,26307,26308,26309],{"id":25370,"depth":330,"text":25371},{"id":4202,"depth":330,"text":25403},{"id":26018,"depth":330,"text":26019},{"id":26065,"depth":330,"text":26066},{"id":2247,"depth":330,"text":2248},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Switch which @keyframes animation a component runs based on its container size using @container size and style queries. No JavaScript, with reduced-motion safety.",{"seoTitle":26312,"datePublished":1447,"dateModified":1447,"faq":26313},"Container-Query-Triggered Animations",[26314,26316,26318,26320],{"q":26222,"a":26315},"Yes. A size container query can set a different animation-name (or different animation properties) inside its rule block, so the same component runs one animation when narrow and another when wide, with no JavaScript.",{"q":26231,"a":26317},"Almost always because no ancestor has container-type set. A component cannot query itself; the parent must declare container-type: inline-size so the child can react to its width.",{"q":26243,"a":26319},"A size query (@container (min-width)) reacts to the container's measured dimensions. A style query (@container style(--var: value)) reacts to the computed value of a custom property on the container, letting you switch animations by state rather than size.",{"q":26257,"a":26321},"Only if you write it. Container queries select which animation runs; you still need a prefers-reduced-motion: reduce block to disable or simplify motion regardless of container size.","\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcontainer-query-triggered-keyframe-animations",{"title":25312,"description":26310},"css-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcontainer-query-triggered-keyframe-animations\u002Findex","gjkckKHA3BMakh8xQl8D-aPpyF7stOYZajSJepXVdYo",{"id":26327,"title":26328,"body":26329,"description":27788,"extension":1444,"meta":27789,"navigation":333,"path":27800,"seo":27801,"stem":27802,"__hash__":27803},"content\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-loading-spinners-and-skeletons\u002Findex.md","CSS-Only Loading Spinners and Skeleton Shimmer Screens with @keyframes",{"type":7,"value":26330,"toc":27779},[26331,26334,26343,26348,26373,26375,26379,26392,26403,26405,26407,26418,26468,26619,27243,27253,27255,27259,27282,27284,27288,27291,27662,27673,27675,27677,27701,27703,27705,27717,27726,27732,27745,27747,27749,27777],[10,26332,26328],{"id":26333},"css-only-loading-spinners-and-skeleton-shimmer-screens-with-keyframes",[14,26335,26336,26337,26339,26340,26342],{},"A loading state is the first thing a user sees and the last thing most teams polish. The narrow problem this guide solves: render a smooth rotating spinner and a content-shaped skeleton shimmer using nothing but markup and ",[18,26338,3968],{},", keep both pinned to the compositor thread so they hold 60fps, and degrade to a calm static state when the visitor has asked the operating system to reduce motion. This page sits within the broader ",[27,26341,25336],{"href":1419}," guide, which covers the family of repeating animations these loaders belong to.",[14,26344,26345],{},[62,26346,26347],{},"What you will build:",[1393,26349,26350,26356,26365,26370],{},[1396,26351,26352,26353,26355],{},"An indeterminate spinner that rotates an arc with ",[18,26354,103],{}," only.",[1396,26357,26358,26359,26362,26363,42],{},"A skeleton block whose shimmer is a moving ",[18,26360,26361],{},"background-position",", swept by ",[18,26364,3968],{},[1396,26366,7081,26367,26369],{},[18,26368,2584],{}," path that strips all motion without breaking layout.",[1396,26371,26372],{},"Accessible announcements so screen readers know the page is busy.",[47,26374],{},[50,26376,26378],{"id":26377},"why-css-instead-of-a-javascript-loader","Why CSS Instead of a JavaScript Loader",[14,26380,26381,26382,26385,26386,26388,26389,26391],{},"A loading indicator runs ",[86,26383,26384],{},"while the main thread is busy"," fetching, parsing, and hydrating. That is precisely the moment a JavaScript-driven animation will jank, because the script driving it is competing for the same thread that is blocked on work. A CSS animation declared with ",[18,26387,3968],{}," is handed to the browser's compositor and keeps ticking even when the main thread is saturated, which is the whole reason a CSS spinner stays smooth during a heavy page load while a ",[18,26390,13074],{}," loop stalls.",[14,26393,26394,26395,26398,26399,26402],{},"The tradeoff is control. CSS cannot read fetch progress, so a CSS loader is inherently ",[86,26396,26397],{},"indeterminate"," — it communicates \"something is happening\", not \"47% complete\". For determinate progress bars you still need a sliver of script to set a width or a custom property. For the common case of \"show motion until content arrives\", CSS wins on smoothness, payload, and resilience. The accessibility cost is small but real: because CSS only paints, you must supply the semantics yourself with ",[18,26400,26401],{},"role=\"status\""," and an accessible name, and you must honour reduced-motion preferences explicitly rather than relying on a library default.",[47,26404],{},[50,26406,25403],{"id":4202},[14,26408,26409,26410,26413,26414,26417],{},"The following is self-contained. The spinner rotates a single element whose border forms an arc; the skeleton is a stack of blocks with a sweeping highlight. Both motion paths are gated behind ",[18,26411,26412],{},"prefers-reduced-motion: no-preference"," so the ",[86,26415,26416],{},"default"," render is static.",[133,26419,140,26421,140,26424,140,26427,140,26431,140,26434,140,26438,140,26441,140,26444,140,26448,140,26452,140,26454,140,26457,140,26460,140,26464],{"viewBox":2588,"role":136,"ariaLabel":26420,"xmlns":138,"style":139},"A rotating spinner arc next to three skeleton bars with a sweeping shimmer highlight",[142,26422,26423],{},"Spinner arc and skeleton shimmer",[146,26425,26426],{},"Left: a circle with one quarter drawn as a coloured arc that rotates. Right: three grey bars with a diagonal highlight band sweeping left to right.",[150,26428,26430],{"x":1787,"y":4146,"style":26429},"text-anchor:middle;fill:currentColor;font:700 16px sans-serif","Spinner (rotate)",[26432,26433],"circle",{"cx":1787,"cy":1830,"r":1786,"fill":72,"stroke":167,"strokeWidth":836,"opacity":566},[253,26435],{"d":26436,"fill":72,"stroke":177,"strokeWidth":836,"strokeLineCap":26437},"M150 100 A60 60 0 0 1 210 160","round",[253,26439],{"d":26440,"fill":177},"M214 150 l8 10 -10 8 z",[150,26442,26443],{"x":1787,"y":3112,"style":10248},"transform: rotate(360deg)",[150,26445,26447],{"x":26446,"y":4146,"style":26429},"490","Skeleton (shimmer)",[171,26449],{"x":2613,"y":9105,"width":26450,"height":26451,"rx":5901,"fill":167,"opacity":22004},"340","26",[171,26453],{"x":2613,"y":181,"width":26450,"height":26451,"rx":5901,"fill":167,"opacity":22004},[171,26455],{"x":2613,"y":26456,"width":538,"height":26451,"rx":5901,"fill":167,"opacity":22004},"168",[171,26458],{"x":26459,"y":5892,"width":5139,"height":182,"fill":177,"opacity":199},"430",[150,26461,26463],{"x":26462,"y":6729,"style":10248},"454","background-position sweep",[150,26465,26467],{"x":26446,"y":26466,"style":1839},"258","highlight moves left to right",[281,26469,26471],{"className":283,"code":26470,"language":285,"meta":286,"style":286},"\u003C!-- Indeterminate spinner -->\n\u003Cdiv class=\"spinner\" role=\"status\" aria-label=\"Loading\">\u003C\u002Fdiv>\n\n\u003C!-- Skeleton placeholder for a text card -->\n\u003Cdiv class=\"skeleton-card\" role=\"status\" aria-label=\"Loading content\" aria-busy=\"true\">\n  \u003Cdiv class=\"skeleton skeleton--line\">\u003C\u002Fdiv>\n  \u003Cdiv class=\"skeleton skeleton--line\">\u003C\u002Fdiv>\n  \u003Cdiv class=\"skeleton skeleton--line skeleton--short\">\u003C\u002Fdiv>\n\u003C\u002Fdiv>\n",[18,26472,26473,26478,26511,26515,26520,26555,26574,26592,26611],{"__ignoreMap":286},[290,26474,26475],{"class":163,"line":292},[290,26476,26477],{"class":455},"\u003C!-- Indeterminate spinner -->\n",[290,26479,26480,26482,26484,26486,26488,26491,26493,26495,26498,26500,26502,26505,26507,26509],{"class":163,"line":330},[290,26481,296],{"class":295},[290,26483,342],{"class":299},[290,26485,314],{"class":303},[290,26487,307],{"class":295},[290,26489,26490],{"class":310},"\"spinner\"",[290,26492,17652],{"class":303},[290,26494,307],{"class":295},[290,26496,26497],{"class":310},"\"status\"",[290,26499,19002],{"class":303},[290,26501,307],{"class":295},[290,26503,26504],{"class":310},"\"Loading\"",[290,26506,11472],{"class":295},[290,26508,342],{"class":299},[290,26510,327],{"class":295},[290,26512,26513],{"class":163,"line":337},[290,26514,334],{"emptyLinePlaceholder":333},[290,26516,26517],{"class":163,"line":364},[290,26518,26519],{"class":455},"\u003C!-- Skeleton placeholder for a text card -->\n",[290,26521,26522,26524,26526,26528,26530,26533,26535,26537,26539,26541,26543,26546,26549,26551,26553],{"class":163,"line":386},[290,26523,296],{"class":295},[290,26525,342],{"class":299},[290,26527,314],{"class":303},[290,26529,307],{"class":295},[290,26531,26532],{"class":310},"\"skeleton-card\"",[290,26534,17652],{"class":303},[290,26536,307],{"class":295},[290,26538,26497],{"class":310},[290,26540,19002],{"class":303},[290,26542,307],{"class":295},[290,26544,26545],{"class":310},"\"Loading content\"",[290,26547,26548],{"class":303}," aria-busy",[290,26550,307],{"class":295},[290,26552,6406],{"class":310},[290,26554,327],{"class":295},[290,26556,26557,26559,26561,26563,26565,26568,26570,26572],{"class":163,"line":408},[290,26558,367],{"class":295},[290,26560,342],{"class":299},[290,26562,314],{"class":303},[290,26564,307],{"class":295},[290,26566,26567],{"class":310},"\"skeleton skeleton--line\"",[290,26569,11472],{"class":295},[290,26571,342],{"class":299},[290,26573,327],{"class":295},[290,26575,26576,26578,26580,26582,26584,26586,26588,26590],{"class":163,"line":428},[290,26577,367],{"class":295},[290,26579,342],{"class":299},[290,26581,314],{"class":303},[290,26583,307],{"class":295},[290,26585,26567],{"class":310},[290,26587,11472],{"class":295},[290,26589,342],{"class":299},[290,26591,327],{"class":295},[290,26593,26594,26596,26598,26600,26602,26605,26607,26609],{"class":163,"line":517},[290,26595,367],{"class":295},[290,26597,342],{"class":299},[290,26599,314],{"class":303},[290,26601,307],{"class":295},[290,26603,26604],{"class":310},"\"skeleton skeleton--line skeleton--short\"",[290,26606,11472],{"class":295},[290,26608,342],{"class":299},[290,26610,327],{"class":295},[290,26612,26613,26615,26617],{"class":163,"line":523},[290,26614,431],{"class":295},[290,26616,342],{"class":299},[290,26618,327],{"class":295},[281,26620,26622],{"className":438,"code":26621,"language":440,"meta":286,"style":286},"\u002F* ---- Spinner: a ring with one coloured quarter, rotated ---- *\u002F\n.spinner {\n  width: 40px;\n  height: 40px;\n  border-radius: 50%;\n  \u002F* Full ring is faint; the top border is the bright arc. *\u002F\n  border: 4px solid color-mix(in srgb, currentColor 18%, transparent);\n  border-top-color: #3b82f6;\n  \u002F* Only transform is animated, so this runs on the compositor. *\u002F\n  animation: spinner-rotate 0.8s linear infinite;\n}\n\n@keyframes spinner-rotate {\n  to { transform: rotate(360deg); }\n}\n\n\u002F* ---- Skeleton: grey blocks with a sweeping highlight ---- *\u002F\n.skeleton-card {\n  display: grid;\n  gap: 12px;\n  max-width: 360px;\n  padding: 16px;\n}\n\n.skeleton {\n  height: 16px;\n  border-radius: 6px;\n  \u002F* Base grey + a diagonal highlight band that we slide across. *\u002F\n  background:\n    linear-gradient(\n      100deg,\n      #e2e8f0 30%,\n      #f1f5f9 50%,\n      #e2e8f0 70%\n    );\n  \u002F* The gradient is wider than the box so there is room to slide. *\u002F\n  background-size: 200% 100%;\n}\n\n.skeleton--line { width: 100%; }\n.skeleton--short { width: 60%; }\n\n\u002F* Default (no animation request) still gets a calm pulse-free sweep\n   ONLY when motion is allowed. The static base above is the fallback. *\u002F\n@media (prefers-reduced-motion: no-preference) {\n  .skeleton {\n    animation: skeleton-shimmer 1.4s ease-in-out infinite;\n  }\n}\n\n@keyframes skeleton-shimmer {\n  \u002F* Slide the oversized gradient from right to left. *\u002F\n  from { background-position: 100% 0; }\n  to   { background-position: -100% 0; }\n}\n\n\u002F* ---- Reduced-motion: freeze everything, keep the shapes ---- *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .spinner {\n    \u002F* Replace rotation with a static dashed ring so the state is\n       still legible as \"busy\" without continuous motion. *\u002F\n    animation: none;\n    border-top-color: currentColor;\n    border-style: dashed;\n  }\n  .skeleton {\n    animation: none;\n    background: #e2e8f0; \u002F* flat grey, no sweep *\u002F\n  }\n}\n",[18,26623,26624,26629,26636,26648,26660,26672,26677,26714,26725,26730,26747,26751,26755,26764,26785,26789,26793,26798,26805,26815,26828,26840,26852,26856,26860,26867,26879,26891,26896,26902,26908,26917,26928,26940,26950,26955,26960,26977,26981,26985,27002,27019,27023,27028,27033,27039,27046,27063,27067,27071,27075,27084,27089,27107,27126,27130,27134,27139,27145,27152,27157,27162,27172,27183,27195,27200,27207,27218,27233,27238],{"__ignoreMap":286},[290,26625,26626],{"class":163,"line":292},[290,26627,26628],{"class":455},"\u002F* ---- Spinner: a ring with one coloured quarter, rotated ---- *\u002F\n",[290,26630,26631,26634],{"class":163,"line":330},[290,26632,26633],{"class":303},".spinner",[290,26635,450],{"class":295},[290,26637,26638,26640,26642,26644,26646],{"class":163,"line":337},[290,26639,17904],{"class":461},[290,26641,465],{"class":295},[290,26643,4146],{"class":461},[290,26645,674],{"class":541},[290,26647,471],{"class":295},[290,26649,26650,26652,26654,26656,26658],{"class":163,"line":364},[290,26651,18162],{"class":461},[290,26653,465],{"class":295},[290,26655,4146],{"class":461},[290,26657,674],{"class":541},[290,26659,471],{"class":295},[290,26661,26662,26664,26666,26668,26670],{"class":163,"line":386},[290,26663,1663],{"class":461},[290,26665,465],{"class":295},[290,26667,1831],{"class":461},[290,26669,11018],{"class":541},[290,26671,471],{"class":295},[290,26673,26674],{"class":163,"line":408},[290,26675,26676],{"class":455},"  \u002F* Full ring is faint; the top border is the bright arc. *\u002F\n",[290,26678,26679,26681,26683,26685,26687,26689,26692,26694,26697,26700,26702,26704,26706,26708,26710,26712],{"class":163,"line":428},[290,26680,1948],{"class":461},[290,26682,465],{"class":295},[290,26684,249],{"class":461},[290,26686,674],{"class":541},[290,26688,852],{"class":461},[290,26690,26691],{"class":461}," color-mix",[290,26693,484],{"class":295},[290,26695,26696],{"class":1561},"in",[290,26698,26699],{"class":461}," srgb",[290,26701,569],{"class":295},[290,26703,167],{"class":461},[290,26705,14607],{"class":461},[290,26707,11018],{"class":541},[290,26709,569],{"class":295},[290,26711,14187],{"class":461},[290,26713,500],{"class":295},[290,26715,26716,26719,26721,26723],{"class":163,"line":517},[290,26717,26718],{"class":461},"  border-top-color",[290,26720,465],{"class":295},[290,26722,24267],{"class":461},[290,26724,471],{"class":295},[290,26726,26727],{"class":163,"line":523},[290,26728,26729],{"class":455},"  \u002F* Only transform is animated, so this runs on the compositor. *\u002F\n",[290,26731,26732,26734,26737,26739,26741,26743,26745],{"class":163,"line":532},[290,26733,6035],{"class":461},[290,26735,26736],{"class":295},": spinner-rotate ",[290,26738,572],{"class":461},[290,26740,1886],{"class":541},[290,26742,3159],{"class":461},[290,26744,21326],{"class":461},[290,26746,471],{"class":295},[290,26748,26749],{"class":163,"line":551},[290,26750,620],{"class":295},[290,26752,26753],{"class":163,"line":586},[290,26754,334],{"emptyLinePlaceholder":333},[290,26756,26757,26759,26762],{"class":163,"line":602},[290,26758,3968],{"class":541},[290,26760,26761],{"class":1561}," spinner-rotate",[290,26763,450],{"class":295},[290,26765,26766,26768,26770,26772,26774,26777,26779,26781,26783],{"class":163,"line":617},[290,26767,6072],{"class":303},[290,26769,790],{"class":295},[290,26771,103],{"class":461},[290,26773,465],{"class":295},[290,26775,26776],{"class":461},"rotate",[290,26778,484],{"class":295},[290,26780,152],{"class":461},[290,26782,10416],{"class":541},[290,26784,1122],{"class":295},[290,26786,26787],{"class":163,"line":623},[290,26788,620],{"class":295},[290,26790,26791],{"class":163,"line":628},[290,26792,334],{"emptyLinePlaceholder":333},[290,26794,26795],{"class":163,"line":634},[290,26796,26797],{"class":455},"\u002F* ---- Skeleton: grey blocks with a sweeping highlight ---- *\u002F\n",[290,26799,26800,26803],{"class":163,"line":649},[290,26801,26802],{"class":303},".skeleton-card",[290,26804,450],{"class":295},[290,26806,26807,26809,26811,26813],{"class":163,"line":660},[290,26808,17742],{"class":461},[290,26810,465],{"class":295},[290,26812,9147],{"class":461},[290,26814,471],{"class":295},[290,26816,26817,26820,26822,26824,26826],{"class":163,"line":688},[290,26818,26819],{"class":461},"  gap",[290,26821,465],{"class":295},[290,26823,5894],{"class":461},[290,26825,674],{"class":541},[290,26827,471],{"class":295},[290,26829,26830,26832,26834,26836,26838],{"class":163,"line":693},[290,26831,17916],{"class":461},[290,26833,465],{"class":295},[290,26835,152],{"class":461},[290,26837,674],{"class":541},[290,26839,471],{"class":295},[290,26841,26842,26844,26846,26848,26850],{"class":163,"line":698},[290,26843,10433],{"class":461},[290,26845,465],{"class":295},[290,26847,9212],{"class":461},[290,26849,674],{"class":541},[290,26851,471],{"class":295},[290,26853,26854],{"class":163,"line":704},[290,26855,620],{"class":295},[290,26857,26858],{"class":163,"line":710},[290,26859,334],{"emptyLinePlaceholder":333},[290,26861,26862,26865],{"class":163,"line":717},[290,26863,26864],{"class":303},".skeleton",[290,26866,450],{"class":295},[290,26868,26869,26871,26873,26875,26877],{"class":163,"line":730},[290,26870,18162],{"class":461},[290,26872,465],{"class":295},[290,26874,9212],{"class":461},[290,26876,674],{"class":541},[290,26878,471],{"class":295},[290,26880,26881,26883,26885,26887,26889],{"class":163,"line":742},[290,26882,1663],{"class":461},[290,26884,465],{"class":295},[290,26886,5901],{"class":461},[290,26888,674],{"class":541},[290,26890,471],{"class":295},[290,26892,26893],{"class":163,"line":768},[290,26894,26895],{"class":455},"  \u002F* Base grey + a diagonal highlight band that we slide across. *\u002F\n",[290,26897,26898,26900],{"class":163,"line":774},[290,26899,1186],{"class":461},[290,26901,529],{"class":295},[290,26903,26904,26906],{"class":163,"line":779},[290,26905,10666],{"class":461},[290,26907,7322],{"class":295},[290,26909,26910,26913,26915],{"class":163,"line":784},[290,26911,26912],{"class":461},"      100",[290,26914,10416],{"class":541},[290,26916,548],{"class":295},[290,26918,26919,26922,26924,26926],{"class":163,"line":812},[290,26920,26921],{"class":461},"      #e2e8f0",[290,26923,14617],{"class":461},[290,26925,11018],{"class":541},[290,26927,548],{"class":295},[290,26929,26930,26933,26936,26938],{"class":163,"line":860},[290,26931,26932],{"class":461},"      #f1f5f9",[290,26934,26935],{"class":461}," 50",[290,26937,11018],{"class":541},[290,26939,548],{"class":295},[290,26941,26942,26944,26947],{"class":163,"line":865},[290,26943,26921],{"class":461},[290,26945,26946],{"class":461}," 70",[290,26948,26949],{"class":541},"%\n",[290,26951,26952],{"class":163,"line":871},[290,26953,26954],{"class":295},"    );\n",[290,26956,26957],{"class":163,"line":880},[290,26958,26959],{"class":455},"  \u002F* The gradient is wider than the box so there is room to slide. *\u002F\n",[290,26961,26962,26965,26967,26969,26971,26973,26975],{"class":163,"line":896},[290,26963,26964],{"class":461},"  background-size",[290,26966,465],{"class":295},[290,26968,174],{"class":461},[290,26970,11018],{"class":541},[290,26972,11575],{"class":461},[290,26974,11018],{"class":541},[290,26976,471],{"class":295},[290,26978,26979],{"class":163,"line":4734},[290,26980,620],{"class":295},[290,26982,26983],{"class":163,"line":4742},[290,26984,334],{"emptyLinePlaceholder":333},[290,26986,26987,26990,26992,26994,26996,26998,27000],{"class":163,"line":4761},[290,26988,26989],{"class":303},".skeleton--line",[290,26991,790],{"class":295},[290,26993,1748],{"class":461},[290,26995,465],{"class":295},[290,26997,165],{"class":461},[290,26999,11018],{"class":541},[290,27001,809],{"class":295},[290,27003,27004,27007,27009,27011,27013,27015,27017],{"class":163,"line":4766},[290,27005,27006],{"class":303},".skeleton--short",[290,27008,790],{"class":295},[290,27010,1748],{"class":461},[290,27012,465],{"class":295},[290,27014,1786],{"class":461},[290,27016,11018],{"class":541},[290,27018,809],{"class":295},[290,27020,27021],{"class":163,"line":4786},[290,27022,334],{"emptyLinePlaceholder":333},[290,27024,27025],{"class":163,"line":9635},[290,27026,27027],{"class":455},"\u002F* Default (no animation request) still gets a calm pulse-free sweep\n",[290,27029,27030],{"class":163,"line":9641},[290,27031,27032],{"class":455},"   ONLY when motion is allowed. The static base above is the fallback. *\u002F\n",[290,27034,27035,27037],{"class":163,"line":9647},[290,27036,874],{"class":541},[290,27038,16018],{"class":295},[290,27040,27041,27044],{"class":163,"line":9665},[290,27042,27043],{"class":303},"  .skeleton",[290,27045,450],{"class":295},[290,27047,27048,27050,27053,27055,27057,27059,27061],{"class":163,"line":9683},[290,27049,6152],{"class":461},[290,27051,27052],{"class":295},": skeleton-shimmer ",[290,27054,1824],{"class":461},[290,27056,1886],{"class":541},[290,27058,21329],{"class":461},[290,27060,21326],{"class":461},[290,27062,471],{"class":295},[290,27064,27065],{"class":163,"line":9688},[290,27066,771],{"class":295},[290,27068,27069],{"class":163,"line":9694},[290,27070,620],{"class":295},[290,27072,27073],{"class":163,"line":9700},[290,27074,334],{"emptyLinePlaceholder":333},[290,27076,27077,27079,27082],{"class":163,"line":9710},[290,27078,3968],{"class":541},[290,27080,27081],{"class":1561}," skeleton-shimmer",[290,27083,450],{"class":295},[290,27085,27086],{"class":163,"line":9716},[290,27087,27088],{"class":455},"  \u002F* Slide the oversized gradient from right to left. *\u002F\n",[290,27090,27091,27093,27095,27097,27099,27101,27103,27105],{"class":163,"line":9738},[290,27092,6191],{"class":303},[290,27094,790],{"class":295},[290,27096,26361],{"class":461},[290,27098,465],{"class":295},[290,27100,165],{"class":461},[290,27102,11018],{"class":541},[290,27104,1198],{"class":461},[290,27106,809],{"class":295},[290,27108,27109,27111,27113,27115,27117,27120,27122,27124],{"class":163,"line":9748},[290,27110,6072],{"class":303},[290,27112,6208],{"class":295},[290,27114,26361],{"class":461},[290,27116,465],{"class":295},[290,27118,27119],{"class":461},"-100",[290,27121,11018],{"class":541},[290,27123,1198],{"class":461},[290,27125,809],{"class":295},[290,27127,27128],{"class":163,"line":9754},[290,27129,620],{"class":295},[290,27131,27132],{"class":163,"line":9760},[290,27133,334],{"emptyLinePlaceholder":333},[290,27135,27136],{"class":163,"line":9777},[290,27137,27138],{"class":455},"\u002F* ---- Reduced-motion: freeze everything, keep the shapes ---- *\u002F\n",[290,27140,27141,27143],{"class":163,"line":9787},[290,27142,874],{"class":541},[290,27144,877],{"class":295},[290,27146,27147,27150],{"class":163,"line":9793},[290,27148,27149],{"class":303},"  .spinner",[290,27151,450],{"class":295},[290,27153,27154],{"class":163,"line":9799},[290,27155,27156],{"class":455},"    \u002F* Replace rotation with a static dashed ring so the state is\n",[290,27158,27159],{"class":163,"line":9805},[290,27160,27161],{"class":455},"       still legible as \"busy\" without continuous motion. *\u002F\n",[290,27163,27164,27166,27168,27170],{"class":163,"line":9811},[290,27165,6152],{"class":461},[290,27167,465],{"class":295},[290,27169,72],{"class":461},[290,27171,471],{"class":295},[290,27173,27174,27177,27179,27181],{"class":163,"line":9820},[290,27175,27176],{"class":461},"    border-top-color",[290,27178,465],{"class":295},[290,27180,167],{"class":461},[290,27182,471],{"class":295},[290,27184,27185,27188,27190,27193],{"class":163,"line":9829},[290,27186,27187],{"class":461},"    border-style",[290,27189,465],{"class":295},[290,27191,27192],{"class":461},"dashed",[290,27194,471],{"class":295},[290,27196,27198],{"class":163,"line":27197},65,[290,27199,771],{"class":295},[290,27201,27203,27205],{"class":163,"line":27202},66,[290,27204,27043],{"class":303},[290,27206,450],{"class":295},[290,27208,27210,27212,27214,27216],{"class":163,"line":27209},67,[290,27211,6152],{"class":461},[290,27213,465],{"class":295},[290,27215,72],{"class":461},[290,27217,471],{"class":295},[290,27219,27221,27223,27225,27228,27230],{"class":163,"line":27220},68,[290,27222,9124],{"class":461},[290,27224,465],{"class":295},[290,27226,27227],{"class":461},"#e2e8f0",[290,27229,828],{"class":295},[290,27231,27232],{"class":455},"\u002F* flat grey, no sweep *\u002F\n",[290,27234,27236],{"class":163,"line":27235},69,[290,27237,771],{"class":295},[290,27239,27241],{"class":163,"line":27240},70,[290,27242,620],{"class":295},[14,27244,27245,27246,27249,27250,27252],{},"The spinner never touches layout: the only animated declaration is ",[18,27247,27248],{},"transform: rotate()",", and rotation is a compositor-only operation. The skeleton animates ",[18,27251,26361],{},", which paints but does not reflow, so a long list of skeleton rows stays cheap even with dozens on screen.",[47,27254],{},[50,27256,27258],{"id":27257},"the-key-technique-an-oversized-gradient-sliding-behind-a-fixed-box","The Key Technique: An Oversized Gradient Sliding Behind a Fixed Box",[14,27260,27261,27262,27265,27266,27269,27270,24192,27272,80,27275,27278,27279,27281],{},"The shimmer illusion has no moving DOM at all. The trick is ",[18,27263,27264],{},"background-size: 200% 100%"," — the gradient is painted twice as wide as the element. That extra width is the runway. ",[18,27267,27268],{},"@keyframes skeleton-shimmer"," then animates ",[18,27271,26361],{},[18,27273,27274],{},"100% 0",[18,27276,27277],{},"-100% 0",", dragging the bright middle stop across the visible window. Because the box itself never resizes and no child elements move, the browser only repaints the element's own pixels; there is no layout pass and no effect on neighbours. Building the highlight as a three-stop ",[18,27280,13130],{}," (dim → bright → dim) means the bright band has soft edges, which reads as a polished sweep rather than a hard bar.",[47,27283],{},[50,27285,27287],{"id":27286},"variation-dark-mode-and-a-dots-loader","Variation: Dark Mode and a Dots Loader",[14,27289,27290],{},"Skeletons must match the surface they sit on, so swap the gradient stops under a dark scheme, and offer a non-rotational loader for layouts where a spinning ring feels heavy.",[281,27292,27294],{"className":438,"code":27293,"language":440,"meta":286,"style":286},"@media (prefers-color-scheme: dark) {\n  .skeleton {\n    background: linear-gradient(\n      100deg,\n      #1e293b 30%,\n      #334155 50%,\n      #1e293b 70%\n    );\n    background-size: 200% 100%;\n  }\n}\n\n\u002F* Three pulsing dots: opacity-only, staggered with negative delay. *\u002F\n.dots { display: inline-flex; gap: 6px; }\n.dots span {\n  width: 8px; height: 8px; border-radius: 50%;\n  background: currentColor;\n  animation: dot-pulse 1.2s ease-in-out infinite;\n}\n.dots span:nth-child(2) { animation-delay: 0.2s; }\n.dots span:nth-child(3) { animation-delay: 0.4s; }\n\n@keyframes dot-pulse {\n  0%, 80%, 100% { opacity: 0.25; transform: scale(0.8); }\n  40%           { opacity: 1;    transform: scale(1); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .dots span { animation: none; opacity: 0.6; }\n}\n",[18,27295,27296,27302,27308,27318,27326,27337,27348,27356,27360,27377,27381,27385,27389,27394,27420,27429,27461,27471,27488,27492,27518,27542,27546,27555,27590,27619,27623,27627,27633,27658],{"__ignoreMap":286},[290,27297,27298,27300],{"class":163,"line":292},[290,27299,874],{"class":541},[290,27301,12872],{"class":295},[290,27303,27304,27306],{"class":163,"line":330},[290,27305,27043],{"class":303},[290,27307,450],{"class":295},[290,27309,27310,27312,27314,27316],{"class":163,"line":337},[290,27311,9124],{"class":461},[290,27313,465],{"class":295},[290,27315,13130],{"class":461},[290,27317,7322],{"class":295},[290,27319,27320,27322,27324],{"class":163,"line":364},[290,27321,26912],{"class":461},[290,27323,10416],{"class":541},[290,27325,548],{"class":295},[290,27327,27328,27331,27333,27335],{"class":163,"line":386},[290,27329,27330],{"class":461},"      #1e293b",[290,27332,14617],{"class":461},[290,27334,11018],{"class":541},[290,27336,548],{"class":295},[290,27338,27339,27342,27344,27346],{"class":163,"line":408},[290,27340,27341],{"class":461},"      #334155",[290,27343,26935],{"class":461},[290,27345,11018],{"class":541},[290,27347,548],{"class":295},[290,27349,27350,27352,27354],{"class":163,"line":428},[290,27351,27330],{"class":461},[290,27353,26946],{"class":461},[290,27355,26949],{"class":541},[290,27357,27358],{"class":163,"line":517},[290,27359,26954],{"class":295},[290,27361,27362,27365,27367,27369,27371,27373,27375],{"class":163,"line":523},[290,27363,27364],{"class":461},"    background-size",[290,27366,465],{"class":295},[290,27368,174],{"class":461},[290,27370,11018],{"class":541},[290,27372,11575],{"class":461},[290,27374,11018],{"class":541},[290,27376,471],{"class":295},[290,27378,27379],{"class":163,"line":532},[290,27380,771],{"class":295},[290,27382,27383],{"class":163,"line":551},[290,27384,620],{"class":295},[290,27386,27387],{"class":163,"line":586},[290,27388,334],{"emptyLinePlaceholder":333},[290,27390,27391],{"class":163,"line":602},[290,27392,27393],{"class":455},"\u002F* Three pulsing dots: opacity-only, staggered with negative delay. *\u002F\n",[290,27395,27396,27399,27401,27403,27405,27408,27410,27412,27414,27416,27418],{"class":163,"line":617},[290,27397,27398],{"class":303},".dots",[290,27400,790],{"class":295},[290,27402,59],{"class":461},[290,27404,465],{"class":295},[290,27406,27407],{"class":461},"inline-flex",[290,27409,828],{"class":295},[290,27411,9070],{"class":461},[290,27413,465],{"class":295},[290,27415,5901],{"class":461},[290,27417,674],{"class":541},[290,27419,809],{"class":295},[290,27421,27422,27424,27427],{"class":163,"line":623},[290,27423,27398],{"class":303},[290,27425,27426],{"class":299}," span",[290,27428,450],{"class":295},[290,27430,27431,27433,27435,27437,27439,27441,27443,27445,27447,27449,27451,27453,27455,27457,27459],{"class":163,"line":628},[290,27432,17904],{"class":461},[290,27434,465],{"class":295},[290,27436,176],{"class":461},[290,27438,674],{"class":541},[290,27440,828],{"class":295},[290,27442,2722],{"class":461},[290,27444,465],{"class":295},[290,27446,176],{"class":461},[290,27448,674],{"class":541},[290,27450,828],{"class":295},[290,27452,831],{"class":461},[290,27454,465],{"class":295},[290,27456,1831],{"class":461},[290,27458,11018],{"class":541},[290,27460,471],{"class":295},[290,27462,27463,27465,27467,27469],{"class":163,"line":634},[290,27464,1186],{"class":461},[290,27466,465],{"class":295},[290,27468,167],{"class":461},[290,27470,471],{"class":295},[290,27472,27473,27475,27478,27480,27482,27484,27486],{"class":163,"line":649},[290,27474,6035],{"class":461},[290,27476,27477],{"class":295},": dot-pulse ",[290,27479,1789],{"class":461},[290,27481,1886],{"class":541},[290,27483,21329],{"class":461},[290,27485,21326],{"class":461},[290,27487,471],{"class":295},[290,27489,27490],{"class":163,"line":660},[290,27491,620],{"class":295},[290,27493,27494,27496,27498,27501,27503,27505,27508,27510,27512,27514,27516],{"class":163,"line":688},[290,27495,27398],{"class":303},[290,27497,27426],{"class":299},[290,27499,27500],{"class":303},":nth-child",[290,27502,484],{"class":295},[290,27504,194],{"class":461},[290,27506,27507],{"class":295},") { ",[290,27509,6980],{"class":461},[290,27511,465],{"class":295},[290,27513,566],{"class":461},[290,27515,1886],{"class":541},[290,27517,809],{"class":295},[290,27519,27520,27522,27524,27526,27528,27530,27532,27534,27536,27538,27540],{"class":163,"line":693},[290,27521,27398],{"class":303},[290,27523,27426],{"class":299},[290,27525,27500],{"class":303},[290,27527,484],{"class":295},[290,27529,1579],{"class":461},[290,27531,27507],{"class":295},[290,27533,6980],{"class":461},[290,27535,465],{"class":295},[290,27537,169],{"class":461},[290,27539,1886],{"class":541},[290,27541,809],{"class":295},[290,27543,27544],{"class":163,"line":698},[290,27545,334],{"emptyLinePlaceholder":333},[290,27547,27548,27550,27553],{"class":163,"line":704},[290,27549,3968],{"class":541},[290,27551,27552],{"class":1561}," dot-pulse",[290,27554,450],{"class":295},[290,27556,27557,27559,27561,27564,27566,27568,27570,27572,27574,27576,27578,27580,27582,27584,27586,27588],{"class":163,"line":710},[290,27558,21039],{"class":303},[290,27560,569],{"class":295},[290,27562,27563],{"class":303},"80%",[290,27565,569],{"class":295},[290,27567,25907],{"class":303},[290,27569,790],{"class":295},[290,27571,76],{"class":461},[290,27573,465],{"class":295},[290,27575,1668],{"class":461},[290,27577,828],{"class":295},[290,27579,103],{"class":461},[290,27581,465],{"class":295},[290,27583,493],{"class":461},[290,27585,484],{"class":295},[290,27587,572],{"class":461},[290,27589,1122],{"class":295},[290,27591,27592,27595,27598,27600,27602,27604,27607,27609,27611,27613,27615,27617],{"class":163,"line":717},[290,27593,27594],{"class":303},"  40%",[290,27596,27597],{"class":295},"           { ",[290,27599,76],{"class":461},[290,27601,465],{"class":295},[290,27603,468],{"class":461},[290,27605,27606],{"class":295},";    ",[290,27608,103],{"class":461},[290,27610,465],{"class":295},[290,27612,493],{"class":461},[290,27614,484],{"class":295},[290,27616,468],{"class":461},[290,27618,1122],{"class":295},[290,27620,27621],{"class":163,"line":730},[290,27622,620],{"class":295},[290,27624,27625],{"class":163,"line":742},[290,27626,334],{"emptyLinePlaceholder":333},[290,27628,27629,27631],{"class":163,"line":768},[290,27630,874],{"class":541},[290,27632,877],{"class":295},[290,27634,27635,27638,27640,27642,27644,27646,27648,27650,27652,27654,27656],{"class":163,"line":774},[290,27636,27637],{"class":303},"  .dots",[290,27639,27426],{"class":299},[290,27641,790],{"class":295},[290,27643,5178],{"class":461},[290,27645,465],{"class":295},[290,27647,72],{"class":461},[290,27649,828],{"class":295},[290,27651,76],{"class":461},[290,27653,465],{"class":295},[290,27655,232],{"class":461},[290,27657,809],{"class":295},[290,27659,27660],{"class":163,"line":779},[290,27661,620],{"class":295},[14,27663,27664,27665,69,27667,27669,27670,42],{},"The dots animate only ",[18,27666,76],{},[18,27668,103],{},", the two properties that stay on the compositor — the same discipline covered in the guide on ",[27,27671,27672],{"href":8756},"optimizing CSS animations for 60fps",[47,27674],{},[50,27676,2248],{"id":2247},[14,27678,27679,27680,569,27682,27684,27685,27687,27688,27690,27691,27693,27694,27697,27698,42],{},"Every property used here is broadly supported. ",[18,27681,3968],{},[18,27683,103],{},", and multi-stop ",[18,27686,13130],{}," backgrounds work in all browsers since well before Chrome\u002FEdge 80, Safari 13, and Firefox 75. ",[18,27689,2584],{}," has been honoured since Chrome 74, Safari 10.1, and Firefox 63, so no fallback is needed for the media query itself. The only modern syntax is ",[18,27692,2424],{}," in the spinner's faint ring, supported in Chrome\u002FEdge 111+, Safari 16.2+, and Firefox 113+; replace it with a plain ",[18,27695,27696],{},"rgba()"," value if you must support older engines via ",[18,27699,27700],{},"@supports (color: color-mix(in srgb, red, blue))",[47,27702],{},[50,27704,1316],{"id":1315},[14,27706,27707,27710,27711,27713,27714,27716],{},[62,27708,27709],{},"Why does my spinner stutter or drop frames?","\nIt is almost always because the keyframes animate a layout or paint property instead of ",[18,27712,103],{},". Rotate the element with ",[18,27715,27248],{}," and the animation runs on the compositor, staying smooth at 60fps even while the main thread is busy fetching.",[14,27718,27719,27722,27723,27725],{},[62,27720,27721],{},"How do I make a skeleton shimmer respect reduced motion?","\nWrap the animated background sweep in a ",[18,27724,26412],{}," media query, and provide a static dimmed placeholder as the default. Users who request reduced motion then see a calm grey block with no sweeping highlight.",[14,27727,27728,27731],{},[62,27729,27730],{},"Should I use a spinner or a skeleton screen?","\nUse a skeleton when you know the rough shape of the incoming content and the wait is short, because it reduces perceived latency. Use a spinner for indeterminate or whole-page waits where the layout is unknown.",[14,27733,27734,27737,27738,27740,27741,27744],{},[62,27735,27736],{},"Do I need aria attributes on a CSS-only loader?","\nYes. CSS only draws pixels, so add ",[18,27739,26401],{}," and an accessible name (or ",[18,27742,27743],{},"aria-busy"," on the region) so assistive technology announces that content is loading.",[47,27746],{},[50,27748,1391],{"id":1390},[1393,27750,27751,27756,27762,27767,27772],{},[1396,27752,27753,27755],{},[27,27754,1420],{"href":1419}," — the parent guide to repeating CSS animations.",[1396,27757,27758,27761],{},[27,27759,27760],{"href":23932},"CSS-Only Toggle Switches & Checkboxes"," — sibling pattern using state-driven transitions.",[1396,27763,27764,27766],{},[27,27765,2511],{"href":1412}," — the full reduced-motion strategy these loaders follow.",[1396,27768,27769,27771],{},[27,27770,8757],{"href":8756}," — why transform and opacity stay smooth under load.",[1396,27773,27774,27776],{},[27,27775,10100],{"href":1426}," — skeletons often fill cards whose size adapts per container.",[1430,27778,26300],{},{"title":286,"searchDepth":330,"depth":330,"links":27780},[27781,27782,27783,27784,27785,27786,27787],{"id":26377,"depth":330,"text":26378},{"id":4202,"depth":330,"text":25403},{"id":27257,"depth":330,"text":27258},{"id":27286,"depth":330,"text":27287},{"id":2247,"depth":330,"text":2248},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Build CSS-only loading spinners and skeleton shimmer screens with @keyframes, GPU-friendly transforms, and a prefers-reduced-motion fallback. No JavaScript.",{"seoTitle":27790,"datePublished":1447,"dateModified":1447,"faq":27791},"CSS-Only Loading Spinners & Skeletons",[27792,27794,27796,27798],{"q":27709,"a":27793},"It is almost always because the keyframes animate a layout or paint property instead of transform. Rotate the element with transform: rotate() and the animation runs on the compositor, staying smooth at 60fps.",{"q":27721,"a":27795},"Wrap the animated background sweep in a prefers-reduced-motion: no-preference media query, and provide a static dimmed placeholder as the default. Users who request reduced motion then see a calm grey block with no sweeping highlight.",{"q":27730,"a":27797},"Use a skeleton when you know the rough shape of the incoming content and the wait is short, because it reduces perceived latency. Use a spinner for indeterminate or whole-page waits where the layout is unknown.",{"q":27736,"a":27799},"Yes. CSS only draws pixels, so add role=status and aria-label (or aria-busy on the region) so assistive technology announces that content is loading.","\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-loading-spinners-and-skeletons",{"title":26328,"description":27788},"css-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-loading-spinners-and-skeletons\u002Findex","4_lExN_DIm7PLoEocugN8x_4UJKqxNWIoVuLJ8MUGvM",{"id":27805,"title":27806,"body":27807,"description":29427,"extension":1444,"meta":29428,"navigation":333,"path":29436,"seo":29437,"stem":29438,"__hash__":29439},"content\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-toggle-switches-and-checkboxes\u002Findex.md","CSS-Only Toggle Switches & Checkboxes: Production-Ready Patterns",{"type":7,"value":27808,"toc":29416},[27809,27812,27821,27826,27846,27848,27856,27867,27899,27901,27905,27922,27928,27975,28019,28024,28117,28518,28520,28524,28534,28575,28580,28639,29132,29134,29138,29144,29176,29178,29180,29238,29240,29244,29337,29339,29341,29354,29372,29384,29386,29413],[10,27810,27806],{"id":27811},"css-only-toggle-switches-checkboxes-production-ready-patterns",[14,27813,27814,27815,27817,27818,27820],{},"Building CSS-only toggle switches and checkboxes is one application of the ",[27,27816,1420],{"href":1419}," guide, and it requires strict separation of concerns: native HTML manages state and form submission, while CSS handles presentation and micro-interactions. As with the rest of ",[27,27819,1481],{"href":1480},", this page isolates implementation patterns, eliminates JavaScript overhead, and enforces WCAG 2.2 AA compliance.",[14,27822,27823],{},[62,27824,27825],{},"Core architectural principles:",[1393,27827,27828,27831,27838,27843],{},[1396,27829,27830],{},"Zero-JS dependency for state management",[1396,27832,27833,27834,69,27836],{},"GPU-accelerated transitions via ",[18,27835,103],{},[18,27837,76],{},[1396,27839,27840,27842],{},[18,27841,1495],{}," and semantic HTML for accessibility compliance",[1396,27844,27845],{},"Custom property-driven theming for scalable design systems",[47,27847],{},[50,27849,27851,27852,27855],{"id":27850},"core-architecture-the-state","Core Architecture & The ",[27853,27854],"checked",{}," State",[14,27857,27858,27859,27862,27863,27866],{},"The foundation relies on explicit ",[18,27860,27861],{},"\u003Clabel>"," binding paired with the native ",[18,27864,27865],{},"\u003Cinput type=\"checkbox\">",". Avoid legacy \"checkbox hack\" patterns that decouple inputs from their targets. Instead, leverage modern selectors for predictable state routing.",[1393,27868,27869,27881,27893],{},[1396,27870,27871,2318,27874,27876,27877,27880],{},[62,27872,27873],{},"Sibling Combinators:",[18,27875,19243],{}," (adjacent) or ",[18,27878,27879],{},"~"," (general) to target visual elements immediately following the input. This maintains a flat DOM and prevents selector specificity wars.",[1396,27882,27883,27888,27889,27892],{},[62,27884,27885,27886,723],{},"Parent Targeting with ",[18,27887,275],{}," Modern layouts benefit from ",[18,27890,27891],{},":has(input:checked)"," to apply layout shifts, theme overrides, or container-level padding directly to the wrapper without structural bloat.",[1396,27894,27895,27898],{},[62,27896,27897],{},"State Isolation:"," The native input remains in the document flow. CSS only alters visual presentation via pseudo-elements or sibling selectors, guaranteeing zero disruption to backend form processing or native validation APIs.",[47,27900],{},[50,27902,27904],{"id":27903},"transition-animation-optimization","Transition & Animation Optimization",[14,27906,27907,27908,69,27910,27912,27913,27917,27918,27921],{},"Smooth micro-interactions must execute exclusively on the compositor thread. Restrict animated properties to ",[18,27909,103],{},[18,27911,76],{}," to bypass main-thread layout and paint calculations. For complex, multi-step state changes, the same keyframe approach used for ",[27,27914,27916],{"href":27915},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-loading-spinners-and-skeletons\u002F","CSS-only loading spinners and skeletons"," and for ",[27,27919,27920],{"href":26277},"staggered list animations with custom properties"," orchestrates sequences without JavaScript.",[14,27923,27924,27925,27927],{},"The diagram below shows how the native ",[18,27926,8708],{}," state routes through a sibling combinator to drive the thumb transform.",[133,27929,140,27932,140,27935,140,27938,140,27941,140,27943,140,27947,140,27949,140,27951,140,27954,140,27957,140,27960,140,27964,140,27967,140,27970,140,27972],{"viewBox":27930,"role":136,"ariaLabel":27931,"xmlns":138,"style":139},"0 0 720 200","State routing from a checked input through a sibling combinator to a transformed thumb",[142,27933,27934],{},"Checked-state routing in a CSS-only toggle",[146,27936,27937],{},"A checked input is matched by a sibling combinator, which updates the track background and translates the thumb on the compositor thread.",[150,27939,27940],{"x":152,"y":153,"style":1781},"How a checkbox drives the thumb",[171,27942],{"x":158,"y":2630,"width":1822,"height":1788,"rx":176,"fill":177,"opacity":178,"stroke":167,"strokeWidth":1789},[150,27944,27946],{"x":27945,"y":1823,"style":1794},"109","Native input",[150,27948,8708],{"x":27945,"y":6645,"style":183},[171,27950],{"x":5127,"y":2630,"width":1822,"height":1788,"rx":176,"fill":72,"stroke":167,"strokeWidth":1789},[150,27952,27953],{"x":152,"y":1823,"style":1794},"Sibling match",[150,27955,27956],{"x":152,"y":6645,"style":183},"input:checked +",[171,27958],{"x":27959,"y":2630,"width":1822,"height":1788,"rx":176,"fill":177,"opacity":1813,"stroke":167,"strokeWidth":1789},"526",[150,27961,27963],{"x":27962,"y":1823,"style":1794},"611","Thumb moves",[150,27965,27966],{"x":27962,"y":6645,"style":183},"translateX",[163,27968],{"x1":27969,"y1":4196,"x2":5127,"y2":4196,"stroke":167,"strokeWidth":1824},"194",[163,27971],{"x1":5158,"y1":4196,"x2":27959,"y2":4196,"stroke":167,"strokeWidth":1824},[150,27973,27974],{"x":152,"y":1830,"style":1808},"compositor-only: transform + opacity",[1393,27976,27977,27990,28004],{},[1396,27978,27979,27982,27983,27985,27986,27989],{},[62,27980,27981],{},"Compositor Isolation:"," Apply ",[18,27984,2075],{}," to the moving thumb. Use ",[18,27987,27988],{},"contain: layout paint"," on the track to prevent repaint bleed into adjacent components.",[1396,27991,27992,27995,27996,2351,27998,3942,28000,28003],{},[62,27993,27994],{},"Easing Curves:"," Replace ",[18,27997,5456],{},[18,27999,6770],{},[18,28001,28002],{},"cubic-bezier(0.4, 0, 0.2, 1)"," for tactile, hardware-accelerated snap feedback that mimics native OS controls.",[1396,28005,28006,28009,28010,569,28012,569,28014,1745,28016,28018],{},[62,28007,28008],{},"Avoid Layout Thrashing:"," Never animate ",[18,28011,1748],{},[18,28013,2722],{},[18,28015,2725],{},[18,28017,2675],{}," on high-frequency toggles. These trigger synchronous style recalculation and layout passes.",[14,28020,28021],{},[62,28022,28023],{},"Production Implementation: GPU-Accelerated Toggle Switch",[281,28025,28027],{"className":283,"code":28026,"language":285,"meta":286,"style":286},"\u003Clabel class=\"toggle\">\n  \u003Cinput type=\"checkbox\" class=\"toggle__input\" \u002F>\n  \u003Cspan class=\"toggle__track\">\n    \u003Cspan class=\"toggle__thumb\">\u003C\u002Fspan>\n  \u003C\u002Fspan>\n\u003C\u002Flabel>\n",[18,28028,28029,28045,28067,28082,28101,28109],{"__ignoreMap":286},[290,28030,28031,28033,28036,28038,28040,28043],{"class":163,"line":292},[290,28032,296],{"class":295},[290,28034,28035],{"class":299},"label",[290,28037,314],{"class":303},[290,28039,307],{"class":295},[290,28041,28042],{"class":310},"\"toggle\"",[290,28044,327],{"class":295},[290,28046,28047,28049,28051,28053,28055,28058,28060,28062,28065],{"class":163,"line":330},[290,28048,367],{"class":295},[290,28050,18985],{"class":299},[290,28052,393],{"class":303},[290,28054,307],{"class":295},[290,28056,28057],{"class":310},"\"checkbox\"",[290,28059,314],{"class":303},[290,28061,307],{"class":295},[290,28063,28064],{"class":310},"\"toggle__input\"",[290,28066,9033],{"class":295},[290,28068,28069,28071,28073,28075,28077,28080],{"class":163,"line":337},[290,28070,367],{"class":295},[290,28072,290],{"class":299},[290,28074,314],{"class":303},[290,28076,307],{"class":295},[290,28078,28079],{"class":310},"\"toggle__track\"",[290,28081,327],{"class":295},[290,28083,28084,28086,28088,28090,28092,28095,28097,28099],{"class":163,"line":364},[290,28085,4290],{"class":295},[290,28087,290],{"class":299},[290,28089,314],{"class":303},[290,28091,307],{"class":295},[290,28093,28094],{"class":310},"\"toggle__thumb\"",[290,28096,11472],{"class":295},[290,28098,290],{"class":299},[290,28100,327],{"class":295},[290,28102,28103,28105,28107],{"class":163,"line":386},[290,28104,4315],{"class":295},[290,28106,290],{"class":299},[290,28108,327],{"class":295},[290,28110,28111,28113,28115],{"class":163,"line":408},[290,28112,431],{"class":295},[290,28114,28035],{"class":299},[290,28116,327],{"class":295},[281,28118,28120],{"className":438,"code":28119,"language":440,"meta":286,"style":286},".toggle__input {\n  position: absolute;\n  opacity: 0;\n  width: 0;\n  height: 0;\n}\n\n.toggle__track {\n  display: block;\n  width: 48px;\n  height: 24px;\n  background: #cbd5e1;\n  border-radius: 12px;\n  position: relative;\n  transition: background 0.2s ease;\n}\n\n.toggle__thumb {\n  position: absolute;\n  top: 2px;\n  left: 2px;\n  width: 20px;\n  height: 20px;\n  background: #fff;\n  border-radius: 50%;\n  transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n  will-change: transform;\n}\n\n.toggle__input:checked + .toggle__track {\n  background: #3b82f6;\n}\n\n.toggle__input:checked + .toggle__track .toggle__thumb {\n  transform: translateX(24px);\n}\n\n.toggle__input:focus-visible + .toggle__track {\n  outline: 2px solid #2563eb;\n  outline-offset: 2px;\n}\n",[18,28121,28122,28129,28139,28149,28159,28169,28173,28177,28184,28194,28206,28218,28229,28241,28251,28268,28272,28276,28283,28293,28306,28318,28330,28342,28352,28364,28394,28400,28404,28408,28420,28430,28434,28438,28451,28467,28471,28475,28486,28502,28514],{"__ignoreMap":286},[290,28123,28124,28127],{"class":163,"line":292},[290,28125,28126],{"class":303},".toggle__input",[290,28128,450],{"class":295},[290,28130,28131,28133,28135,28137],{"class":163,"line":330},[290,28132,1866],{"class":461},[290,28134,465],{"class":295},[290,28136,1927],{"class":461},[290,28138,471],{"class":295},[290,28140,28141,28143,28145,28147],{"class":163,"line":337},[290,28142,462],{"class":461},[290,28144,465],{"class":295},[290,28146,487],{"class":461},[290,28148,471],{"class":295},[290,28150,28151,28153,28155,28157],{"class":163,"line":364},[290,28152,17904],{"class":461},[290,28154,465],{"class":295},[290,28156,487],{"class":461},[290,28158,471],{"class":295},[290,28160,28161,28163,28165,28167],{"class":163,"line":386},[290,28162,18162],{"class":461},[290,28164,465],{"class":295},[290,28166,487],{"class":461},[290,28168,471],{"class":295},[290,28170,28171],{"class":163,"line":408},[290,28172,620],{"class":295},[290,28174,28175],{"class":163,"line":428},[290,28176,334],{"emptyLinePlaceholder":333},[290,28178,28179,28182],{"class":163,"line":517},[290,28180,28181],{"class":303},".toggle__track",[290,28183,450],{"class":295},[290,28185,28186,28188,28190,28192],{"class":163,"line":523},[290,28187,17742],{"class":461},[290,28189,465],{"class":295},[290,28191,68],{"class":461},[290,28193,471],{"class":295},[290,28195,28196,28198,28200,28202,28204],{"class":163,"line":532},[290,28197,17904],{"class":461},[290,28199,465],{"class":295},[290,28201,5139],{"class":461},[290,28203,674],{"class":541},[290,28205,471],{"class":295},[290,28207,28208,28210,28212,28214,28216],{"class":163,"line":551},[290,28209,18162],{"class":461},[290,28211,465],{"class":295},[290,28213,158],{"class":461},[290,28215,674],{"class":541},[290,28217,471],{"class":295},[290,28219,28220,28222,28224,28227],{"class":163,"line":586},[290,28221,1186],{"class":461},[290,28223,465],{"class":295},[290,28225,28226],{"class":461},"#cbd5e1",[290,28228,471],{"class":295},[290,28230,28231,28233,28235,28237,28239],{"class":163,"line":602},[290,28232,1663],{"class":461},[290,28234,465],{"class":295},[290,28236,5894],{"class":461},[290,28238,674],{"class":541},[290,28240,471],{"class":295},[290,28242,28243,28245,28247,28249],{"class":163,"line":617},[290,28244,1866],{"class":461},[290,28246,465],{"class":295},[290,28248,1871],{"class":461},[290,28250,471],{"class":295},[290,28252,28253,28255,28257,28259,28262,28264,28266],{"class":163,"line":623},[290,28254,526],{"class":461},[290,28256,465],{"class":295},[290,28258,1217],{"class":1216},[290,28260,28261],{"class":461}," 0.2",[290,28263,1886],{"class":541},[290,28265,545],{"class":461},[290,28267,471],{"class":295},[290,28269,28270],{"class":163,"line":628},[290,28271,620],{"class":295},[290,28273,28274],{"class":163,"line":634},[290,28275,334],{"emptyLinePlaceholder":333},[290,28277,28278,28281],{"class":163,"line":649},[290,28279,28280],{"class":303},".toggle__thumb",[290,28282,450],{"class":295},[290,28284,28285,28287,28289,28291],{"class":163,"line":660},[290,28286,1866],{"class":461},[290,28288,465],{"class":295},[290,28290,1927],{"class":461},[290,28292,471],{"class":295},[290,28294,28295,28298,28300,28302,28304],{"class":163,"line":688},[290,28296,28297],{"class":461},"  top",[290,28299,465],{"class":295},[290,28301,194],{"class":461},[290,28303,674],{"class":541},[290,28305,471],{"class":295},[290,28307,28308,28310,28312,28314,28316],{"class":163,"line":693},[290,28309,17877],{"class":461},[290,28311,465],{"class":295},[290,28313,194],{"class":461},[290,28315,674],{"class":541},[290,28317,471],{"class":295},[290,28319,28320,28322,28324,28326,28328],{"class":163,"line":698},[290,28321,17904],{"class":461},[290,28323,465],{"class":295},[290,28325,1785],{"class":461},[290,28327,674],{"class":541},[290,28329,471],{"class":295},[290,28331,28332,28334,28336,28338,28340],{"class":163,"line":704},[290,28333,18162],{"class":461},[290,28335,465],{"class":295},[290,28337,1785],{"class":461},[290,28339,674],{"class":541},[290,28341,471],{"class":295},[290,28343,28344,28346,28348,28350],{"class":163,"line":710},[290,28345,1186],{"class":461},[290,28347,465],{"class":295},[290,28349,9138],{"class":461},[290,28351,471],{"class":295},[290,28353,28354,28356,28358,28360,28362],{"class":163,"line":717},[290,28355,1663],{"class":461},[290,28357,465],{"class":295},[290,28359,1831],{"class":461},[290,28361,11018],{"class":541},[290,28363,471],{"class":295},[290,28365,28366,28368,28370,28372,28374,28376,28378,28380,28382,28384,28386,28388,28390,28392],{"class":163,"line":730},[290,28367,526],{"class":461},[290,28369,1880],{"class":295},[290,28371,566],{"class":461},[290,28373,1886],{"class":541},[290,28375,561],{"class":461},[290,28377,484],{"class":295},[290,28379,169],{"class":461},[290,28381,569],{"class":295},[290,28383,487],{"class":461},[290,28385,569],{"class":295},[290,28387,566],{"class":461},[290,28389,569],{"class":295},[290,28391,468],{"class":461},[290,28393,500],{"class":295},[290,28395,28396,28398],{"class":163,"line":742},[290,28397,3518],{"class":461},[290,28399,15503],{"class":295},[290,28401,28402],{"class":163,"line":768},[290,28403,620],{"class":295},[290,28405,28406],{"class":163,"line":774},[290,28407,334],{"emptyLinePlaceholder":333},[290,28409,28410,28413,28415,28418],{"class":163,"line":779},[290,28411,28412],{"class":303},".toggle__input:checked",[290,28414,3592],{"class":541},[290,28416,28417],{"class":303}," .toggle__track",[290,28419,450],{"class":295},[290,28421,28422,28424,28426,28428],{"class":163,"line":784},[290,28423,1186],{"class":461},[290,28425,465],{"class":295},[290,28427,24267],{"class":461},[290,28429,471],{"class":295},[290,28431,28432],{"class":163,"line":812},[290,28433,620],{"class":295},[290,28435,28436],{"class":163,"line":860},[290,28437,334],{"emptyLinePlaceholder":333},[290,28439,28440,28442,28444,28446,28449],{"class":163,"line":865},[290,28441,28412],{"class":303},[290,28443,3592],{"class":541},[290,28445,28417],{"class":303},[290,28447,28448],{"class":303}," .toggle__thumb",[290,28450,450],{"class":295},[290,28452,28453,28455,28457,28459,28461,28463,28465],{"class":163,"line":871},[290,28454,476],{"class":461},[290,28456,465],{"class":295},[290,28458,27966],{"class":461},[290,28460,484],{"class":295},[290,28462,158],{"class":461},[290,28464,674],{"class":541},[290,28466,500],{"class":295},[290,28468,28469],{"class":163,"line":880},[290,28470,620],{"class":295},[290,28472,28473],{"class":163,"line":896},[290,28474,334],{"emptyLinePlaceholder":333},[290,28476,28477,28480,28482,28484],{"class":163,"line":4734},[290,28478,28479],{"class":303},".toggle__input:focus-visible",[290,28481,3592],{"class":541},[290,28483,28417],{"class":303},[290,28485,450],{"class":295},[290,28487,28488,28490,28492,28494,28496,28498,28500],{"class":163,"line":4742},[290,28489,1617],{"class":461},[290,28491,465],{"class":295},[290,28493,194],{"class":461},[290,28495,674],{"class":541},[290,28497,852],{"class":461},[290,28499,20579],{"class":461},[290,28501,471],{"class":295},[290,28503,28504,28506,28508,28510,28512],{"class":163,"line":4761},[290,28505,1647],{"class":461},[290,28507,465],{"class":295},[290,28509,194],{"class":461},[290,28511,674],{"class":541},[290,28513,471],{"class":295},[290,28515,28516],{"class":163,"line":4766},[290,28517,620],{"class":295},[47,28519],{},[50,28521,28523],{"id":28522},"accessibility-focus-management","Accessibility & Focus Management",[14,28525,28526,28527,569,28529,1745,28531,28533],{},"Screen readers and keyboard navigation require the native input to remain fully interactive. Never use ",[18,28528,20],{},[18,28530,5681],{},[18,28532,2365],{}," on the checkbox.",[1393,28535,28536,28545,28554],{},[1396,28537,28538,2318,28541,28544],{},[62,28539,28540],{},"Visual Hiding:",[18,28542,28543],{},"position: absolute; opacity: 0; width: 0; height: 0;"," to remove the element from the visual tree while preserving tab order, click targets, and native form behavior.",[1396,28546,28547,28550,28551,28553],{},[62,28548,28549],{},"Focus Indicators:"," Style ",[18,28552,1495],{}," exclusively. This ensures keyboard users receive a clear, high-contrast ring while pointer interactions remain visually clean.",[1396,28555,28556,28559,28560,69,28562,28564,28565,3942,28567,28570,28571,28574],{},[62,28557,28558],{},"State Communication:"," Rely on native ",[18,28561,27853],{},[18,28563,26397],{}," states. If custom checkmarks are required, render them via ",[18,28566,2412],{},[18,28568,28569],{},"content: \"\""," and ensure the visual wrapper carries ",[18,28572,28573],{},"aria-hidden=\"true\""," to prevent duplicate announcements.",[14,28576,28577],{},[62,28578,28579],{},"Production Implementation: Custom Property Checkbox with Indeterminate State",[281,28581,28583],{"className":283,"code":28582,"language":285,"meta":286,"style":286},"\u003Cinput type=\"checkbox\" class=\"checkbox\" id=\"opt-in\" \u002F>\n\u003Clabel for=\"opt-in\" class=\"checkbox-label\">Enable notifications\u003C\u002Flabel>\n",[18,28584,28585,28612],{"__ignoreMap":286},[290,28586,28587,28589,28591,28593,28595,28597,28599,28601,28603,28605,28607,28610],{"class":163,"line":292},[290,28588,296],{"class":295},[290,28590,18985],{"class":299},[290,28592,393],{"class":303},[290,28594,307],{"class":295},[290,28596,28057],{"class":310},[290,28598,314],{"class":303},[290,28600,307],{"class":295},[290,28602,28057],{"class":310},[290,28604,345],{"class":303},[290,28606,307],{"class":295},[290,28608,28609],{"class":310},"\"opt-in\"",[290,28611,9033],{"class":295},[290,28613,28614,28616,28618,28621,28623,28625,28627,28629,28632,28635,28637],{"class":163,"line":330},[290,28615,296],{"class":295},[290,28617,28035],{"class":299},[290,28619,28620],{"class":303}," for",[290,28622,307],{"class":295},[290,28624,28609],{"class":310},[290,28626,314],{"class":303},[290,28628,307],{"class":295},[290,28630,28631],{"class":310},"\"checkbox-label\"",[290,28633,28634],{"class":295},">Enable notifications\u003C\u002F",[290,28636,28035],{"class":299},[290,28638,327],{"class":295},[281,28640,28642],{"className":438,"code":28641,"language":440,"meta":286,"style":286},".checkbox {\n  --bg: #e2e8f0;\n  --check: #fff;\n  --size: 20px;\n  appearance: none;\n  width: var(--size);\n  height: var(--size);\n  background: var(--bg);\n  border-radius: 4px;\n  cursor: pointer;\n  position: relative;\n  transition: background 0.2s;\n}\n\n.checkbox:checked {\n  --bg: #10b981;\n  background: var(--bg);\n}\n\n.checkbox:checked::after {\n  content: \"\";\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 6px;\n  height: 10px;\n  border: solid var(--check);\n  border-width: 0 2px 2px 0;\n  transform: translate(-50%, -60%) rotate(45deg);\n}\n\n.checkbox:indeterminate {\n  background: #f59e0b;\n}\n\n.checkbox:indeterminate::after {\n  content: \"\";\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  width: 10px;\n  height: 2px;\n  background: var(--check);\n  transform: translate(-50%, -50%);\n}\n",[18,28643,28644,28651,28662,28673,28686,28697,28712,28726,28741,28753,28763,28773,28787,28791,28795,28802,28813,28827,28831,28835,28842,28852,28862,28874,28886,28898,28910,28927,28948,28983,28987,28991,28998,29009,29013,29017,29024,29034,29044,29056,29068,29080,29092,29106,29128],{"__ignoreMap":286},[290,28645,28646,28649],{"class":163,"line":292},[290,28647,28648],{"class":303},".checkbox",[290,28650,450],{"class":295},[290,28652,28653,28656,28658,28660],{"class":163,"line":330},[290,28654,28655],{"class":1561},"  --bg",[290,28657,465],{"class":295},[290,28659,27227],{"class":461},[290,28661,471],{"class":295},[290,28663,28664,28667,28669,28671],{"class":163,"line":337},[290,28665,28666],{"class":1561},"  --check",[290,28668,465],{"class":295},[290,28670,9138],{"class":461},[290,28672,471],{"class":295},[290,28674,28675,28678,28680,28682,28684],{"class":163,"line":364},[290,28676,28677],{"class":1561},"  --size",[290,28679,465],{"class":295},[290,28681,1785],{"class":461},[290,28683,674],{"class":541},[290,28685,471],{"class":295},[290,28687,28688,28691,28693,28695],{"class":163,"line":386},[290,28689,28690],{"class":461},"  appearance",[290,28692,465],{"class":295},[290,28694,72],{"class":461},[290,28696,471],{"class":295},[290,28698,28699,28701,28703,28705,28707,28710],{"class":163,"line":408},[290,28700,17904],{"class":461},[290,28702,465],{"class":295},[290,28704,1622],{"class":461},[290,28706,484],{"class":295},[290,28708,28709],{"class":1561},"--size",[290,28711,500],{"class":295},[290,28713,28714,28716,28718,28720,28722,28724],{"class":163,"line":428},[290,28715,18162],{"class":461},[290,28717,465],{"class":295},[290,28719,1622],{"class":461},[290,28721,484],{"class":295},[290,28723,28709],{"class":1561},[290,28725,500],{"class":295},[290,28727,28728,28730,28732,28734,28736,28739],{"class":163,"line":517},[290,28729,1186],{"class":461},[290,28731,465],{"class":295},[290,28733,1622],{"class":461},[290,28735,484],{"class":295},[290,28737,28738],{"class":1561},"--bg",[290,28740,500],{"class":295},[290,28742,28743,28745,28747,28749,28751],{"class":163,"line":523},[290,28744,1663],{"class":461},[290,28746,465],{"class":295},[290,28748,249],{"class":461},[290,28750,674],{"class":541},[290,28752,471],{"class":295},[290,28754,28755,28757,28759,28761],{"class":163,"line":532},[290,28756,17841],{"class":461},[290,28758,465],{"class":295},[290,28760,18849],{"class":461},[290,28762,471],{"class":295},[290,28764,28765,28767,28769,28771],{"class":163,"line":551},[290,28766,1866],{"class":461},[290,28768,465],{"class":295},[290,28770,1871],{"class":461},[290,28772,471],{"class":295},[290,28774,28775,28777,28779,28781,28783,28785],{"class":163,"line":586},[290,28776,526],{"class":461},[290,28778,465],{"class":295},[290,28780,1217],{"class":1216},[290,28782,28261],{"class":461},[290,28784,1886],{"class":541},[290,28786,471],{"class":295},[290,28788,28789],{"class":163,"line":602},[290,28790,620],{"class":295},[290,28792,28793],{"class":163,"line":617},[290,28794,334],{"emptyLinePlaceholder":333},[290,28796,28797,28800],{"class":163,"line":623},[290,28798,28799],{"class":303},".checkbox:checked",[290,28801,450],{"class":295},[290,28803,28804,28806,28808,28811],{"class":163,"line":628},[290,28805,28655],{"class":1561},[290,28807,465],{"class":295},[290,28809,28810],{"class":461},"#10b981",[290,28812,471],{"class":295},[290,28814,28815,28817,28819,28821,28823,28825],{"class":163,"line":634},[290,28816,1186],{"class":461},[290,28818,465],{"class":295},[290,28820,1622],{"class":461},[290,28822,484],{"class":295},[290,28824,28738],{"class":1561},[290,28826,500],{"class":295},[290,28828,28829],{"class":163,"line":649},[290,28830,620],{"class":295},[290,28832,28833],{"class":163,"line":660},[290,28834,334],{"emptyLinePlaceholder":333},[290,28836,28837,28840],{"class":163,"line":688},[290,28838,28839],{"class":303},".checkbox:checked::after",[290,28841,450],{"class":295},[290,28843,28844,28846,28848,28850],{"class":163,"line":693},[290,28845,1911],{"class":461},[290,28847,465],{"class":295},[290,28849,1916],{"class":310},[290,28851,471],{"class":295},[290,28853,28854,28856,28858,28860],{"class":163,"line":698},[290,28855,1866],{"class":461},[290,28857,465],{"class":295},[290,28859,1927],{"class":461},[290,28861,471],{"class":295},[290,28863,28864,28866,28868,28870,28872],{"class":163,"line":704},[290,28865,28297],{"class":461},[290,28867,465],{"class":295},[290,28869,1831],{"class":461},[290,28871,11018],{"class":541},[290,28873,471],{"class":295},[290,28875,28876,28878,28880,28882,28884],{"class":163,"line":710},[290,28877,17877],{"class":461},[290,28879,465],{"class":295},[290,28881,1831],{"class":461},[290,28883,11018],{"class":541},[290,28885,471],{"class":295},[290,28887,28888,28890,28892,28894,28896],{"class":163,"line":717},[290,28889,17904],{"class":461},[290,28891,465],{"class":295},[290,28893,5901],{"class":461},[290,28895,674],{"class":541},[290,28897,471],{"class":295},[290,28899,28900,28902,28904,28906,28908],{"class":163,"line":730},[290,28901,18162],{"class":461},[290,28903,465],{"class":295},[290,28905,836],{"class":461},[290,28907,674],{"class":541},[290,28909,471],{"class":295},[290,28911,28912,28914,28916,28918,28920,28922,28925],{"class":163,"line":742},[290,28913,1948],{"class":461},[290,28915,465],{"class":295},[290,28917,1632],{"class":461},[290,28919,1635],{"class":461},[290,28921,484],{"class":295},[290,28923,28924],{"class":1561},"--check",[290,28926,500],{"class":295},[290,28928,28929,28932,28934,28936,28938,28940,28942,28944,28946],{"class":163,"line":768},[290,28930,28931],{"class":461},"  border-width",[290,28933,465],{"class":295},[290,28935,487],{"class":461},[290,28937,3290],{"class":461},[290,28939,674],{"class":541},[290,28941,3290],{"class":461},[290,28943,674],{"class":541},[290,28945,1198],{"class":461},[290,28947,471],{"class":295},[290,28949,28950,28952,28954,28956,28958,28961,28963,28965,28968,28970,28972,28974,28976,28979,28981],{"class":163,"line":774},[290,28951,476],{"class":461},[290,28953,465],{"class":295},[290,28955,2710],{"class":461},[290,28957,484],{"class":295},[290,28959,28960],{"class":461},"-50",[290,28962,11018],{"class":541},[290,28964,569],{"class":295},[290,28966,28967],{"class":461},"-60",[290,28969,11018],{"class":541},[290,28971,490],{"class":295},[290,28973,26776],{"class":461},[290,28975,484],{"class":295},[290,28977,28978],{"class":461},"45",[290,28980,10416],{"class":541},[290,28982,500],{"class":295},[290,28984,28985],{"class":163,"line":779},[290,28986,620],{"class":295},[290,28988,28989],{"class":163,"line":784},[290,28990,334],{"emptyLinePlaceholder":333},[290,28992,28993,28996],{"class":163,"line":812},[290,28994,28995],{"class":303},".checkbox:indeterminate",[290,28997,450],{"class":295},[290,28999,29000,29002,29004,29007],{"class":163,"line":860},[290,29001,1186],{"class":461},[290,29003,465],{"class":295},[290,29005,29006],{"class":461},"#f59e0b",[290,29008,471],{"class":295},[290,29010,29011],{"class":163,"line":865},[290,29012,620],{"class":295},[290,29014,29015],{"class":163,"line":871},[290,29016,334],{"emptyLinePlaceholder":333},[290,29018,29019,29022],{"class":163,"line":880},[290,29020,29021],{"class":303},".checkbox:indeterminate::after",[290,29023,450],{"class":295},[290,29025,29026,29028,29030,29032],{"class":163,"line":896},[290,29027,1911],{"class":461},[290,29029,465],{"class":295},[290,29031,1916],{"class":310},[290,29033,471],{"class":295},[290,29035,29036,29038,29040,29042],{"class":163,"line":4734},[290,29037,1866],{"class":461},[290,29039,465],{"class":295},[290,29041,1927],{"class":461},[290,29043,471],{"class":295},[290,29045,29046,29048,29050,29052,29054],{"class":163,"line":4742},[290,29047,28297],{"class":461},[290,29049,465],{"class":295},[290,29051,1831],{"class":461},[290,29053,11018],{"class":541},[290,29055,471],{"class":295},[290,29057,29058,29060,29062,29064,29066],{"class":163,"line":4761},[290,29059,17877],{"class":461},[290,29061,465],{"class":295},[290,29063,1831],{"class":461},[290,29065,11018],{"class":541},[290,29067,471],{"class":295},[290,29069,29070,29072,29074,29076,29078],{"class":163,"line":4766},[290,29071,17904],{"class":461},[290,29073,465],{"class":295},[290,29075,836],{"class":461},[290,29077,674],{"class":541},[290,29079,471],{"class":295},[290,29081,29082,29084,29086,29088,29090],{"class":163,"line":4786},[290,29083,18162],{"class":461},[290,29085,465],{"class":295},[290,29087,194],{"class":461},[290,29089,674],{"class":541},[290,29091,471],{"class":295},[290,29093,29094,29096,29098,29100,29102,29104],{"class":163,"line":9635},[290,29095,1186],{"class":461},[290,29097,465],{"class":295},[290,29099,1622],{"class":461},[290,29101,484],{"class":295},[290,29103,28924],{"class":1561},[290,29105,500],{"class":295},[290,29107,29108,29110,29112,29114,29116,29118,29120,29122,29124,29126],{"class":163,"line":9641},[290,29109,476],{"class":461},[290,29111,465],{"class":295},[290,29113,2710],{"class":461},[290,29115,484],{"class":295},[290,29117,28960],{"class":461},[290,29119,11018],{"class":541},[290,29121,569],{"class":295},[290,29123,28960],{"class":461},[290,29125,11018],{"class":541},[290,29127,500],{"class":295},[290,29129,29130],{"class":163,"line":9647},[290,29131,620],{"class":295},[47,29133],{},[50,29135,29137],{"id":29136},"fallback-strategies-progressive-enhancement","Fallback Strategies & Progressive Enhancement",[14,29139,29140,29141,29143],{},"Production systems must degrade gracefully when modern selectors or user preferences intervene. Align your implementation with broader ",[27,29142,1481],{"href":1480}," standards to ensure resilience across environments.",[1393,29145,29146,29158,29167],{},[1396,29147,29148,29151,29152,29154,29155,29157],{},[62,29149,29150],{},"Reduced Motion Compliance:"," Wrap transitions in ",[18,29153,4126],{}," to force instant state changes (",[18,29156,15429],{},"). This respects OS-level accessibility settings and prevents vestibular triggers.",[1396,29159,29160,2318,29163,29166],{},[62,29161,29162],{},"Feature Detection:",[18,29164,29165],{},"@supports selector(:has(*))"," to conditionally apply parent-targeting styles. Provide a baseline sibling-combinator fallback for unsupported environments.",[1396,29168,29169,29172,29173,29175],{},[62,29170,29171],{},"Base Functionality Guarantee:"," Ensure the unchecked\u002Fchecked states render correctly without any ",[18,29174,887],{}," declarations. If CSS parsing fails or animations are blocked by browser policies, the native checkbox remains fully operational and visually distinct.",[47,29177],{},[50,29179,2248],{"id":2247},[2250,29181,29182,29192],{},[2253,29183,29184],{},[2256,29185,29186,29188,29190],{},[2259,29187,2261],{},[2259,29189,2264],{},[2259,29191,2267],{},[2269,29193,29194,29207,29218,29228],{},[2256,29195,29196,29198,29201],{},[2274,29197,2276],{},[2274,29199,29200],{},"105+",[2274,29202,29203,29204,29206],{},"Full ",[18,29205,275],{}," support",[2256,29208,29209,29211,29214],{},[2274,29210,2287],{},[2274,29212,29213],{},"103+",[2274,29215,29203,29216,29206],{},[18,29217,275],{},[2256,29219,29220,29222,29224],{},[2274,29221,2297],{},[2274,29223,2300],{},[2274,29225,29203,29226,29206],{},[18,29227,275],{},[2256,29229,29230,29232,29234],{},[2274,29231,2308],{},[2274,29233,29200],{},[2274,29235,29203,29236,29206],{},[18,29237,275],{},[47,29239],{},[50,29241,29243],{"id":29242},"common-issues-debugging","Common Issues & Debugging",[2250,29245,29246,29257],{},[2253,29247,29248],{},[2256,29249,29250,29252,29254],{},[2259,29251,2338],{},[2259,29253,3876],{},[2259,29255,29256],{},"Debugging & Fix",[2269,29258,29259,29285,29308],{},[2256,29260,29261,29266,29269],{},[2274,29262,29263],{},[62,29264,29265],{},"Click\u002Ftap area too small on mobile",[2274,29267,29268],{},"Label lacks padding; hit area doesn't match visual bounds.",[2274,29270,29271,29272,1331,29275,29277,29278,29281,29282,29284],{},"Apply ",[18,29273,29274],{},"padding: 8px",[18,29276,27861],{},". Ensure ",[18,29279,29280],{},"cursor: pointer"," is set. Expand the track's ",[18,29283,2811],{}," pseudo-element with negative margins to guarantee a 44x44px minimum touch target per WCAG 2.2.",[2256,29286,29287,29292,29299],{},[2274,29288,29289],{},[62,29290,29291],{},"State desync or visual lag during rapid toggling",[2274,29293,29294,29295,569,29297,3724],{},"Animating layout-triggering properties (",[18,29296,1748],{},[18,29298,2675],{},[2274,29300,29301,29302,29304,29305,29307],{},"Inspect with DevTools Performance tab. Replace all non-compositor properties. Animate ",[18,29303,3925],{}," exclusively. Add ",[18,29306,887],{}," only to the track, but isolate thumb movement to the compositor thread.",[2256,29309,29310,29315,29323],{},[2274,29311,29312],{},[62,29313,29314],{},"Focus ring breaks design or overlaps thumb",[2274,29316,29317,29318,2774,29320,29322],{},"Using ",[18,29319,1532],{},[18,29321,1495],{},"; default outline intersects component.",[2274,29324,21520,29325,29327,29328,29331,29332,29334,29335,42],{},[18,29326,1495],{},". Apply ",[18,29329,29330],{},"outline-offset: 2px"," to push the ring outside the component bounds, or use a custom ",[18,29333,1506],{}," that respects ",[18,29336,2584],{},[47,29338],{},[50,29340,1316],{"id":1315},[14,29342,29343,29346,29347,29349,29350,29353],{},[62,29344,29345],{},"Can CSS-only toggles submit form data correctly?","\nYes. The native ",[18,29348,27865],{}," remains in the DOM and handles all form submission logic. The CSS only controls the visual presentation, ensuring zero disruption to backend processing or ",[18,29351,29352],{},"FormData"," serialization.",[14,29355,29356,29359,29360,69,29362,29364,29365,69,29367,29327,29369,29371],{},[62,29357,29358],{},"How do I prevent layout shifts when toggling animations?","\nReserve space using explicit ",[18,29361,1748],{},[18,29363,2722],{}," on the container. Animate only ",[18,29366,103],{},[18,29368,76],{},[18,29370,27988],{}," to the component wrapper to isolate rendering and prevent reflow propagation to parent elements.",[14,29373,29374,29377,29378,29380,29381,29383],{},[62,29375,29376],{},"Is the checkbox hack still viable for modern projects?","\nOnly for legacy browser support (IE11\u002FEdge Legacy). Modern implementations prefer semantic ",[18,29379,27861],{}," binding with ",[18,29382,275],{}," or sibling combinators, which maintain accessibility, reduce DOM complexity, and prevent state leakage in componentized architectures.",[50,29385,1391],{"id":1390},[1393,29387,29388,29393,29398,29403,29408],{},[1396,29389,29390,29392],{},[27,29391,1420],{"href":1419}," — the parent guide for state-driven CSS animation.",[1396,29394,29395,29397],{},[27,29396,27790],{"href":27915}," — sibling pattern for continuous keyframe animation.",[1396,29399,29400,29402],{},[27,29401,26278],{"href":26277}," — sibling pattern for sequenced reveals.",[1396,29404,29405,29407],{},[27,29406,2511],{"href":1412}," — collapse toggle transitions when users opt out of motion.",[1396,29409,29410,29412],{},[27,29411,5778],{"href":5777}," — the responsive-layout area, for toggles that adapt to component width.",[1430,29414,29415],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":286,"searchDepth":330,"depth":330,"links":29417},[29418,29420,29421,29422,29423,29424,29425,29426],{"id":27850,"depth":330,"text":29419},"Core Architecture & The  State",{"id":27903,"depth":330,"text":27904},{"id":28522,"depth":330,"text":28523},{"id":29136,"depth":330,"text":29137},{"id":2247,"depth":330,"text":2248},{"id":29242,"depth":330,"text":29243},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Build CSS-only toggle switches and checkboxes: native HTML state management, WCAG 2.2 AA compliance, and zero-JavaScript micro-interaction patterns.",{"seoTitle":27760,"datePublished":1447,"dateModified":1447,"faq":29429},[29430,29432,29434],{"q":29345,"a":29431},"Yes. The native checkbox input remains in the DOM and handles all form submission logic. The CSS only controls visual presentation, so backend processing and FormData serialization are unaffected.",{"q":29358,"a":29433},"Reserve space with explicit width and height on the container and animate only transform and opacity. Apply contain: layout paint to the wrapper to isolate rendering and prevent reflow propagation to parent elements.",{"q":29376,"a":29435},"Only for legacy browser support such as IE11 or Edge Legacy. Modern implementations prefer semantic label binding with :has() or sibling combinators, which keep accessibility intact, reduce DOM complexity, and prevent state leakage.","\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-toggle-switches-and-checkboxes",{"title":27806,"description":29427},"css-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcss-only-toggle-switches-and-checkboxes\u002Findex","FNeHOvEF5oTnCxOlgP1IPyy8qI5kUPWpoxH-9ekxVpY",{"id":29441,"title":29442,"body":29443,"description":31882,"extension":1444,"meta":31883,"navigation":333,"path":31894,"seo":31895,"stem":31896,"__hash__":31897},"content\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Findex.md","Keyframe Animation Patterns: Spec-Compliant Architectures for Modern UIs",{"type":7,"value":29444,"toc":31863},[29445,29448,29454,29504,29508,29525,29527,29531,29549,29553,29559,29667,30136,30143,30145,30149,30163,30167,30173,30433,30542,30558,30560,30564,30585,30589,30601,30847,30849,30880,30885,30887,30891,30905,30909,30915,31499,31504,31506,31508,31529,31654,31656,31658,31754,31756,31758,31787,31807,31824,31826,31828,31860],[10,29446,29442],{"id":29447},"keyframe-animation-patterns-spec-compliant-architectures-for-modern-uis",[14,29449,29450,29451,29453],{},"Master production-ready keyframe animation patterns to build performant, maintainable interfaces. This guide bridges foundational concepts from ",[27,29452,1481],{"href":1480}," with scalable component architectures, focusing on declarative state mapping, GPU-optimized transforms, and spec-compliant motion design.",[133,29455,140,29457,140,29460,140,29463,140,29466,140,29468,140,29470,140,29472,140,29476,140,29480,140,29483,140,29486,140,29488,140,29491,140,29494,140,29498,140,29501],{"viewBox":4133,"role":136,"ariaLabel":29456,"xmlns":138,"style":139},"Staggered animation timeline with per-item delay offsets",[142,29458,29459],{},"Staggered keyframe timeline",[146,29461,29462],{},"Timeline showing how a per-item animation-delay offset staggers three elements along a shared keyframe.",[150,29464,29465],{"x":152,"y":2598,"style":154},"Staggered delay offsets",[163,29467],{"x1":4147,"y1":9105,"x2":25430,"y2":9105,"stroke":167,"strokeWidth":168,"opacity":798},[163,29469],{"x1":4147,"y1":1787,"x2":25430,"y2":1787,"stroke":167,"strokeWidth":168,"opacity":798},[163,29471],{"x1":4147,"y1":538,"x2":25430,"y2":538,"stroke":167,"strokeWidth":168,"opacity":798},[150,29473,29475],{"x":173,"y":1793,"style":29474},"text-anchor:end;fill:currentColor;font:13px sans-serif","Item 0",[150,29477,29479],{"x":173,"y":29478,"style":29474},"154","Item 1",[150,29481,29482],{"x":173,"y":191,"style":29474},"Item 2",[171,29484],{"x":4147,"y":29485,"width":538,"height":158,"rx":5911,"fill":177,"opacity":199},"68",[171,29487],{"x":174,"y":5149,"width":538,"height":158,"rx":5911,"fill":177,"opacity":199},[171,29489],{"x":11114,"y":29490,"width":538,"height":158,"rx":5911,"fill":177,"opacity":199},"208",[150,29492,29493],{"x":4190,"y":1793,"style":183},"delay 0s",[150,29495,29497],{"x":29496,"y":29478,"style":183},"310","delay .15s",[150,29499,29500],{"x":19646,"y":191,"style":183},"delay .30s",[150,29502,29503],{"x":152,"y":2601,"style":1808},"One keyframe, offset per item via calc(var(--i) * step)",[14,29505,29506],{},[62,29507,19673],{},[1393,29509,29510,29513,29516,29522],{},[1396,29511,29512],{},"Declarative state routing without JavaScript",[1396,29514,29515],{},"Compositor-only property optimization",[1396,29517,29518,29519,29521],{},"Modular ",[18,29520,3968],{}," architecture for design systems",[1396,29523,29524],{},"Accessibility-first motion constraints",[47,29526],{},[50,29528,29530],{"id":29529},"state-driven-keyframe-architecture","State-Driven Keyframe Architecture",[14,29532,29533,29534,69,29536,29539,29540,29545,29546,29548],{},"Modern UI motion should be driven by state, not imperative JS calls. By leveraging CSS custom properties as animation controllers and modern selectors like ",[18,29535,275],{},[18,29537,29538],{},":target",", you can decouple layout logic from visual feedback layers. This pattern aligns with the ",[27,29541,29544],{"href":29542,"rel":29543},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-animations-1\u002F",[13489],"CSS Animations Level 1"," specification, which defines ",[18,29547,3968],{}," as declarative timeline mappings.",[2757,29550,29552],{"id":29551},"implementation-css-variable-routing","Implementation: CSS Variable Routing",[14,29554,29555,29556,29558],{},"Use custom properties to toggle animation states, allowing you to swap entire sequences without rewriting selectors. Combine this with ",[18,29557,275],{}," for parent-level state observation.",[281,29560,29562],{"className":283,"code":29561,"language":285,"meta":286,"style":286},"\u003C!-- HTML -->\n\u003Cdiv class=\"card\" data-state=\"idle\">\n  \u003Cdiv class=\"card__content\">\n    \u003Ch3>Declarative Motion\u003C\u002Fh3>\n    \u003Cp>State-driven architecture reduces JS overhead.\u003C\u002Fp>\n  \u003C\u002Fdiv>\n  \u003Cdiv class=\"card__indicator\">\u003C\u002Fdiv>\n\u003C\u002Fdiv>\n",[18,29563,29564,29569,29591,29606,29619,29632,29640,29659],{"__ignoreMap":286},[290,29565,29566],{"class":163,"line":292},[290,29567,29568],{"class":455},"\u003C!-- HTML -->\n",[290,29570,29571,29573,29575,29577,29579,29581,29584,29586,29589],{"class":163,"line":330},[290,29572,296],{"class":295},[290,29574,342],{"class":299},[290,29576,314],{"class":303},[290,29578,307],{"class":295},[290,29580,9295],{"class":310},[290,29582,29583],{"class":303}," data-state",[290,29585,307],{"class":295},[290,29587,29588],{"class":310},"\"idle\"",[290,29590,327],{"class":295},[290,29592,29593,29595,29597,29599,29601,29604],{"class":163,"line":337},[290,29594,367],{"class":295},[290,29596,342],{"class":299},[290,29598,314],{"class":303},[290,29600,307],{"class":295},[290,29602,29603],{"class":310},"\"card__content\"",[290,29605,327],{"class":295},[290,29607,29608,29610,29612,29615,29617],{"class":163,"line":364},[290,29609,4290],{"class":295},[290,29611,2757],{"class":299},[290,29613,29614],{"class":295},">Declarative Motion\u003C\u002F",[290,29616,2757],{"class":299},[290,29618,327],{"class":295},[290,29620,29621,29623,29625,29628,29630],{"class":163,"line":386},[290,29622,4290],{"class":295},[290,29624,14],{"class":299},[290,29626,29627],{"class":295},">State-driven architecture reduces JS overhead.\u003C\u002F",[290,29629,14],{"class":299},[290,29631,327],{"class":295},[290,29633,29634,29636,29638],{"class":163,"line":408},[290,29635,4315],{"class":295},[290,29637,342],{"class":299},[290,29639,327],{"class":295},[290,29641,29642,29644,29646,29648,29650,29653,29655,29657],{"class":163,"line":428},[290,29643,367],{"class":295},[290,29645,342],{"class":299},[290,29647,314],{"class":303},[290,29649,307],{"class":295},[290,29651,29652],{"class":310},"\"card__indicator\"",[290,29654,11472],{"class":295},[290,29656,342],{"class":299},[290,29658,327],{"class":295},[290,29660,29661,29663,29665],{"class":163,"line":517},[290,29662,431],{"class":295},[290,29664,342],{"class":299},[290,29666,327],{"class":295},[281,29668,29670],{"className":438,"code":29669,"language":440,"meta":286,"style":286},"\u002F* CSS *\u002F\n:root {\n  --anim-duration: 0.4s;\n  --anim-easing: cubic-bezier(0.2, 0.8, 0.2, 1);\n}\n\n.card {\n  --state: idle;\n  position: relative;\n  overflow: hidden;\n  border-radius: 12px;\n  background: #f8fafc;\n}\n\n\u002F* State routing via custom property *\u002F\n.card[data-state=\"active\"] {\n  --state: active;\n}\n\n.card__indicator {\n  position: absolute;\n  inset: 0;\n  background: rgba(59, 130, 246, 0.1);\n  opacity: 0;\n  transform: scale(0.95);\n  animation: var(--state, idle) var(--anim-duration) var(--anim-easing) forwards;\n}\n\n@keyframes active {\n  0% {\n    opacity: 0;\n    transform: scale(0.95);\n  }\n  100% {\n    opacity: 1;\n    transform: scale(1);\n  }\n}\n\n\u002F* Fallback for idle state *\u002F\n@keyframes idle {\n  0%,\n  100% {\n    opacity: 0;\n    transform: scale(1);\n  }\n}\n\n\u002F* Parent-driven activation using :has() *\u002F\n.card:has(.card__content:hover) {\n  --state: active;\n}\n",[18,29671,29672,29677,29683,29696,29723,29727,29731,29737,29745,29755,29765,29777,29787,29791,29795,29800,29816,29827,29831,29835,29842,29852,29862,29889,29899,29913,29950,29954,29958,29967,29973,29983,29997,30001,30007,30017,30031,30035,30039,30043,30048,30057,30063,30069,30079,30093,30097,30101,30105,30110,30122,30132],{"__ignoreMap":286},[290,29673,29674],{"class":163,"line":292},[290,29675,29676],{"class":455},"\u002F* CSS *\u002F\n",[290,29678,29679,29681],{"class":163,"line":330},[290,29680,1554],{"class":303},[290,29682,450],{"class":295},[290,29684,29685,29688,29690,29692,29694],{"class":163,"line":337},[290,29686,29687],{"class":1561},"  --anim-duration",[290,29689,465],{"class":295},[290,29691,169],{"class":461},[290,29693,1886],{"class":541},[290,29695,471],{"class":295},[290,29697,29698,29701,29703,29705,29707,29709,29711,29713,29715,29717,29719,29721],{"class":163,"line":364},[290,29699,29700],{"class":1561},"  --anim-easing",[290,29702,465],{"class":295},[290,29704,11767],{"class":461},[290,29706,484],{"class":295},[290,29708,566],{"class":461},[290,29710,569],{"class":295},[290,29712,572],{"class":461},[290,29714,569],{"class":295},[290,29716,566],{"class":461},[290,29718,569],{"class":295},[290,29720,468],{"class":461},[290,29722,500],{"class":295},[290,29724,29725],{"class":163,"line":386},[290,29726,620],{"class":295},[290,29728,29729],{"class":163,"line":408},[290,29730,334],{"emptyLinePlaceholder":333},[290,29732,29733,29735],{"class":163,"line":428},[290,29734,11528],{"class":303},[290,29736,450],{"class":295},[290,29738,29739,29742],{"class":163,"line":517},[290,29740,29741],{"class":1561},"  --state",[290,29743,29744],{"class":295},": idle;\n",[290,29746,29747,29749,29751,29753],{"class":163,"line":523},[290,29748,1866],{"class":461},[290,29750,465],{"class":295},[290,29752,1871],{"class":461},[290,29754,471],{"class":295},[290,29756,29757,29759,29761,29763],{"class":163,"line":532},[290,29758,7803],{"class":461},[290,29760,465],{"class":295},[290,29762,7808],{"class":461},[290,29764,471],{"class":295},[290,29766,29767,29769,29771,29773,29775],{"class":163,"line":551},[290,29768,1663],{"class":461},[290,29770,465],{"class":295},[290,29772,5894],{"class":461},[290,29774,674],{"class":541},[290,29776,471],{"class":295},[290,29778,29779,29781,29783,29785],{"class":163,"line":586},[290,29780,1186],{"class":461},[290,29782,465],{"class":295},[290,29784,19811],{"class":461},[290,29786,471],{"class":295},[290,29788,29789],{"class":163,"line":602},[290,29790,620],{"class":295},[290,29792,29793],{"class":163,"line":617},[290,29794,334],{"emptyLinePlaceholder":333},[290,29796,29797],{"class":163,"line":623},[290,29798,29799],{"class":455},"\u002F* State routing via custom property *\u002F\n",[290,29801,29802,29804,29806,29809,29811,29814],{"class":163,"line":628},[290,29803,11528],{"class":303},[290,29805,1140],{"class":295},[290,29807,29808],{"class":303},"data-state",[290,29810,307],{"class":541},[290,29812,29813],{"class":310},"\"active\"",[290,29815,14739],{"class":295},[290,29817,29818,29820,29822,29825],{"class":163,"line":634},[290,29819,29741],{"class":1561},[290,29821,465],{"class":295},[290,29823,29824],{"class":461},"active",[290,29826,471],{"class":295},[290,29828,29829],{"class":163,"line":649},[290,29830,620],{"class":295},[290,29832,29833],{"class":163,"line":660},[290,29834,334],{"emptyLinePlaceholder":333},[290,29836,29837,29840],{"class":163,"line":688},[290,29838,29839],{"class":303},".card__indicator",[290,29841,450],{"class":295},[290,29843,29844,29846,29848,29850],{"class":163,"line":693},[290,29845,1866],{"class":461},[290,29847,465],{"class":295},[290,29849,1927],{"class":461},[290,29851,471],{"class":295},[290,29853,29854,29856,29858,29860],{"class":163,"line":698},[290,29855,1934],{"class":461},[290,29857,465],{"class":295},[290,29859,487],{"class":461},[290,29861,471],{"class":295},[290,29863,29864,29866,29868,29871,29873,29875,29877,29879,29881,29883,29885,29887],{"class":163,"line":704},[290,29865,1186],{"class":461},[290,29867,465],{"class":295},[290,29869,29870],{"class":461},"rgba",[290,29872,484],{"class":295},[290,29874,19907],{"class":461},[290,29876,569],{"class":295},[290,29878,182],{"class":461},[290,29880,569],{"class":295},[290,29882,19916],{"class":461},[290,29884,569],{"class":295},[290,29886,3125],{"class":461},[290,29888,500],{"class":295},[290,29890,29891,29893,29895,29897],{"class":163,"line":710},[290,29892,462],{"class":461},[290,29894,465],{"class":295},[290,29896,487],{"class":461},[290,29898,471],{"class":295},[290,29900,29901,29903,29905,29907,29909,29911],{"class":163,"line":717},[290,29902,476],{"class":461},[290,29904,465],{"class":295},[290,29906,493],{"class":461},[290,29908,484],{"class":295},[290,29910,1119],{"class":461},[290,29912,500],{"class":295},[290,29914,29915,29917,29919,29921,29923,29925,29928,29930,29932,29935,29937,29939,29941,29944,29946,29948],{"class":163,"line":730},[290,29916,6035],{"class":461},[290,29918,465],{"class":295},[290,29920,1622],{"class":461},[290,29922,484],{"class":295},[290,29924,25413],{"class":1561},[290,29926,29927],{"class":295},", idle) ",[290,29929,1622],{"class":461},[290,29931,484],{"class":295},[290,29933,29934],{"class":1561},"--anim-duration",[290,29936,490],{"class":295},[290,29938,1622],{"class":461},[290,29940,484],{"class":295},[290,29942,29943],{"class":1561},"--anim-easing",[290,29945,490],{"class":295},[290,29947,7063],{"class":461},[290,29949,471],{"class":295},[290,29951,29952],{"class":163,"line":742},[290,29953,620],{"class":295},[290,29955,29956],{"class":163,"line":768},[290,29957,334],{"emptyLinePlaceholder":333},[290,29959,29960,29962,29965],{"class":163,"line":774},[290,29961,3968],{"class":541},[290,29963,29964],{"class":1561}," active",[290,29966,450],{"class":295},[290,29968,29969,29971],{"class":163,"line":779},[290,29970,21039],{"class":303},[290,29972,450],{"class":295},[290,29974,29975,29977,29979,29981],{"class":163,"line":784},[290,29976,733],{"class":461},[290,29978,465],{"class":295},[290,29980,487],{"class":461},[290,29982,471],{"class":295},[290,29984,29985,29987,29989,29991,29993,29995],{"class":163,"line":812},[290,29986,745],{"class":461},[290,29988,465],{"class":295},[290,29990,493],{"class":461},[290,29992,484],{"class":295},[290,29994,1119],{"class":461},[290,29996,500],{"class":295},[290,29998,29999],{"class":163,"line":860},[290,30000,771],{"class":295},[290,30002,30003,30005],{"class":163,"line":865},[290,30004,21111],{"class":303},[290,30006,450],{"class":295},[290,30008,30009,30011,30013,30015],{"class":163,"line":871},[290,30010,733],{"class":461},[290,30012,465],{"class":295},[290,30014,468],{"class":461},[290,30016,471],{"class":295},[290,30018,30019,30021,30023,30025,30027,30029],{"class":163,"line":880},[290,30020,745],{"class":461},[290,30022,465],{"class":295},[290,30024,493],{"class":461},[290,30026,484],{"class":295},[290,30028,468],{"class":461},[290,30030,500],{"class":295},[290,30032,30033],{"class":163,"line":896},[290,30034,771],{"class":295},[290,30036,30037],{"class":163,"line":4734},[290,30038,620],{"class":295},[290,30040,30041],{"class":163,"line":4742},[290,30042,334],{"emptyLinePlaceholder":333},[290,30044,30045],{"class":163,"line":4761},[290,30046,30047],{"class":455},"\u002F* Fallback for idle state *\u002F\n",[290,30049,30050,30052,30055],{"class":163,"line":4766},[290,30051,3968],{"class":541},[290,30053,30054],{"class":1561}," idle",[290,30056,450],{"class":295},[290,30058,30059,30061],{"class":163,"line":4786},[290,30060,21039],{"class":303},[290,30062,548],{"class":295},[290,30064,30065,30067],{"class":163,"line":9635},[290,30066,21111],{"class":303},[290,30068,450],{"class":295},[290,30070,30071,30073,30075,30077],{"class":163,"line":9641},[290,30072,733],{"class":461},[290,30074,465],{"class":295},[290,30076,487],{"class":461},[290,30078,471],{"class":295},[290,30080,30081,30083,30085,30087,30089,30091],{"class":163,"line":9647},[290,30082,745],{"class":461},[290,30084,465],{"class":295},[290,30086,493],{"class":461},[290,30088,484],{"class":295},[290,30090,468],{"class":461},[290,30092,500],{"class":295},[290,30094,30095],{"class":163,"line":9665},[290,30096,771],{"class":295},[290,30098,30099],{"class":163,"line":9683},[290,30100,620],{"class":295},[290,30102,30103],{"class":163,"line":9688},[290,30104,334],{"emptyLinePlaceholder":333},[290,30106,30107],{"class":163,"line":9694},[290,30108,30109],{"class":455},"\u002F* Parent-driven activation using :has() *\u002F\n",[290,30111,30112,30115,30117,30120],{"class":163,"line":9700},[290,30113,30114],{"class":303},".card:has",[290,30116,484],{"class":295},[290,30118,30119],{"class":303},".card__content:hover",[290,30121,646],{"class":295},[290,30123,30124,30126,30128,30130],{"class":163,"line":9710},[290,30125,29741],{"class":1561},[290,30127,465],{"class":295},[290,30129,29824],{"class":461},[290,30131,471],{"class":295},[290,30133,30134],{"class":163,"line":9716},[290,30135,620],{"class":295},[14,30137,30138,30140,30141,3914],{},[62,30139,15215],{}," CSS Custom Properties Level 1, CSS Selectors Level 4 (",[18,30142,275],{},[47,30144],{},[50,30146,30148],{"id":30147},"core-motion-patterns-component-implementation","Core Motion Patterns & Component Implementation",[14,30150,30151,30152,30154,30155,69,30157,30159,30160,30162],{},"Reusable motion sequences require predictable easing and synchronized timing. When designing loading indicators, entrance\u002Fexit transitions, or micro-feedback loops, chain ",[18,30153,6980],{}," offsets and standardize easing curves to match natural physics. The same approach underpins both ",[27,30156,27916],{"href":27915},[27,30158,27920],{"href":26277},", where a single keyframe is reused with per-item offsets. For compound interactions, integrate these patterns with established ",[27,30161,3345],{"href":3344}," principles to maintain consistent tactile feedback across components.",[2757,30164,30166],{"id":30165},"implementation-staggered-loading-sequence","Implementation: Staggered Loading Sequence",[14,30168,30169,30170,30172],{},"The following pattern uses ",[18,30171,6980],{}," with a CSS variable multiplier to create a scalable, staggered effect without duplicating keyframes.",[281,30174,30176],{"className":438,"code":30175,"language":440,"meta":286,"style":286},"\u002F* CSS *\u002F\n.spinner-group {\n  display: flex;\n  gap: 8px;\n  align-items: center;\n}\n\n.spinner-dot {\n  width: 10px;\n  height: 10px;\n  border-radius: 50%;\n  background: #6366f1;\n  animation: pulse 1.2s infinite ease-in-out;\n  animation-delay: calc(var(--i, 0) * 0.15s);\n}\n\n@keyframes pulse {\n  0%,\n  80%,\n  100% {\n    transform: scale(0.6);\n    opacity: 0.4;\n  }\n  40% {\n    transform: scale(1);\n    opacity: 1;\n  }\n}\n",[18,30177,30178,30182,30189,30199,30211,30222,30226,30230,30237,30249,30261,30273,30284,30301,30331,30335,30339,30348,30354,30361,30367,30381,30391,30395,30401,30415,30425,30429],{"__ignoreMap":286},[290,30179,30180],{"class":163,"line":292},[290,30181,29676],{"class":455},[290,30183,30184,30187],{"class":163,"line":330},[290,30185,30186],{"class":303},".spinner-group",[290,30188,450],{"class":295},[290,30190,30191,30193,30195,30197],{"class":163,"line":337},[290,30192,17742],{"class":461},[290,30194,465],{"class":295},[290,30196,9055],{"class":461},[290,30198,471],{"class":295},[290,30200,30201,30203,30205,30207,30209],{"class":163,"line":364},[290,30202,26819],{"class":461},[290,30204,465],{"class":295},[290,30206,176],{"class":461},[290,30208,674],{"class":541},[290,30210,471],{"class":295},[290,30212,30213,30216,30218,30220],{"class":163,"line":386},[290,30214,30215],{"class":461},"  align-items",[290,30217,465],{"class":295},[290,30219,9157],{"class":461},[290,30221,471],{"class":295},[290,30223,30224],{"class":163,"line":408},[290,30225,620],{"class":295},[290,30227,30228],{"class":163,"line":428},[290,30229,334],{"emptyLinePlaceholder":333},[290,30231,30232,30235],{"class":163,"line":517},[290,30233,30234],{"class":303},".spinner-dot",[290,30236,450],{"class":295},[290,30238,30239,30241,30243,30245,30247],{"class":163,"line":523},[290,30240,17904],{"class":461},[290,30242,465],{"class":295},[290,30244,836],{"class":461},[290,30246,674],{"class":541},[290,30248,471],{"class":295},[290,30250,30251,30253,30255,30257,30259],{"class":163,"line":532},[290,30252,18162],{"class":461},[290,30254,465],{"class":295},[290,30256,836],{"class":461},[290,30258,674],{"class":541},[290,30260,471],{"class":295},[290,30262,30263,30265,30267,30269,30271],{"class":163,"line":551},[290,30264,1663],{"class":461},[290,30266,465],{"class":295},[290,30268,1831],{"class":461},[290,30270,11018],{"class":541},[290,30272,471],{"class":295},[290,30274,30275,30277,30279,30282],{"class":163,"line":586},[290,30276,1186],{"class":461},[290,30278,465],{"class":295},[290,30280,30281],{"class":461},"#6366f1",[290,30283,471],{"class":295},[290,30285,30286,30288,30291,30293,30295,30297,30299],{"class":163,"line":602},[290,30287,6035],{"class":461},[290,30289,30290],{"class":295},": pulse ",[290,30292,1789],{"class":461},[290,30294,1886],{"class":541},[290,30296,21326],{"class":461},[290,30298,21329],{"class":461},[290,30300,471],{"class":295},[290,30302,30303,30305,30307,30309,30311,30313,30315,30317,30319,30321,30323,30325,30327,30329],{"class":163,"line":617},[290,30304,23038],{"class":461},[290,30306,465],{"class":295},[290,30308,3556],{"class":461},[290,30310,484],{"class":295},[290,30312,1622],{"class":461},[290,30314,484],{"class":295},[290,30316,24085],{"class":1561},[290,30318,569],{"class":295},[290,30320,487],{"class":461},[290,30322,490],{"class":295},[290,30324,4415],{"class":541},[290,30326,18082],{"class":461},[290,30328,1886],{"class":541},[290,30330,500],{"class":295},[290,30332,30333],{"class":163,"line":623},[290,30334,620],{"class":295},[290,30336,30337],{"class":163,"line":628},[290,30338,334],{"emptyLinePlaceholder":333},[290,30340,30341,30343,30346],{"class":163,"line":634},[290,30342,3968],{"class":541},[290,30344,30345],{"class":1561}," pulse",[290,30347,450],{"class":295},[290,30349,30350,30352],{"class":163,"line":649},[290,30351,21039],{"class":303},[290,30353,548],{"class":295},[290,30355,30356,30359],{"class":163,"line":660},[290,30357,30358],{"class":303},"  80%",[290,30360,548],{"class":295},[290,30362,30363,30365],{"class":163,"line":688},[290,30364,21111],{"class":303},[290,30366,450],{"class":295},[290,30368,30369,30371,30373,30375,30377,30379],{"class":163,"line":693},[290,30370,745],{"class":461},[290,30372,465],{"class":295},[290,30374,493],{"class":461},[290,30376,484],{"class":295},[290,30378,232],{"class":461},[290,30380,500],{"class":295},[290,30382,30383,30385,30387,30389],{"class":163,"line":698},[290,30384,733],{"class":461},[290,30386,465],{"class":295},[290,30388,169],{"class":461},[290,30390,471],{"class":295},[290,30392,30393],{"class":163,"line":704},[290,30394,771],{"class":295},[290,30396,30397,30399],{"class":163,"line":710},[290,30398,27594],{"class":303},[290,30400,450],{"class":295},[290,30402,30403,30405,30407,30409,30411,30413],{"class":163,"line":717},[290,30404,745],{"class":461},[290,30406,465],{"class":295},[290,30408,493],{"class":461},[290,30410,484],{"class":295},[290,30412,468],{"class":461},[290,30414,500],{"class":295},[290,30416,30417,30419,30421,30423],{"class":163,"line":730},[290,30418,733],{"class":461},[290,30420,465],{"class":295},[290,30422,468],{"class":461},[290,30424,471],{"class":295},[290,30426,30427],{"class":163,"line":742},[290,30428,771],{"class":295},[290,30430,30431],{"class":163,"line":768},[290,30432,620],{"class":295},[281,30434,30436],{"className":283,"code":30435,"language":285,"meta":286,"style":286},"\u003C!-- HTML -->\n\u003Cdiv class=\"spinner-group\">\n  \u003Cspan class=\"spinner-dot\" style=\"--i: 0\">\u003C\u002Fspan>\n  \u003Cspan class=\"spinner-dot\" style=\"--i: 1\">\u003C\u002Fspan>\n  \u003Cspan class=\"spinner-dot\" style=\"--i: 2\">\u003C\u002Fspan>\n\u003C\u002Fdiv>\n",[18,30437,30438,30442,30457,30484,30509,30534],{"__ignoreMap":286},[290,30439,30440],{"class":163,"line":292},[290,30441,29568],{"class":455},[290,30443,30444,30446,30448,30450,30452,30455],{"class":163,"line":330},[290,30445,296],{"class":295},[290,30447,342],{"class":299},[290,30449,314],{"class":303},[290,30451,307],{"class":295},[290,30453,30454],{"class":310},"\"spinner-group\"",[290,30456,327],{"class":295},[290,30458,30459,30461,30463,30465,30467,30470,30473,30475,30478,30480,30482],{"class":163,"line":337},[290,30460,367],{"class":295},[290,30462,290],{"class":299},[290,30464,314],{"class":303},[290,30466,307],{"class":295},[290,30468,30469],{"class":310},"\"spinner-dot\"",[290,30471,30472],{"class":303}," style",[290,30474,307],{"class":295},[290,30476,30477],{"class":310},"\"--i: 0\"",[290,30479,11472],{"class":295},[290,30481,290],{"class":299},[290,30483,327],{"class":295},[290,30485,30486,30488,30490,30492,30494,30496,30498,30500,30503,30505,30507],{"class":163,"line":364},[290,30487,367],{"class":295},[290,30489,290],{"class":299},[290,30491,314],{"class":303},[290,30493,307],{"class":295},[290,30495,30469],{"class":310},[290,30497,30472],{"class":303},[290,30499,307],{"class":295},[290,30501,30502],{"class":310},"\"--i: 1\"",[290,30504,11472],{"class":295},[290,30506,290],{"class":299},[290,30508,327],{"class":295},[290,30510,30511,30513,30515,30517,30519,30521,30523,30525,30528,30530,30532],{"class":163,"line":386},[290,30512,367],{"class":295},[290,30514,290],{"class":299},[290,30516,314],{"class":303},[290,30518,307],{"class":295},[290,30520,30469],{"class":310},[290,30522,30472],{"class":303},[290,30524,307],{"class":295},[290,30526,30527],{"class":310},"\"--i: 2\"",[290,30529,11472],{"class":295},[290,30531,290],{"class":299},[290,30533,327],{"class":295},[290,30535,30536,30538,30540],{"class":163,"line":408},[290,30537,431],{"class":295},[290,30539,342],{"class":299},[290,30541,327],{"class":295},[14,30543,30544,30546,30547,30549,30550,30552,30553,30555,30556,42],{},[62,30545,20503],{}," When sequencing multiple animations, prefer ",[18,30548,6980],{}," over JavaScript ",[18,30551,24],{},". For simpler, single-state changes, evaluate whether ",[27,30554,30],{"href":29}," provide a lighter-weight alternative before committing to ",[18,30557,3968],{},[47,30559],{},[50,30561,30563],{"id":30562},"performance-gpu-acceleration-strategies","Performance & GPU Acceleration Strategies",[14,30565,30566,30567,569,30569,569,30571,569,30573,1745,30575,30577,30578,569,30580,8393,30582,30584],{},"Achieving consistent 60fps requires strict adherence to compositor-only properties. Animating ",[18,30568,1748],{},[18,30570,2722],{},[18,30572,2728],{},[18,30574,2731],{},[18,30576,2725],{}," forces synchronous layout recalculations (reflow), which blocks the main thread. Restrict motion to ",[18,30579,103],{},[18,30581,76],{},[18,30583,6792],{}," to leverage the browser's compositor thread.",[2757,30586,30588],{"id":30587},"implementation-hardware-accelerated-morphing","Implementation: Hardware-Accelerated Morphing",[14,30590,1499,30591,2351,30594,30597,30598,30600],{},[18,30592,30593],{},"transform: matrix3d()",[18,30595,30596],{},"translate3d()"," to force GPU layer promotion, and apply ",[18,30599,3797],{}," strategically.",[281,30602,30604],{"className":438,"code":30603,"language":440,"meta":286,"style":286},"\u002F* CSS *\u002F\n.morph-container {\n  \u002F* Force GPU layer creation *\u002F\n  transform: translateZ(0);\n  will-change: transform, opacity;\n}\n\n.morph-target {\n  animation: hardware-morph 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;\n}\n\n@keyframes hardware-morph {\n  0% {\n    transform: scale(0.8) translate3d(0, -10px, 0);\n    opacity: 0;\n  }\n  100% {\n    transform: scale(1) translate3d(0, 0, 0);\n    opacity: 1;\n  }\n}\n\n\u002F* Cleanup will-change post-animation to free memory *\u002F\n.morph-target.animation-complete {\n  will-change: auto;\n}\n",[18,30605,30606,30610,30617,30622,30636,30642,30646,30650,30657,30692,30696,30700,30709,30715,30749,30759,30763,30769,30799,30809,30813,30817,30821,30826,30833,30843],{"__ignoreMap":286},[290,30607,30608],{"class":163,"line":292},[290,30609,29676],{"class":455},[290,30611,30612,30615],{"class":163,"line":330},[290,30613,30614],{"class":303},".morph-container",[290,30616,450],{"class":295},[290,30618,30619],{"class":163,"line":337},[290,30620,30621],{"class":455},"  \u002F* Force GPU layer creation *\u002F\n",[290,30623,30624,30626,30628,30630,30632,30634],{"class":163,"line":364},[290,30625,476],{"class":461},[290,30627,465],{"class":295},[290,30629,15490],{"class":461},[290,30631,484],{"class":295},[290,30633,487],{"class":461},[290,30635,500],{"class":295},[290,30637,30638,30640],{"class":163,"line":386},[290,30639,3518],{"class":461},[290,30641,3521],{"class":295},[290,30643,30644],{"class":163,"line":408},[290,30645,620],{"class":295},[290,30647,30648],{"class":163,"line":428},[290,30649,334],{"emptyLinePlaceholder":333},[290,30651,30652,30655],{"class":163,"line":517},[290,30653,30654],{"class":303},".morph-target",[290,30656,450],{"class":295},[290,30658,30659,30661,30664,30666,30668,30670,30672,30674,30676,30678,30680,30682,30684,30686,30688,30690],{"class":163,"line":523},[290,30660,6035],{"class":461},[290,30662,30663],{"class":295},": hardware-morph ",[290,30665,232],{"class":461},[290,30667,1886],{"class":541},[290,30669,561],{"class":461},[290,30671,484],{"class":295},[290,30673,169],{"class":461},[290,30675,569],{"class":295},[290,30677,487],{"class":461},[290,30679,569],{"class":295},[290,30681,566],{"class":461},[290,30683,569],{"class":295},[290,30685,468],{"class":461},[290,30687,490],{"class":295},[290,30689,7063],{"class":461},[290,30691,471],{"class":295},[290,30693,30694],{"class":163,"line":532},[290,30695,620],{"class":295},[290,30697,30698],{"class":163,"line":551},[290,30699,334],{"emptyLinePlaceholder":333},[290,30701,30702,30704,30707],{"class":163,"line":586},[290,30703,3968],{"class":541},[290,30705,30706],{"class":1561}," hardware-morph",[290,30708,450],{"class":295},[290,30710,30711,30713],{"class":163,"line":602},[290,30712,21039],{"class":303},[290,30714,450],{"class":295},[290,30716,30717,30719,30721,30723,30725,30727,30729,30732,30734,30736,30738,30741,30743,30745,30747],{"class":163,"line":617},[290,30718,745],{"class":461},[290,30720,465],{"class":295},[290,30722,493],{"class":461},[290,30724,484],{"class":295},[290,30726,572],{"class":461},[290,30728,490],{"class":295},[290,30730,30731],{"class":461},"translate3d",[290,30733,484],{"class":295},[290,30735,487],{"class":461},[290,30737,569],{"class":295},[290,30739,30740],{"class":461},"-10",[290,30742,674],{"class":541},[290,30744,569],{"class":295},[290,30746,487],{"class":461},[290,30748,500],{"class":295},[290,30750,30751,30753,30755,30757],{"class":163,"line":623},[290,30752,733],{"class":461},[290,30754,465],{"class":295},[290,30756,487],{"class":461},[290,30758,471],{"class":295},[290,30760,30761],{"class":163,"line":628},[290,30762,771],{"class":295},[290,30764,30765,30767],{"class":163,"line":634},[290,30766,21111],{"class":303},[290,30768,450],{"class":295},[290,30770,30771,30773,30775,30777,30779,30781,30783,30785,30787,30789,30791,30793,30795,30797],{"class":163,"line":649},[290,30772,745],{"class":461},[290,30774,465],{"class":295},[290,30776,493],{"class":461},[290,30778,484],{"class":295},[290,30780,468],{"class":461},[290,30782,490],{"class":295},[290,30784,30731],{"class":461},[290,30786,484],{"class":295},[290,30788,487],{"class":461},[290,30790,569],{"class":295},[290,30792,487],{"class":461},[290,30794,569],{"class":295},[290,30796,487],{"class":461},[290,30798,500],{"class":295},[290,30800,30801,30803,30805,30807],{"class":163,"line":660},[290,30802,733],{"class":461},[290,30804,465],{"class":295},[290,30806,468],{"class":461},[290,30808,471],{"class":295},[290,30810,30811],{"class":163,"line":688},[290,30812,771],{"class":295},[290,30814,30815],{"class":163,"line":693},[290,30816,620],{"class":295},[290,30818,30819],{"class":163,"line":698},[290,30820,334],{"emptyLinePlaceholder":333},[290,30822,30823],{"class":163,"line":704},[290,30824,30825],{"class":455},"\u002F* Cleanup will-change post-animation to free memory *\u002F\n",[290,30827,30828,30831],{"class":163,"line":710},[290,30829,30830],{"class":303},".morph-target.animation-complete",[290,30832,450],{"class":295},[290,30834,30835,30837,30839,30841],{"class":163,"line":717},[290,30836,3518],{"class":461},[290,30838,465],{"class":295},[290,30840,250],{"class":461},[290,30842,471],{"class":295},[290,30844,30845],{"class":163,"line":730},[290,30846,620],{"class":295},[2757,30848,13396],{"id":8357},[3017,30850,30851,30857,30868],{},[1396,30852,30853,30856],{},[62,30854,30855],{},"Performance Tab:"," Record a 3-second trace while the animation runs. Look for red \"Layout\" or \"Paint\" bars. If present, you're animating non-compositor properties.",[1396,30858,30859,30862,30863,2351,30866,42],{},[62,30860,30861],{},"Layers Panel:"," Toggle \"Paint Flashing\" and \"Layer Borders\". Compositor-promoted elements render with a blue border. If your animated element lacks one, add ",[18,30864,30865],{},"transform: translateZ(0)",[18,30867,2075],{},[1396,30869,30870,30873,30874,30876,30877,30879],{},[62,30871,30872],{},"Console Warnings:"," Browsers warn when ",[18,30875,3797],{}," is applied to too many elements. Scope it to active states only and remove it via class toggling or ",[18,30878,2766],{}," listeners if JS is available.",[14,30881,30882,30884],{},[62,30883,15215],{}," CSS Will Change Module Level 1, CSS Transforms Level 2",[47,30886],{},[50,30888,30890],{"id":30889},"advanced-component-patterns-interactive-controls","Advanced Component Patterns: Interactive Controls",[14,30892,30893,30894,569,30896,30898,30899,30901,30902,30904],{},"Complex form elements and UI controls can leverage keyframes for rich, accessible feedback without JavaScript. By animating pseudo-elements (",[18,30895,2811],{},[18,30897,2412],{},") and routing through ",[18,30900,8708],{}," states, you maintain semantic HTML while delivering polished motion. This architecture scales cleanly into ",[27,30903,23933],{"href":23932}," and custom radio groups.",[2757,30906,30908],{"id":30907},"implementation-accessible-toggle-with-reduced-motion-fallback","Implementation: Accessible Toggle with Reduced Motion Fallback",[14,30910,30911,30912,30914],{},"Always wrap complex sequences in ",[18,30913,2584],{}," to respect user OS settings. The fallback should not disable motion entirely; instead, simplify it to instant opacity\u002Ftransform shifts.",[281,30916,30918],{"className":438,"code":30917,"language":440,"meta":286,"style":286},"\u002F* CSS *\u002F\n.toggle-input {\n  position: absolute;\n  opacity: 0;\n  pointer-events: none;\n}\n\n.toggle-track {\n  display: block;\n  width: 48px;\n  height: 26px;\n  background: #cbd5e1;\n  border-radius: 13px;\n  position: relative;\n  cursor: pointer;\n  transition: background 0.2s;\n}\n\n.toggle-thumb {\n  position: absolute;\n  top: 3px;\n  left: 3px;\n  width: 20px;\n  height: 20px;\n  background: white;\n  border-radius: 50%;\n  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);\n  animation: thumb-idle 0.3s ease-out forwards;\n}\n\n.toggle-input:checked + .toggle-track .toggle-thumb {\n  animation: thumb-active 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;\n}\n\n@keyframes thumb-active {\n  0% {\n    transform: translateX(0);\n  }\n  100% {\n    transform: translateX(22px);\n  }\n}\n\n@keyframes thumb-idle {\n  0% {\n    transform: translateX(22px);\n  }\n  100% {\n    transform: translateX(0);\n  }\n}\n\n\u002F* Accessibility Fallback *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .toggle-thumb {\n    animation: none;\n    transition: transform 0.01s linear;\n  }\n  .toggle-input:checked + .toggle-track .toggle-thumb {\n    transform: translateX(22px);\n  }\n}\n",[18,30919,30920,30924,30931,30941,30951,30961,30965,30969,30976,30986,30998,31010,31020,31033,31043,31053,31067,31071,31075,31082,31092,31104,31116,31128,31140,31151,31163,31199,31216,31220,31224,31239,31274,31278,31282,31291,31297,31311,31315,31321,31337,31341,31345,31349,31358,31364,31380,31384,31390,31404,31408,31412,31416,31421,31427,31434,31444,31458,31462,31475,31491,31495],{"__ignoreMap":286},[290,30921,30922],{"class":163,"line":292},[290,30923,29676],{"class":455},[290,30925,30926,30929],{"class":163,"line":330},[290,30927,30928],{"class":303},".toggle-input",[290,30930,450],{"class":295},[290,30932,30933,30935,30937,30939],{"class":163,"line":337},[290,30934,1866],{"class":461},[290,30936,465],{"class":295},[290,30938,1927],{"class":461},[290,30940,471],{"class":295},[290,30942,30943,30945,30947,30949],{"class":163,"line":364},[290,30944,462],{"class":461},[290,30946,465],{"class":295},[290,30948,487],{"class":461},[290,30950,471],{"class":295},[290,30952,30953,30955,30957,30959],{"class":163,"line":386},[290,30954,2008],{"class":461},[290,30956,465],{"class":295},[290,30958,72],{"class":461},[290,30960,471],{"class":295},[290,30962,30963],{"class":163,"line":408},[290,30964,620],{"class":295},[290,30966,30967],{"class":163,"line":428},[290,30968,334],{"emptyLinePlaceholder":333},[290,30970,30971,30974],{"class":163,"line":517},[290,30972,30973],{"class":303},".toggle-track",[290,30975,450],{"class":295},[290,30977,30978,30980,30982,30984],{"class":163,"line":523},[290,30979,17742],{"class":461},[290,30981,465],{"class":295},[290,30983,68],{"class":461},[290,30985,471],{"class":295},[290,30987,30988,30990,30992,30994,30996],{"class":163,"line":532},[290,30989,17904],{"class":461},[290,30991,465],{"class":295},[290,30993,5139],{"class":461},[290,30995,674],{"class":541},[290,30997,471],{"class":295},[290,30999,31000,31002,31004,31006,31008],{"class":163,"line":551},[290,31001,18162],{"class":461},[290,31003,465],{"class":295},[290,31005,26451],{"class":461},[290,31007,674],{"class":541},[290,31009,471],{"class":295},[290,31011,31012,31014,31016,31018],{"class":163,"line":586},[290,31013,1186],{"class":461},[290,31015,465],{"class":295},[290,31017,28226],{"class":461},[290,31019,471],{"class":295},[290,31021,31022,31024,31026,31029,31031],{"class":163,"line":602},[290,31023,1663],{"class":461},[290,31025,465],{"class":295},[290,31027,31028],{"class":461},"13",[290,31030,674],{"class":541},[290,31032,471],{"class":295},[290,31034,31035,31037,31039,31041],{"class":163,"line":617},[290,31036,1866],{"class":461},[290,31038,465],{"class":295},[290,31040,1871],{"class":461},[290,31042,471],{"class":295},[290,31044,31045,31047,31049,31051],{"class":163,"line":623},[290,31046,17841],{"class":461},[290,31048,465],{"class":295},[290,31050,18849],{"class":461},[290,31052,471],{"class":295},[290,31054,31055,31057,31059,31061,31063,31065],{"class":163,"line":628},[290,31056,526],{"class":461},[290,31058,465],{"class":295},[290,31060,1217],{"class":1216},[290,31062,28261],{"class":461},[290,31064,1886],{"class":541},[290,31066,471],{"class":295},[290,31068,31069],{"class":163,"line":634},[290,31070,620],{"class":295},[290,31072,31073],{"class":163,"line":649},[290,31074,334],{"emptyLinePlaceholder":333},[290,31076,31077,31080],{"class":163,"line":660},[290,31078,31079],{"class":303},".toggle-thumb",[290,31081,450],{"class":295},[290,31083,31084,31086,31088,31090],{"class":163,"line":688},[290,31085,1866],{"class":461},[290,31087,465],{"class":295},[290,31089,1927],{"class":461},[290,31091,471],{"class":295},[290,31093,31094,31096,31098,31100,31102],{"class":163,"line":693},[290,31095,28297],{"class":461},[290,31097,465],{"class":295},[290,31099,1579],{"class":461},[290,31101,674],{"class":541},[290,31103,471],{"class":295},[290,31105,31106,31108,31110,31112,31114],{"class":163,"line":698},[290,31107,17877],{"class":461},[290,31109,465],{"class":295},[290,31111,1579],{"class":461},[290,31113,674],{"class":541},[290,31115,471],{"class":295},[290,31117,31118,31120,31122,31124,31126],{"class":163,"line":704},[290,31119,17904],{"class":461},[290,31121,465],{"class":295},[290,31123,1785],{"class":461},[290,31125,674],{"class":541},[290,31127,471],{"class":295},[290,31129,31130,31132,31134,31136,31138],{"class":163,"line":710},[290,31131,18162],{"class":461},[290,31133,465],{"class":295},[290,31135,1785],{"class":461},[290,31137,674],{"class":541},[290,31139,471],{"class":295},[290,31141,31142,31144,31146,31149],{"class":163,"line":717},[290,31143,1186],{"class":461},[290,31145,465],{"class":295},[290,31147,31148],{"class":461},"white",[290,31150,471],{"class":295},[290,31152,31153,31155,31157,31159,31161],{"class":163,"line":730},[290,31154,1663],{"class":461},[290,31156,465],{"class":295},[290,31158,1831],{"class":461},[290,31160,11018],{"class":541},[290,31162,471],{"class":295},[290,31164,31165,31167,31169,31171,31173,31175,31177,31179,31181,31183,31185,31187,31189,31191,31193,31195,31197],{"class":163,"line":742},[290,31166,3207],{"class":461},[290,31168,465],{"class":295},[290,31170,487],{"class":461},[290,31172,804],{"class":461},[290,31174,674],{"class":541},[290,31176,19770],{"class":461},[290,31178,674],{"class":541},[290,31180,3224],{"class":461},[290,31182,484],{"class":295},[290,31184,487],{"class":461},[290,31186,569],{"class":295},[290,31188,487],{"class":461},[290,31190,569],{"class":295},[290,31192,487],{"class":461},[290,31194,569],{"class":295},[290,31196,566],{"class":461},[290,31198,500],{"class":295},[290,31200,31201,31203,31206,31208,31210,31212,31214],{"class":163,"line":768},[290,31202,6035],{"class":461},[290,31204,31205],{"class":295},": thumb-idle ",[290,31207,199],{"class":461},[290,31209,1886],{"class":541},[290,31211,1889],{"class":461},[290,31213,6048],{"class":461},[290,31215,471],{"class":295},[290,31217,31218],{"class":163,"line":774},[290,31219,620],{"class":295},[290,31221,31222],{"class":163,"line":779},[290,31223,334],{"emptyLinePlaceholder":333},[290,31225,31226,31229,31231,31234,31237],{"class":163,"line":784},[290,31227,31228],{"class":303},".toggle-input:checked",[290,31230,3592],{"class":541},[290,31232,31233],{"class":303}," .toggle-track",[290,31235,31236],{"class":303}," .toggle-thumb",[290,31238,450],{"class":295},[290,31240,31241,31243,31246,31248,31250,31252,31254,31256,31258,31260,31262,31264,31266,31268,31270,31272],{"class":163,"line":812},[290,31242,6035],{"class":461},[290,31244,31245],{"class":295},": thumb-active ",[290,31247,199],{"class":461},[290,31249,1886],{"class":541},[290,31251,561],{"class":461},[290,31253,484],{"class":295},[290,31255,14044],{"class":461},[290,31257,569],{"class":295},[290,31259,14049],{"class":461},[290,31261,569],{"class":295},[290,31263,14054],{"class":461},[290,31265,569],{"class":295},[290,31267,468],{"class":461},[290,31269,490],{"class":295},[290,31271,7063],{"class":461},[290,31273,471],{"class":295},[290,31275,31276],{"class":163,"line":860},[290,31277,620],{"class":295},[290,31279,31280],{"class":163,"line":865},[290,31281,334],{"emptyLinePlaceholder":333},[290,31283,31284,31286,31289],{"class":163,"line":871},[290,31285,3968],{"class":541},[290,31287,31288],{"class":1561}," thumb-active",[290,31290,450],{"class":295},[290,31292,31293,31295],{"class":163,"line":880},[290,31294,21039],{"class":303},[290,31296,450],{"class":295},[290,31298,31299,31301,31303,31305,31307,31309],{"class":163,"line":896},[290,31300,745],{"class":461},[290,31302,465],{"class":295},[290,31304,27966],{"class":461},[290,31306,484],{"class":295},[290,31308,487],{"class":461},[290,31310,500],{"class":295},[290,31312,31313],{"class":163,"line":4734},[290,31314,771],{"class":295},[290,31316,31317,31319],{"class":163,"line":4742},[290,31318,21111],{"class":303},[290,31320,450],{"class":295},[290,31322,31323,31325,31327,31329,31331,31333,31335],{"class":163,"line":4761},[290,31324,745],{"class":461},[290,31326,465],{"class":295},[290,31328,27966],{"class":461},[290,31330,484],{"class":295},[290,31332,17581],{"class":461},[290,31334,674],{"class":541},[290,31336,500],{"class":295},[290,31338,31339],{"class":163,"line":4766},[290,31340,771],{"class":295},[290,31342,31343],{"class":163,"line":4786},[290,31344,620],{"class":295},[290,31346,31347],{"class":163,"line":9635},[290,31348,334],{"emptyLinePlaceholder":333},[290,31350,31351,31353,31356],{"class":163,"line":9641},[290,31352,3968],{"class":541},[290,31354,31355],{"class":1561}," thumb-idle",[290,31357,450],{"class":295},[290,31359,31360,31362],{"class":163,"line":9647},[290,31361,21039],{"class":303},[290,31363,450],{"class":295},[290,31365,31366,31368,31370,31372,31374,31376,31378],{"class":163,"line":9665},[290,31367,745],{"class":461},[290,31369,465],{"class":295},[290,31371,27966],{"class":461},[290,31373,484],{"class":295},[290,31375,17581],{"class":461},[290,31377,674],{"class":541},[290,31379,500],{"class":295},[290,31381,31382],{"class":163,"line":9683},[290,31383,771],{"class":295},[290,31385,31386,31388],{"class":163,"line":9688},[290,31387,21111],{"class":303},[290,31389,450],{"class":295},[290,31391,31392,31394,31396,31398,31400,31402],{"class":163,"line":9694},[290,31393,745],{"class":461},[290,31395,465],{"class":295},[290,31397,27966],{"class":461},[290,31399,484],{"class":295},[290,31401,487],{"class":461},[290,31403,500],{"class":295},[290,31405,31406],{"class":163,"line":9700},[290,31407,771],{"class":295},[290,31409,31410],{"class":163,"line":9710},[290,31411,620],{"class":295},[290,31413,31414],{"class":163,"line":9716},[290,31415,334],{"emptyLinePlaceholder":333},[290,31417,31418],{"class":163,"line":9738},[290,31419,31420],{"class":455},"\u002F* Accessibility Fallback *\u002F\n",[290,31422,31423,31425],{"class":163,"line":9748},[290,31424,874],{"class":541},[290,31426,877],{"class":295},[290,31428,31429,31432],{"class":163,"line":9754},[290,31430,31431],{"class":303},"  .toggle-thumb",[290,31433,450],{"class":295},[290,31435,31436,31438,31440,31442],{"class":163,"line":9760},[290,31437,6152],{"class":461},[290,31439,465],{"class":295},[290,31441,72],{"class":461},[290,31443,471],{"class":295},[290,31445,31446,31448,31450,31452,31454,31456],{"class":163,"line":9777},[290,31447,4745],{"class":461},[290,31449,1880],{"class":295},[290,31451,2831],{"class":461},[290,31453,1886],{"class":541},[290,31455,3159],{"class":461},[290,31457,471],{"class":295},[290,31459,31460],{"class":163,"line":9787},[290,31461,771],{"class":295},[290,31463,31464,31467,31469,31471,31473],{"class":163,"line":9793},[290,31465,31466],{"class":303},"  .toggle-input:checked",[290,31468,3592],{"class":541},[290,31470,31233],{"class":303},[290,31472,31236],{"class":303},[290,31474,450],{"class":295},[290,31476,31477,31479,31481,31483,31485,31487,31489],{"class":163,"line":9799},[290,31478,745],{"class":461},[290,31480,465],{"class":295},[290,31482,27966],{"class":461},[290,31484,484],{"class":295},[290,31486,17581],{"class":461},[290,31488,674],{"class":541},[290,31490,500],{"class":295},[290,31492,31493],{"class":163,"line":9805},[290,31494,771],{"class":295},[290,31496,31497],{"class":163,"line":9811},[290,31498,620],{"class":295},[14,31500,31501,31503],{},[62,31502,15215],{}," CSS UI Level 4, WAI-ARIA Authoring Practices Guide (Motion & Reduced Motion)",[47,31505],{},[50,31507,15880],{"id":15879},[14,31509,31510,31511,31515,31516,3942,31518,31520,31521,69,31523,31526,31527,723],{},"Full support across modern browsers (Chrome 115+, Firefox 120+, Safari 16.4+). Requires graceful degradation for legacy WebKit\u002FBlink engines. Animations can also be gated on a component's own size rather than the viewport — see ",[27,31512,31514],{"href":31513},"\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fcontainer-query-triggered-keyframe-animations\u002F","container-query-triggered keyframe animations"," for combining ",[18,31517,12001],{},[18,31519,3968],{},". Experimental features like ",[18,31522,8833],{},[18,31524,31525],{},"@scroll-timeline"," should be progressively enhanced using ",[18,31528,2086],{},[281,31530,31532],{"className":438,"code":31531,"language":440,"meta":286,"style":286},"@supports (animation-timeline: scroll()) {\n  .scroll-driven-element {\n    animation: fade-in linear both;\n    animation-timeline: scroll(root);\n  }\n}\n\n\u002F* Fallback for unsupported browsers *\u002F\n@supports not (animation-timeline: scroll()) {\n  .scroll-driven-element {\n    animation: fade-in 1s ease-out forwards;\n    animation-timeline: none;\n  }\n}\n",[18,31533,31534,31549,31556,31569,31581,31585,31589,31593,31598,31614,31620,31636,31646,31650],{"__ignoreMap":286},[290,31535,31536,31538,31540,31542,31544,31546],{"class":163,"line":292},[290,31537,2086],{"class":541},[290,31539,3595],{"class":295},[290,31541,8833],{"class":461},[290,31543,465],{"class":295},[290,31545,4561],{"class":461},[290,31547,31548],{"class":295},"()) {\n",[290,31550,31551,31554],{"class":163,"line":330},[290,31552,31553],{"class":303},"  .scroll-driven-element",[290,31555,450],{"class":295},[290,31557,31558,31560,31563,31565,31567],{"class":163,"line":337},[290,31559,6152],{"class":461},[290,31561,31562],{"class":295},": fade-in ",[290,31564,5456],{"class":461},[290,31566,22990],{"class":461},[290,31568,471],{"class":295},[290,31570,31571,31574,31576,31578],{"class":163,"line":364},[290,31572,31573],{"class":461},"    animation-timeline",[290,31575,465],{"class":295},[290,31577,4561],{"class":461},[290,31579,31580],{"class":295},"(root);\n",[290,31582,31583],{"class":163,"line":386},[290,31584,771],{"class":295},[290,31586,31587],{"class":163,"line":408},[290,31588,620],{"class":295},[290,31590,31591],{"class":163,"line":428},[290,31592,334],{"emptyLinePlaceholder":333},[290,31594,31595],{"class":163,"line":517},[290,31596,31597],{"class":455},"\u002F* Fallback for unsupported browsers *\u002F\n",[290,31599,31600,31602,31604,31606,31608,31610,31612],{"class":163,"line":523},[290,31601,2086],{"class":541},[290,31603,2116],{"class":541},[290,31605,3595],{"class":295},[290,31607,8833],{"class":461},[290,31609,465],{"class":295},[290,31611,4561],{"class":461},[290,31613,31548],{"class":295},[290,31615,31616,31618],{"class":163,"line":532},[290,31617,31553],{"class":303},[290,31619,450],{"class":295},[290,31621,31622,31624,31626,31628,31630,31632,31634],{"class":163,"line":551},[290,31623,6152],{"class":461},[290,31625,31562],{"class":295},[290,31627,468],{"class":461},[290,31629,1886],{"class":541},[290,31631,1889],{"class":461},[290,31633,6048],{"class":461},[290,31635,471],{"class":295},[290,31637,31638,31640,31642,31644],{"class":163,"line":586},[290,31639,31573],{"class":461},[290,31641,465],{"class":295},[290,31643,72],{"class":461},[290,31645,471],{"class":295},[290,31647,31648],{"class":163,"line":602},[290,31649,771],{"class":295},[290,31651,31652],{"class":163,"line":617},[290,31653,620],{"class":295},[47,31655],{},[50,31657,29243],{"id":29242},[2250,31659,31660,31670],{},[2253,31661,31662],{},[2256,31663,31664,31666,31668],{},[2259,31665,2338],{},[2259,31667,3876],{},[2259,31669,8563],{},[2269,31671,31672,31699,31719,31737],{},[2256,31673,31674,31679,31689],{},[2274,31675,31676],{},[62,31677,31678],{},"Layout Thrashing",[2274,31680,16092,31681,569,31683,569,31685,569,31687],{},[18,31682,1748],{},[18,31684,2722],{},[18,31686,2728],{},[18,31688,2731],{},[2274,31690,31691,31692,3041,31694,2401,31696,31698],{},"Restrict to ",[18,31693,103],{},[18,31695,76],{},[18,31697,25012],{}," for size changes.",[2256,31700,31701,31706,31712],{},[2274,31702,31703],{},[62,31704,31705],{},"Memory Leaks",[2274,31707,31708,31709,31711],{},"Persistent ",[18,31710,3797],{}," declarations",[2274,31713,31714,31715,31718],{},"Apply only during active states. Remove via ",[18,31716,31717],{},"will-change: auto"," or class swap.",[2256,31720,31721,31726,31729],{},[2274,31722,31723],{},[62,31724,31725],{},"Inconsistent Easing",[2274,31727,31728],{},"Vendor-specific cubic-bezier interpolation",[2274,31730,31731,31732,2351,31734,31736],{},"Test across engines. Use standard ",[18,31733,5411],{},[18,31735,7084],{}," for cross-browser parity.",[2256,31738,31739,31744,31749],{},[2274,31740,31741],{},[62,31742,31743],{},"Accessibility Violations",[2274,31745,31746,31747],{},"Ignoring ",[18,31748,2584],{},[2274,31750,21710,31751,31753],{},[18,31752,4126],{}," with instant state swaps. Never remove motion entirely; simplify it.",[47,31755],{},[50,31757,1316],{"id":1315},[14,31759,31760,31763,31764,569,31766,8393,31768,31770,31771,569,31773,569,31775,1745,31777,31779,31780,31782,31783,31786],{},[62,31761,31762],{},"How do I prevent keyframe animations from causing layout thrashing?","\nRestrict animated properties to ",[18,31765,103],{},[18,31767,76],{},[18,31769,6792],{},". These are handled by the GPU compositor. Avoid animating ",[18,31772,1748],{},[18,31774,2722],{},[18,31776,2728],{},[18,31778,2731],{},", which trigger expensive layout recalculations. If dimensional changes are required, use ",[18,31781,2071],{}," and adjust ",[18,31784,31785],{},"transform-origin"," accordingly.",[14,31788,31789,4968,31795,31797,31798,31800,31801,31803,31804,42],{},[62,31790,31791,31792,31794],{},"Can I chain multiple ",[18,31793,3968],{}," without JavaScript?",[18,31796,6980],{}," offsets, CSS custom properties to control iteration counts, or the modern ",[18,31799,8833],{}," API for scroll-driven sequencing. For sequential playback on a single element, define multiple keyframes in the ",[18,31802,5178],{}," shorthand: ",[18,31805,31806],{},"animation: slideIn 0.3s forwards, fadeOut 0.3s 0.3s forwards;",[14,31808,31809,31814,31815,31817,31818,2351,31820,31823],{},[62,31810,31811,31812,5734],{},"How should keyframe architectures handle ",[18,31813,2584],{},"\nWrap complex sequences in ",[18,31816,4126],{}," and replace them with instant state changes using ",[18,31819,76],{},[18,31821,31822],{},"transform: none",". Never disable motion entirely; simplify it. Provide a direct visual state change that maintains usability without vestibular triggers.",[47,31825],{},[50,31827,1391],{"id":1390},[1393,31829,31830,31835,31840,31849,31855],{},[1396,31831,31832,31834],{},[27,31833,1481],{"href":1480}," — the parent guide tying transitions, keyframes, and accessibility together.",[1396,31836,31837,31839],{},[27,31838,27916],{"href":27915}," — script-free loading feedback built from a single keyframe.",[1396,31841,31842,31845,31846,31848],{},[27,31843,31844],{"href":26277},"Staggered list animations with custom properties"," — drive per-item delay offsets with a ",[18,31847,24085],{}," variable.",[1396,31850,31851,31854],{},[27,31852,31853],{"href":31513},"Container-query-triggered keyframe animations"," — fire animations based on a component's own width.",[1396,31856,31857,31859],{},[27,31858,4037],{"href":4036}," — the container-query foundation those size-aware animations build on.",[1430,31861,31862],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":286,"searchDepth":330,"depth":330,"links":31864},[31865,31868,31871,31875,31878,31879,31880,31881],{"id":29529,"depth":330,"text":29530,"children":31866},[31867],{"id":29551,"depth":337,"text":29552},{"id":30147,"depth":330,"text":30148,"children":31869},[31870],{"id":30165,"depth":337,"text":30166},{"id":30562,"depth":330,"text":30563,"children":31872},[31873,31874],{"id":30587,"depth":337,"text":30588},{"id":8357,"depth":337,"text":13396},{"id":30889,"depth":330,"text":30890,"children":31876},[31877],{"id":30907,"depth":337,"text":30908},{"id":15879,"depth":330,"text":15880},{"id":29242,"depth":330,"text":29243},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Production-ready CSS keyframe animation patterns: declarative state mapping, GPU-optimised transforms, and spec-compliant motion architectures for modern UIs.",{"seoTitle":31884,"datePublished":1447,"dateModified":1447,"faq":31885},"CSS Keyframe Animation Patterns Guide",[31886,31888,31891],{"q":31762,"a":31887},"Restrict animated properties to transform, opacity, and filter, which are handled by the GPU compositor. Avoid animating width, height, top, or left; if dimensional changes are required, use transform: scale() and adjust transform-origin accordingly.",{"q":31889,"a":31890},"Can I chain multiple @keyframes without JavaScript?","Yes. Use animation-delay offsets, custom properties to control iteration counts, or animation-timeline for scroll-driven sequencing. For sequential playback on one element, list multiple animations in the animation shorthand.",{"q":31892,"a":31893},"How should keyframe architectures handle prefers-reduced-motion?","Wrap complex sequences in @media (prefers-reduced-motion: reduce) and replace them with instant state changes using opacity or transform: none. Never disable motion entirely; simplify it to a direct visual change that avoids vestibular triggers.","\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns",{"title":29442,"description":31882},"css-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Findex","l7AziCiAfY0Vb9PktKmP_aiujSs1ZfLdwRpF7XcRGyo",{"id":31899,"title":31900,"body":31901,"description":32934,"extension":1444,"meta":32935,"navigation":333,"path":32946,"seo":32947,"stem":32948,"__hash__":32949},"content\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fstaggered-list-animations-with-custom-properties\u002Findex.md","Staggered List Animations Using --i Index Custom Properties",{"type":7,"value":31902,"toc":32925},[31903,31906,31921,31926,31949,31951,31955,31976,31986,31988,31990,32011,32081,32209,32524,32546,32548,32552,32591,32593,32597,32609,32798,32805,32807,32809,32833,32835,32837,32852,32867,32881,32889,32891,32893,32922],[10,31904,31900],{"id":31905},"staggered-list-animations-using-i-index-custom-properties",[14,31907,31908,31909,31911,31912,31914,31915,31917,31918,25342],{},"When a menu, card grid, or notification list appears, animating every item at the same instant looks flat; revealing them in a quick cascade reads as intentional and polished. The narrow problem here: produce that staggered entrance with pure CSS by storing each item's position in an ",[18,31910,24085],{}," custom property and multiplying it into ",[18,31913,6980],{},", so item zero starts immediately, item one a beat later, and so on — with no JavaScript timing loop. This pattern is part of the broader ",[27,31916,25336],{"href":1419}," guide and leans on the same custom-property discipline described in the ",[27,31919,31920],{"href":10147},"CSS custom properties architecture",[14,31922,31923],{},[62,31924,31925],{},"What this technique gives you:",[1393,31927,31928,31934,31940,31946],{},[1396,31929,31930,31931,31933],{},"One ",[18,31932,3968],{}," rule shared by every item.",[1396,31935,31936,31937,31939],{},"A per-item delay derived from a single ",[18,31938,24085],{}," token.",[1396,31941,31942,31943,42],{},"Zero runtime JavaScript — the index is static markup or ",[18,31944,31945],{},"nth-child",[1396,31947,31948],{},"A clean reduced-motion path that reveals everything at once.",[47,31950],{},[50,31952,31954],{"id":31953},"why-a-custom-property-beats-hand-written-delays","Why a Custom Property Beats Hand-Written Delays",[14,31956,31957,31958,31960,31961,569,31964,31967,31968,31971,31972,31975],{},"The obvious approach is to write ",[18,31959,6980],{}," by hand for each child: ",[18,31962,31963],{},":nth-child(1) { animation-delay: 0ms }",[18,31965,31966],{},":nth-child(2) { animation-delay: 60ms }",", and so on. That works for three items and rots immediately afterwards — every list length needs a new block of rules, and changing the rhythm means editing every line. Promoting the index to a custom property collapses all of that to one declaration: ",[18,31969,31970],{},"animation-delay: calc(var(--i) * 60ms)",". The cascade computes the right delay per element, and tuning the whole sequence is a single ",[18,31973,31974],{},"60ms"," edit.",[14,31977,31978,31979,31981,31982,31985],{},"The reason to prefer CSS over a JavaScript stagger (such as a library that sets ",[18,31980,24],{}," per node) is the same as for any entrance animation: the work is declarative, ships no bytes of script, and the browser schedules it on the compositor. The accessibility tradeoff is the one to watch — a stagger is ",[86,31983,31984],{},"additive latency",". Each item's content is invisible until its delay elapses, so a 12-item list at 80ms each hides the last item for nearly a second. Keep per-item steps small, cap total duration, and always collapse the stagger when the user prefers reduced motion. A stagger is a flourish, never a gate on reading content.",[47,31987],{},[50,31989,25403],{"id":4202},[14,31991,31992,31993,31996,31997,32000,32001,32003,32004,32006,32007,32010],{},"Each ",[18,31994,31995],{},"\u003Cli>"," carries its index inline as ",[18,31998,31999],{},"style=\"--i:N\"",". A single keyframe rule fades-and-slides each item up; the per-item delay comes entirely from ",[18,32002,24085],{},". The resting state (",[18,32005,907],{},") plus ",[18,32008,32009],{},"animation-fill-mode: both"," keeps items invisible during their delay so nothing flashes before its turn.",[133,32012,140,32015,140,32018,140,32021,140,32023,140,32025,140,32027,140,32031,140,32033,140,32043,140,32053,140,32062,140,32071],{"viewBox":32013,"role":136,"ariaLabel":32014,"xmlns":138,"style":139},"0 0 720 340","A timeline showing five list items, each starting its fade-in animation one step later than the previous one",[142,32016,32017],{},"Staggered animation-delay timeline",[146,32019,32020],{},"Five horizontal bars stacked vertically; each bar's animation block begins further to the right, offset by an --i multiplied delay along a shared time axis.",[150,32022,31970],{"x":152,"y":2598,"style":1781},[163,32024],{"x1":1787,"y1":1786,"x2":1787,"y2":5397,"stroke":167,"strokeWidth":468,"opacity":169},[150,32026,14994],{"x":1787,"y":2613,"style":1808},[150,32028,32030],{"x":32029,"y":2613,"style":14997},"690","time",[163,32032],{"x1":1787,"y1":5397,"x2":26119,"y2":5397,"stroke":167,"strokeWidth":468,"opacity":169},[32034,32035,242,32036,242,32040,140],"g",{},[150,32037,32039],{"x":4146,"y":4196,"style":32038},"text-anchor:start;fill:currentColor;font:12px ui-monospace,monospace;opacity:0.85","--i:0",[171,32041],{"x":1787,"y":32042,"width":4147,"height":17581,"rx":5911,"fill":177,"opacity":199},"76",[32034,32044,242,32045,242,32048,242,32051,140],{},[150,32046,32047],{"x":4146,"y":6708,"style":32038},"--i:1",[171,32049],{"x":1787,"y":4147,"width":32050,"height":17581,"rx":5911,"fill":167,"opacity":3241},"36",[171,32052],{"x":4184,"y":4147,"width":4147,"height":17581,"rx":5911,"fill":177,"opacity":199},[32034,32054,242,32055,242,32058,242,32060,140],{},[150,32056,32057],{"x":4146,"y":2602,"style":32038},"--i:2",[171,32059],{"x":1787,"y":4166,"width":5892,"height":17581,"rx":5911,"fill":167,"opacity":3241},[171,32061],{"x":209,"y":4166,"width":4147,"height":17581,"rx":5911,"fill":177,"opacity":199},[32034,32063,242,32064,242,32067,242,32069,140],{},[150,32065,32066],{"x":4146,"y":191,"style":32038},"--i:3",[171,32068],{"x":1787,"y":29490,"width":173,"height":17581,"rx":5911,"fill":167,"opacity":3241},[171,32070],{"x":26466,"y":29490,"width":4147,"height":17581,"rx":5911,"fill":177,"opacity":199},[32034,32072,242,32073,242,32076,242,32078,140],{},[150,32074,32075],{"x":4146,"y":6736,"style":32038},"--i:4",[171,32077],{"x":1787,"y":6641,"width":4162,"height":17581,"rx":5911,"fill":167,"opacity":3241},[171,32079],{"x":32080,"y":6641,"width":4147,"height":17581,"rx":5911,"fill":177,"opacity":199},"294",[281,32082,32084],{"className":283,"code":32083,"language":285,"meta":286,"style":286},"\u003Cul class=\"stagger\">\n  \u003Cli style=\"--i:0\">Dashboard\u003C\u002Fli>\n  \u003Cli style=\"--i:1\">Projects\u003C\u002Fli>\n  \u003Cli style=\"--i:2\">Team\u003C\u002Fli>\n  \u003Cli style=\"--i:3\">Reports\u003C\u002Fli>\n  \u003Cli style=\"--i:4\">Settings\u003C\u002Fli>\n\u003C\u002Ful>\n",[18,32085,32086,32101,32121,32141,32161,32181,32201],{"__ignoreMap":286},[290,32087,32088,32090,32092,32094,32096,32099],{"class":163,"line":292},[290,32089,296],{"class":295},[290,32091,1393],{"class":299},[290,32093,314],{"class":303},[290,32095,307],{"class":295},[290,32097,32098],{"class":310},"\"stagger\"",[290,32100,327],{"class":295},[290,32102,32103,32105,32107,32109,32111,32114,32117,32119],{"class":163,"line":330},[290,32104,367],{"class":295},[290,32106,1396],{"class":299},[290,32108,30472],{"class":303},[290,32110,307],{"class":295},[290,32112,32113],{"class":310},"\"--i:0\"",[290,32115,32116],{"class":295},">Dashboard\u003C\u002F",[290,32118,1396],{"class":299},[290,32120,327],{"class":295},[290,32122,32123,32125,32127,32129,32131,32134,32137,32139],{"class":163,"line":337},[290,32124,367],{"class":295},[290,32126,1396],{"class":299},[290,32128,30472],{"class":303},[290,32130,307],{"class":295},[290,32132,32133],{"class":310},"\"--i:1\"",[290,32135,32136],{"class":295},">Projects\u003C\u002F",[290,32138,1396],{"class":299},[290,32140,327],{"class":295},[290,32142,32143,32145,32147,32149,32151,32154,32157,32159],{"class":163,"line":364},[290,32144,367],{"class":295},[290,32146,1396],{"class":299},[290,32148,30472],{"class":303},[290,32150,307],{"class":295},[290,32152,32153],{"class":310},"\"--i:2\"",[290,32155,32156],{"class":295},">Team\u003C\u002F",[290,32158,1396],{"class":299},[290,32160,327],{"class":295},[290,32162,32163,32165,32167,32169,32171,32174,32177,32179],{"class":163,"line":386},[290,32164,367],{"class":295},[290,32166,1396],{"class":299},[290,32168,30472],{"class":303},[290,32170,307],{"class":295},[290,32172,32173],{"class":310},"\"--i:3\"",[290,32175,32176],{"class":295},">Reports\u003C\u002F",[290,32178,1396],{"class":299},[290,32180,327],{"class":295},[290,32182,32183,32185,32187,32189,32191,32194,32197,32199],{"class":163,"line":408},[290,32184,367],{"class":295},[290,32186,1396],{"class":299},[290,32188,30472],{"class":303},[290,32190,307],{"class":295},[290,32192,32193],{"class":310},"\"--i:4\"",[290,32195,32196],{"class":295},">Settings\u003C\u002F",[290,32198,1396],{"class":299},[290,32200,327],{"class":295},[290,32202,32203,32205,32207],{"class":163,"line":428},[290,32204,431],{"class":295},[290,32206,1393],{"class":299},[290,32208,327],{"class":295},[281,32210,32212],{"className":438,"code":32211,"language":440,"meta":286,"style":286},".stagger {\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  display: grid;\n  gap: 8px;\n}\n\n.stagger li {\n  \u002F* Resting (hidden) state. fill-mode: both applies the `from`\n     keyframe during the delay so the item is invisible until its turn. *\u002F\n  opacity: 0;\n  \u002F* One shared keyframe; the delay is the only per-item difference. *\u002F\n  animation: item-enter 0.4s cubic-bezier(0.2, 0, 0, 1) both;\n  \u002F* `--i` is multiplied by a step. Multiply by ms or calc fails. *\u002F\n  animation-delay: calc(var(--i) * 60ms);\n}\n\n@keyframes item-enter {\n  from {\n    opacity: 0;\n    transform: translateY(12px);\n  }\n  to {\n    opacity: 1;\n    transform: translateY(0);\n  }\n}\n\n\u002F* Reduced motion: no cascade, no slide. Everything is present at once. *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .stagger li {\n    animation: none;\n    opacity: 1;\n  }\n}\n",[18,32213,32214,32220,32231,32242,32252,32262,32274,32278,32282,32291,32296,32301,32311,32316,32351,32356,32382,32386,32390,32399,32405,32415,32431,32435,32441,32451,32465,32469,32473,32477,32482,32488,32496,32506,32516,32520],{"__ignoreMap":286},[290,32215,32216,32218],{"class":163,"line":292},[290,32217,24014],{"class":303},[290,32219,450],{"class":295},[290,32221,32222,32225,32227,32229],{"class":163,"line":330},[290,32223,32224],{"class":461},"  list-style",[290,32226,465],{"class":295},[290,32228,72],{"class":461},[290,32230,471],{"class":295},[290,32232,32233,32236,32238,32240],{"class":163,"line":337},[290,32234,32235],{"class":461},"  margin",[290,32237,465],{"class":295},[290,32239,487],{"class":461},[290,32241,471],{"class":295},[290,32243,32244,32246,32248,32250],{"class":163,"line":364},[290,32245,10433],{"class":461},[290,32247,465],{"class":295},[290,32249,487],{"class":461},[290,32251,471],{"class":295},[290,32253,32254,32256,32258,32260],{"class":163,"line":386},[290,32255,17742],{"class":461},[290,32257,465],{"class":295},[290,32259,9147],{"class":461},[290,32261,471],{"class":295},[290,32263,32264,32266,32268,32270,32272],{"class":163,"line":408},[290,32265,26819],{"class":461},[290,32267,465],{"class":295},[290,32269,176],{"class":461},[290,32271,674],{"class":541},[290,32273,471],{"class":295},[290,32275,32276],{"class":163,"line":428},[290,32277,620],{"class":295},[290,32279,32280],{"class":163,"line":517},[290,32281,334],{"emptyLinePlaceholder":333},[290,32283,32284,32286,32289],{"class":163,"line":523},[290,32285,24014],{"class":303},[290,32287,32288],{"class":299}," li",[290,32290,450],{"class":295},[290,32292,32293],{"class":163,"line":532},[290,32294,32295],{"class":455},"  \u002F* Resting (hidden) state. fill-mode: both applies the `from`\n",[290,32297,32298],{"class":163,"line":551},[290,32299,32300],{"class":455},"     keyframe during the delay so the item is invisible until its turn. *\u002F\n",[290,32302,32303,32305,32307,32309],{"class":163,"line":586},[290,32304,462],{"class":461},[290,32306,465],{"class":295},[290,32308,487],{"class":461},[290,32310,471],{"class":295},[290,32312,32313],{"class":163,"line":602},[290,32314,32315],{"class":455},"  \u002F* One shared keyframe; the delay is the only per-item difference. *\u002F\n",[290,32317,32318,32320,32323,32325,32327,32329,32331,32333,32335,32337,32339,32341,32343,32345,32347,32349],{"class":163,"line":617},[290,32319,6035],{"class":461},[290,32321,32322],{"class":295},": item-enter ",[290,32324,169],{"class":461},[290,32326,1886],{"class":541},[290,32328,561],{"class":461},[290,32330,484],{"class":295},[290,32332,566],{"class":461},[290,32334,569],{"class":295},[290,32336,487],{"class":461},[290,32338,569],{"class":295},[290,32340,487],{"class":461},[290,32342,569],{"class":295},[290,32344,468],{"class":461},[290,32346,490],{"class":295},[290,32348,7069],{"class":461},[290,32350,471],{"class":295},[290,32352,32353],{"class":163,"line":623},[290,32354,32355],{"class":455},"  \u002F* `--i` is multiplied by a step. Multiply by ms or calc fails. *\u002F\n",[290,32357,32358,32360,32362,32364,32366,32368,32370,32372,32374,32376,32378,32380],{"class":163,"line":628},[290,32359,23038],{"class":461},[290,32361,465],{"class":295},[290,32363,3556],{"class":461},[290,32365,484],{"class":295},[290,32367,1622],{"class":461},[290,32369,484],{"class":295},[290,32371,24085],{"class":1561},[290,32373,490],{"class":295},[290,32375,4415],{"class":541},[290,32377,11021],{"class":461},[290,32379,542],{"class":541},[290,32381,500],{"class":295},[290,32383,32384],{"class":163,"line":634},[290,32385,620],{"class":295},[290,32387,32388],{"class":163,"line":649},[290,32389,334],{"emptyLinePlaceholder":333},[290,32391,32392,32394,32397],{"class":163,"line":660},[290,32393,3968],{"class":541},[290,32395,32396],{"class":1561}," item-enter",[290,32398,450],{"class":295},[290,32400,32401,32403],{"class":163,"line":688},[290,32402,6191],{"class":303},[290,32404,450],{"class":295},[290,32406,32407,32409,32411,32413],{"class":163,"line":693},[290,32408,733],{"class":461},[290,32410,465],{"class":295},[290,32412,487],{"class":461},[290,32414,471],{"class":295},[290,32416,32417,32419,32421,32423,32425,32427,32429],{"class":163,"line":698},[290,32418,745],{"class":461},[290,32420,465],{"class":295},[290,32422,481],{"class":461},[290,32424,484],{"class":295},[290,32426,5894],{"class":461},[290,32428,674],{"class":541},[290,32430,500],{"class":295},[290,32432,32433],{"class":163,"line":704},[290,32434,771],{"class":295},[290,32436,32437,32439],{"class":163,"line":710},[290,32438,6072],{"class":303},[290,32440,450],{"class":295},[290,32442,32443,32445,32447,32449],{"class":163,"line":717},[290,32444,733],{"class":461},[290,32446,465],{"class":295},[290,32448,468],{"class":461},[290,32450,471],{"class":295},[290,32452,32453,32455,32457,32459,32461,32463],{"class":163,"line":730},[290,32454,745],{"class":461},[290,32456,465],{"class":295},[290,32458,481],{"class":461},[290,32460,484],{"class":295},[290,32462,487],{"class":461},[290,32464,500],{"class":295},[290,32466,32467],{"class":163,"line":742},[290,32468,771],{"class":295},[290,32470,32471],{"class":163,"line":768},[290,32472,620],{"class":295},[290,32474,32475],{"class":163,"line":774},[290,32476,334],{"emptyLinePlaceholder":333},[290,32478,32479],{"class":163,"line":779},[290,32480,32481],{"class":455},"\u002F* Reduced motion: no cascade, no slide. Everything is present at once. *\u002F\n",[290,32483,32484,32486],{"class":163,"line":784},[290,32485,874],{"class":541},[290,32487,877],{"class":295},[290,32489,32490,32492,32494],{"class":163,"line":812},[290,32491,24132],{"class":303},[290,32493,32288],{"class":299},[290,32495,450],{"class":295},[290,32497,32498,32500,32502,32504],{"class":163,"line":860},[290,32499,6152],{"class":461},[290,32501,465],{"class":295},[290,32503,72],{"class":461},[290,32505,471],{"class":295},[290,32507,32508,32510,32512,32514],{"class":163,"line":865},[290,32509,733],{"class":461},[290,32511,465],{"class":295},[290,32513,468],{"class":461},[290,32515,471],{"class":295},[290,32517,32518],{"class":163,"line":871},[290,32519,771],{"class":295},[290,32521,32522],{"class":163,"line":880},[290,32523,620],{"class":295},[14,32525,32526,32527,32529,32530,32532,32533,32536,32537,32539,32540,32542,32543,32545],{},"Note ",[18,32528,32009],{}," (the trailing ",[18,32531,7069],{}," in the shorthand). Without it the item would render at full opacity ",[86,32534,32535],{},"during"," its delay and only snap to hidden when the animation finally begins — producing a flash. ",[18,32538,7069],{}," applies the ",[18,32541,88],{}," state forward across the delay and holds the ",[18,32544,8151],{}," state after, so the item is cleanly hidden, then revealed.",[47,32547],{},[50,32549,32551],{"id":32550},"the-key-technique-multiplying-a-unitless-token-by-a-time-unit","The Key Technique: Multiplying a Unitless Token by a Time Unit",[14,32553,32554,32557,32558,32560,32561,32563,32564,465,32566,32569,32570,32573,32574,32576,32577,32580,32581,32583,32584,32587,32588,32590],{},[18,32555,32556],{},"var(--i)"," resolves to a plain number like ",[18,32559,194],{},". A number is not a duration, so you cannot drop it straight into ",[18,32562,6980],{},". The bridge is ",[18,32565,3693],{},[18,32567,32568],{},"calc(var(--i) * 60ms)"," multiplies the unitless index by a time value, and the multiplication of ",[18,32571,32572],{},"\u003Cnumber> * \u003Ctime>"," yields a valid ",[18,32575,6923],{},". This is the single rule everything else hangs on. The same construction lets you derive the stagger step from a design token — ",[18,32578,32579],{},"calc(var(--i) * var(--stagger-step, 60ms))"," — so the rhythm is themeable, exactly the token-driven approach in the ",[27,32582,31920],{"href":10147}," guide. The common failure mode is forgetting the unit (",[18,32585,32586],{},"calc(var(--i) * 60)","), which produces an invalid ",[18,32589,6923],{}," and the browser silently discards the delay, so every item fires at once.",[47,32592],{},[50,32594,32596],{"id":32595},"variation-auto-indexing-with-nth-child-no-inline-style","Variation: Auto-Indexing with nth-child (No Inline Style)",[14,32598,32599,32600,32602,32603,32605,32606,32608],{},"If you cannot edit the markup to add ",[18,32601,31999],{}," — for instance the list comes from a CMS — derive the index in CSS with ",[18,32604,31945],{},". It is more verbose but keeps the HTML clean, and you can still share the single ",[18,32607,3968],{}," rule.",[281,32610,32612],{"className":438,"code":32611,"language":440,"meta":286,"style":286},".stagger li { --i: 0; }\n.stagger li:nth-child(2) { --i: 1; }\n.stagger li:nth-child(3) { --i: 2; }\n.stagger li:nth-child(4) { --i: 3; }\n.stagger li:nth-child(5) { --i: 4; }\n.stagger li:nth-child(6) { --i: 5; }\n\n\u002F* Cap the cascade so a long list never hides its tail for too long. *\u002F\n.stagger li {\n  animation-delay: min(calc(var(--i) * 60ms), 360ms);\n}\n",[18,32613,32614,32630,32652,32674,32696,32718,32740,32744,32749,32757,32794],{"__ignoreMap":286},[290,32615,32616,32618,32620,32622,32624,32626,32628],{"class":163,"line":292},[290,32617,24014],{"class":303},[290,32619,32288],{"class":299},[290,32621,790],{"class":295},[290,32623,24085],{"class":1561},[290,32625,465],{"class":295},[290,32627,487],{"class":461},[290,32629,809],{"class":295},[290,32631,32632,32634,32636,32638,32640,32642,32644,32646,32648,32650],{"class":163,"line":330},[290,32633,24014],{"class":303},[290,32635,32288],{"class":299},[290,32637,27500],{"class":303},[290,32639,484],{"class":295},[290,32641,194],{"class":461},[290,32643,27507],{"class":295},[290,32645,24085],{"class":1561},[290,32647,465],{"class":295},[290,32649,468],{"class":461},[290,32651,809],{"class":295},[290,32653,32654,32656,32658,32660,32662,32664,32666,32668,32670,32672],{"class":163,"line":337},[290,32655,24014],{"class":303},[290,32657,32288],{"class":299},[290,32659,27500],{"class":303},[290,32661,484],{"class":295},[290,32663,1579],{"class":461},[290,32665,27507],{"class":295},[290,32667,24085],{"class":1561},[290,32669,465],{"class":295},[290,32671,194],{"class":461},[290,32673,809],{"class":295},[290,32675,32676,32678,32680,32682,32684,32686,32688,32690,32692,32694],{"class":163,"line":364},[290,32677,24014],{"class":303},[290,32679,32288],{"class":299},[290,32681,27500],{"class":303},[290,32683,484],{"class":295},[290,32685,249],{"class":461},[290,32687,27507],{"class":295},[290,32689,24085],{"class":1561},[290,32691,465],{"class":295},[290,32693,1579],{"class":461},[290,32695,809],{"class":295},[290,32697,32698,32700,32702,32704,32706,32708,32710,32712,32714,32716],{"class":163,"line":386},[290,32699,24014],{"class":303},[290,32701,32288],{"class":299},[290,32703,27500],{"class":303},[290,32705,484],{"class":295},[290,32707,5911],{"class":461},[290,32709,27507],{"class":295},[290,32711,24085],{"class":1561},[290,32713,465],{"class":295},[290,32715,249],{"class":461},[290,32717,809],{"class":295},[290,32719,32720,32722,32724,32726,32728,32730,32732,32734,32736,32738],{"class":163,"line":408},[290,32721,24014],{"class":303},[290,32723,32288],{"class":299},[290,32725,27500],{"class":303},[290,32727,484],{"class":295},[290,32729,5901],{"class":461},[290,32731,27507],{"class":295},[290,32733,24085],{"class":1561},[290,32735,465],{"class":295},[290,32737,5911],{"class":461},[290,32739,809],{"class":295},[290,32741,32742],{"class":163,"line":428},[290,32743,334],{"emptyLinePlaceholder":333},[290,32745,32746],{"class":163,"line":517},[290,32747,32748],{"class":455},"\u002F* Cap the cascade so a long list never hides its tail for too long. *\u002F\n",[290,32750,32751,32753,32755],{"class":163,"line":523},[290,32752,24014],{"class":303},[290,32754,32288],{"class":299},[290,32756,450],{"class":295},[290,32758,32759,32761,32763,32766,32768,32770,32772,32774,32776,32778,32780,32782,32784,32786,32788,32790,32792],{"class":163,"line":532},[290,32760,23038],{"class":461},[290,32762,465],{"class":295},[290,32764,32765],{"class":461},"min",[290,32767,484],{"class":295},[290,32769,3556],{"class":461},[290,32771,484],{"class":295},[290,32773,1622],{"class":461},[290,32775,484],{"class":295},[290,32777,24085],{"class":1561},[290,32779,490],{"class":295},[290,32781,4415],{"class":541},[290,32783,11021],{"class":461},[290,32785,542],{"class":541},[290,32787,24264],{"class":295},[290,32789,152],{"class":461},[290,32791,542],{"class":541},[290,32793,500],{"class":295},[290,32795,32796],{"class":163,"line":551},[290,32797,620],{"class":295},[14,32799,32800,32801,32804],{},"Wrapping the delay in ",[18,32802,32803],{},"min(…, 360ms)"," clamps the total so even a 30-item list finishes its entrance in well under half a second, which keeps the flourish from becoming a latency problem.",[47,32806],{},[50,32808,2248],{"id":2247},[14,32810,32811,32812,32814,32815,8393,32817,32819,32820,32823,32824,32826,32827,32829,32830,32832],{},"The whole pattern is broadly supported: custom properties, ",[18,32813,3693],{}," mixing numbers and time units, ",[18,32816,3968],{},[18,32818,6779],{}," all work in Chrome\u002FEdge 49+, Safari 10+, and Firefox 31+. The ",[18,32821,32822],{},"min()"," clamp in the variation needs Chrome\u002FEdge 79+, Safari 11.1+, and Firefox 75+ — if you must support older engines, drop the ",[18,32825,32822],{}," and keep the plain ",[18,32828,3693],{}," delay, accepting that very long lists stagger further. No ",[18,32831,2086],{}," guard is necessary for the core technique.",[47,32834],{},[50,32836,1316],{"id":1315},[14,32838,32839,32842,32843,569,32846,32848,32849,32851],{},[62,32840,32841],{},"How do I set the --i index without JavaScript?","\nWrite the index inline with ",[18,32844,32845],{},"style=\"--i:0\"",[18,32847,32047],{}," and so on in the HTML, or generate it with ",[18,32850,31945],{}," rules in CSS. Both are static and need no script at runtime.",[14,32853,32854,32857,32858,32860,32861,32863,32864,32866],{},[62,32855,32856],{},"Why does calc(var(--i) * 60ms) not work in animation-delay?","\nA bare custom property is an unitless token until you multiply it by a time unit. ",[18,32859,32568],{}," is correct; ",[18,32862,32586],{}," without the ",[18,32865,542],{}," unit produces an invalid value and the delay is ignored.",[14,32868,32869,32872,32873,32875,32876,26413,32878,32880],{},[62,32870,32871],{},"How do I keep items hidden before their delay fires?","\nSet the resting hidden state on the element itself (",[18,32874,907],{},") and use ",[18,32877,32009],{},[18,32879,88],{}," keyframe is applied during the delay, before the animation starts.",[14,32882,32883,32886,32887,42],{},[62,32884,32885],{},"Does a long stagger hurt accessibility?","\nIt can. Long cascades delay content for everyone and can trigger vestibular discomfort, so cap the total at a few hundred milliseconds and collapse the stagger to zero under ",[18,32888,2584],{},[47,32890],{},[50,32892,1391],{"id":1390},[1393,32894,32895,32900,32906,32911,32916],{},[1396,32896,32897,32899],{},[27,32898,1420],{"href":1419}," — the parent guide to repeating and entrance animations.",[1396,32901,32902,32905],{},[27,32903,32904],{"href":27915},"CSS-Only Loading Spinners and Skeletons"," — sibling pattern; skeleton rows often use the same stagger.",[1396,32907,32908,32910],{},[27,32909,3355],{"href":10147}," — how token-driven values like the stagger step are structured.",[1396,32912,32913,32915],{},[27,32914,2511],{"href":1412}," — collapsing the cascade for reduced-motion users.",[1396,32917,32918,32921],{},[27,32919,32920],{"href":11317},"Fluid Space Scale with Clamp"," — the same multiply-a-token idea applied to responsive spacing.",[1430,32923,32924],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":286,"searchDepth":330,"depth":330,"links":32926},[32927,32928,32929,32930,32931,32932,32933],{"id":31953,"depth":330,"text":31954},{"id":4202,"depth":330,"text":25403},{"id":32550,"depth":330,"text":32551},{"id":32595,"depth":330,"text":32596},{"id":2247,"depth":330,"text":2248},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Stagger CSS entrance animations with a --i index custom property to offset animation-delay per item. No JavaScript, with a prefers-reduced-motion fallback.",{"seoTitle":32936,"datePublished":1447,"dateModified":1447,"faq":32937},"Staggered List Animations with --i",[32938,32940,32942,32944],{"q":32841,"a":32939},"Write the index inline with style=\"--i:0\", --i:1 and so on in the HTML, or generate it with nth-child rules in CSS. Both are static and need no script at runtime.",{"q":32856,"a":32941},"A bare custom property is an unitless token until you multiply it by a time unit. calc(var(--i) * 60ms) is correct; calc(var(--i) * 60) without the ms unit produces an invalid value and the delay is ignored.",{"q":32871,"a":32943},"Set the resting hidden state on the element itself (opacity:0) and use animation-fill-mode: both so the from keyframe is applied during the delay, before the animation starts.",{"q":32885,"a":32945},"It can. Long cascades delay content for everyone and can trigger vestibular discomfort, so cap the total at a few hundred milliseconds and collapse the stagger to zero under prefers-reduced-motion.","\u002Fcss-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fstaggered-list-animations-with-custom-properties",{"title":31900,"description":32934},"css-only-micro-interactions-animations\u002Fkeyframe-animation-patterns\u002Fstaggered-list-animations-with-custom-properties\u002Findex","k0Osh17rlenCN8tKFCkc_wQh0RCXqXsbU7iBW_KRPb4",{"id":32951,"title":32952,"body":32953,"description":34058,"extension":1444,"meta":34059,"navigation":333,"path":34070,"seo":34071,"stem":34072,"__hash__":34073},"content\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Findex.md","Performance & GPU Acceleration in CSS: A Developer’s Blueprint",{"type":7,"value":32954,"toc":34038},[32955,32958,32967,33020,33024,33058,33060,33064,33077,33091,33095,33133,33136,33138,33146,33162,33166,33289,33306,33308,33312,33326,33332,33339,33480,33495,33497,33501,33508,33554,33558,33561,33741,33743,33745,33820,33837,33839,33841,33958,33960,33962,33979,33990,34004,34006,34008,34035],[10,32956,32952],{"id":32957},"performance-gpu-acceleration-in-css-a-developers-blueprint",[14,32959,32960,32961,32963,32964,32966],{},"Achieving fluid, jank-free interfaces requires moving beyond basic styling into the realm of ",[27,32962,1481],{"href":1480}," and hardware-accelerated rendering. This guide dissects how modern browsers composite layers, leverage the GPU for ",[27,32965,3345],{"href":3344},", and eliminate main-thread bottlenecks. By mastering compositor-friendly properties and strategic layer promotion, frontend engineers can deliver 60fps experiences without relying on heavy JavaScript.",[133,32968,140,32971,140,32974,140,32977,140,32980,140,32982,140,32985,140,32988,140,32991,140,32994,140,32997,140,33000,140,33003,140,33005,140,33008,140,33011,140,33014,140,33017],{"viewBox":32969,"role":136,"ariaLabel":32970,"xmlns":138,"style":139},"0 0 720 280","Browser rendering pipeline showing which phases compositor properties skip",[142,32972,32973],{},"Rendering pipeline and the compositor fast path",[146,32975,32976],{},"Diagram of the Style, Layout, Paint, Composite pipeline showing that transform and opacity skip straight to Composite.",[150,32978,32979],{"x":152,"y":2598,"style":154},"Style → Layout → Paint → Composite",[171,32981],{"x":158,"y":5144,"width":1787,"height":1788,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},[150,32983,32984],{"x":19627,"y":1798,"style":19628},"Style",[171,32986],{"x":32987,"y":5144,"width":1787,"height":1788,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},"198",[150,32989,13462],{"x":32990,"y":1798,"style":19628},"273",[171,32992],{"x":32993,"y":5144,"width":1787,"height":1788,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},"372",[150,32995,13465],{"x":32996,"y":1798,"style":19628},"447",[171,32998],{"x":32999,"y":5144,"width":1787,"height":1788,"rx":176,"fill":177,"opacity":199},"546",[150,33001,16208],{"x":33002,"y":1798,"style":19628},"621",[163,33004],{"x1":12454,"y1":18870,"x2":32987,"y2":18870,"stroke":167,"strokeWidth":168},[163,33006],{"x1":33007,"y1":18870,"x2":32993,"y2":18870,"stroke":167,"strokeWidth":168},"348",[163,33009],{"x1":33010,"y1":18870,"x2":32999,"y2":18870,"stroke":167,"strokeWidth":168},"522",[253,33012],{"d":33013,"fill":72,"stroke":177,"strokeWidth":194},"M99 180 C 99 230, 621 230, 621 130",[150,33015,21768],{"x":152,"y":33016,"style":19647},"225",[150,33018,33019],{"x":152,"y":5914,"style":1808},"Compositor-only properties skip Layout and Paint",[14,33021,33022],{},[62,33023,3678],{},[1393,33025,33028,33036,33042,33052],{"className":33026},[33027],"contains-task-list",[1396,33029,33032,33035],{"className":33030},[33031],"task-list-item",[18985,33033],{"disabled":333,"type":33034},"checkbox"," Understand the browser rendering pipeline and compositor thread separation",[1396,33037,33039,33041],{"className":33038},[33031],[18985,33040],{"disabled":333,"type":33034}," Identify which CSS properties trigger layout, paint, or composite",[1396,33043,33045,33047,33048,69,33050],{"className":33044},[33031],[18985,33046],{"disabled":333,"type":33034}," Implement hardware acceleration using ",[18,33049,103],{},[18,33051,76],{},[1396,33053,33055,33057],{"className":33054},[33031],[18985,33056],{"disabled":333,"type":33034}," Audit and optimize animation performance with DevTools",[47,33059],{},[50,33061,33063],{"id":33062},"the-browser-rendering-pipeline-compositor-thread","The Browser Rendering Pipeline & Compositor Thread",[14,33065,33066,33067,33069,33070,569,33072,569,33074,33076],{},"Browsers process frames through a strict sequence: ",[62,33068,32979],{},". The main thread handles JavaScript execution, DOM mutations, and layout recalculation. When an animation modifies properties that affect geometry (e.g., ",[18,33071,1748],{},[18,33073,2725],{},[18,33075,2728],{},"), the browser must recalculate the entire document tree, triggering expensive layout thrashing prevention protocols and blocking user input.",[14,33078,33079,33080,33083,33084,33087,33088,33090],{},"Modern rendering engines mitigate this by delegating the final ",[62,33081,33082],{},"composite"," phase to a dedicated ",[62,33085,33086],{},"compositor thread",". This thread runs independently on the GPU, reading pre-rasterized textures and applying matrix transformations without touching the main thread. This architectural separation is the foundation of ",[27,33089,30],{"href":29}," that maintain responsiveness under heavy script load.",[2757,33092,33094],{"id":33093},"devtools-profiling-workflow","DevTools Profiling Workflow",[3017,33096,33097,33103,33110,33118,33124],{},[1396,33098,33099,33100,33102],{},"Open Chrome\u002FEdge DevTools → ",[62,33101,16173],{}," tab",[1396,33104,33105,33106,33109],{},"Click ",[62,33107,33108],{},"Record",", trigger the micro-interaction, then stop",[1396,33111,33112,33113,2351,33115,33117],{},"Inspect the flame chart: look for ",[18,33114,13462],{},[18,33116,13465],{}," bars overlapping your interaction timeline",[1396,33119,33120,33121],{},"Navigate to ",[62,33122,33123],{},"More Tools → Rendering",[1396,33125,33126,33127,33129,33130,33132],{},"Enable ",[18,33128,16164],{}," (blue outlines indicate GPU-composited layers) and ",[18,33131,16161],{}," (orange flashes indicate main-thread repaints)",[14,33134,33135],{},"If your animation triggers orange paint flashes, it is not running on the compositor thread and will cause frame drops under load.",[47,33137],{},[50,33139,33141,33142,1692,33144],{"id":33140},"hardware-acceleration-via-transform-opacity","Hardware Acceleration via ",[18,33143,103],{},[18,33145,76],{},[14,33147,33148,33149,69,33151,33153,33154,2351,33156,33158,33159,42],{},"Only ",[18,33150,103],{},[18,33152,76],{}," bypass layout and paint entirely. They operate directly on pre-composited GPU textures via matrix math. Using ",[18,33155,30596],{},[18,33157,25012],{}," forces the browser to promote the element to its own rendering layer, enabling true ",[62,33160,33161],{},"CSS hardware acceleration",[2757,33163,33165],{"id":33164},"copy-paste-pattern-gpu-promoted-card-hover","Copy-Paste Pattern: GPU-Promoted Card Hover",[281,33167,33169],{"className":438,"code":33168,"language":440,"meta":286,"style":286},"\u002F* Base state: promote to compositor layer *\u002F\n.card {\n  \u002F* Legacy fallback for older WebKit\u002FBlink engines *\u002F\n  transform: translateZ(0);\n  \u002F* Modern standard: hints compositor without forcing immediate promotion *\u002F\n  will-change: transform;\n  transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);\n}\n\n\u002F* Interaction: zero-layout-cost transform *\u002F\n.card:hover {\n  transform: translateZ(0) scale(1.02);\n}\n",[18,33170,33171,33176,33182,33187,33201,33206,33212,33244,33248,33252,33257,33263,33285],{"__ignoreMap":286},[290,33172,33173],{"class":163,"line":292},[290,33174,33175],{"class":455},"\u002F* Base state: promote to compositor layer *\u002F\n",[290,33177,33178,33180],{"class":163,"line":330},[290,33179,11528],{"class":303},[290,33181,450],{"class":295},[290,33183,33184],{"class":163,"line":337},[290,33185,33186],{"class":455},"  \u002F* Legacy fallback for older WebKit\u002FBlink engines *\u002F\n",[290,33188,33189,33191,33193,33195,33197,33199],{"class":163,"line":364},[290,33190,476],{"class":461},[290,33192,465],{"class":295},[290,33194,15490],{"class":461},[290,33196,484],{"class":295},[290,33198,487],{"class":461},[290,33200,500],{"class":295},[290,33202,33203],{"class":163,"line":386},[290,33204,33205],{"class":455},"  \u002F* Modern standard: hints compositor without forcing immediate promotion *\u002F\n",[290,33207,33208,33210],{"class":163,"line":408},[290,33209,3518],{"class":461},[290,33211,15503],{"class":295},[290,33213,33214,33216,33218,33220,33222,33224,33226,33228,33230,33233,33235,33237,33239,33242],{"class":163,"line":428},[290,33215,526],{"class":461},[290,33217,1880],{"class":295},[290,33219,199],{"class":461},[290,33221,1886],{"class":541},[290,33223,561],{"class":461},[290,33225,484],{"class":295},[290,33227,1668],{"class":461},[290,33229,569],{"class":295},[290,33231,33232],{"class":461},"0.46",[290,33234,569],{"class":295},[290,33236,6041],{"class":461},[290,33238,569],{"class":295},[290,33240,33241],{"class":461},"0.94",[290,33243,500],{"class":295},[290,33245,33246],{"class":163,"line":517},[290,33247,620],{"class":295},[290,33249,33250],{"class":163,"line":523},[290,33251,334],{"emptyLinePlaceholder":333},[290,33253,33254],{"class":163,"line":532},[290,33255,33256],{"class":455},"\u002F* Interaction: zero-layout-cost transform *\u002F\n",[290,33258,33259,33261],{"class":163,"line":551},[290,33260,11814],{"class":303},[290,33262,450],{"class":295},[290,33264,33265,33267,33269,33271,33273,33275,33277,33279,33281,33283],{"class":163,"line":586},[290,33266,476],{"class":461},[290,33268,465],{"class":295},[290,33270,15490],{"class":461},[290,33272,484],{"class":295},[290,33274,487],{"class":461},[290,33276,490],{"class":295},[290,33278,493],{"class":461},[290,33280,484],{"class":295},[290,33282,2057],{"class":461},[290,33284,500],{"class":295},[290,33286,33287],{"class":163,"line":602},[290,33288,620],{"class":295},[14,33290,33291,2694,33294,33296,33297,33299,33300,33302,33303,33305],{},[62,33292,33293],{},"Why this works:",[18,33295,25012],{}," modifies the element's transformation matrix without altering its intrinsic box model. The browser simply instructs the GPU to scale the existing texture. ",[18,33298,22267],{}," remains a valid fallback for legacy Safari versions that require explicit 3D context to trigger layer promotion, but modern Blink\u002FGecko engines rely on ",[18,33301,3797],{}," and specific ",[18,33304,103],{}," values for intelligent compositing.",[47,33307],{},[50,33309,33311],{"id":33310},"strategic-layer-promotion-memory-management","Strategic Layer Promotion & Memory Management",[14,33313,33314,33315,33317,33318,33322,33323,42],{},"Every promoted layer consumes VRAM. Indiscriminate use of the ",[18,33316,3797],{}," property forces the browser to allocate GPU textures for static elements, leading to texture thrashing, increased memory pressure, and potential crashes on low-end mobile GPUs. The mechanics of how that hint hands work to the compositor are covered in depth in ",[27,33319,33321],{"href":33320},"\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fwill-change-and-the-compositor-thread\u002F","will-change and the compositor thread","; the solution here is ",[62,33324,33325],{},"conditional, state-driven promotion",[14,33327,33328,33329,42],{},"Apply layers only during active interaction windows (typically 100–200ms before the animation starts) and remove them immediately after completion. For implementation patterns that balance visual fidelity with strict memory budgets, see ",[27,33330,33331],{"href":8756},"Optimizing CSS animations for 60fps",[2757,33333,33335,33336,33338],{"id":33334},"dynamic-will-change-management","Dynamic ",[18,33337,3797],{}," Management",[281,33340,33342],{"className":438,"code":33341,"language":440,"meta":286,"style":286},"\u002F* Only promote when interaction is imminent *\u002F\n.interactive-element {\n  \u002F* Avoid static will-change on non-animating elements *\u002F\n  transition:\n    transform 0.25s ease,\n    opacity 0.25s ease;\n}\n\n.interactive-element:hover,\n.interactive-element:focus-visible {\n  will-change: transform, opacity;\n  transform: translateY(-4px);\n  opacity: 0.95;\n}\n\n\u002F* Optional: JS-assisted cleanup for complex sequences *\u002F\n.interactive-element:active {\n  will-change: auto; \u002F* Release GPU memory immediately after interaction *\u002F\n}\n",[18,33343,33344,33349,33355,33360,33366,33378,33390,33394,33398,33404,33411,33417,33433,33443,33447,33451,33456,33463,33476],{"__ignoreMap":286},[290,33345,33346],{"class":163,"line":292},[290,33347,33348],{"class":455},"\u002F* Only promote when interaction is imminent *\u002F\n",[290,33350,33351,33353],{"class":163,"line":330},[290,33352,20783],{"class":303},[290,33354,450],{"class":295},[290,33356,33357],{"class":163,"line":337},[290,33358,33359],{"class":455},"  \u002F* Avoid static will-change on non-animating elements *\u002F\n",[290,33361,33362,33364],{"class":163,"line":364},[290,33363,526],{"class":461},[290,33365,529],{"class":295},[290,33367,33368,33370,33372,33374,33376],{"class":163,"line":386},[290,33369,554],{"class":295},[290,33371,1668],{"class":461},[290,33373,1886],{"class":541},[290,33375,545],{"class":461},[290,33377,548],{"class":295},[290,33379,33380,33382,33384,33386,33388],{"class":163,"line":408},[290,33381,535],{"class":295},[290,33383,1668],{"class":461},[290,33385,1886],{"class":541},[290,33387,545],{"class":461},[290,33389,471],{"class":295},[290,33391,33392],{"class":163,"line":428},[290,33393,620],{"class":295},[290,33395,33396],{"class":163,"line":517},[290,33397,334],{"emptyLinePlaceholder":333},[290,33399,33400,33402],{"class":163,"line":523},[290,33401,20865],{"class":303},[290,33403,548],{"class":295},[290,33405,33406,33409],{"class":163,"line":532},[290,33407,33408],{"class":303},".interactive-element:focus-visible",[290,33410,450],{"class":295},[290,33412,33413,33415],{"class":163,"line":551},[290,33414,3518],{"class":461},[290,33416,3521],{"class":295},[290,33418,33419,33421,33423,33425,33427,33429,33431],{"class":163,"line":586},[290,33420,476],{"class":461},[290,33422,465],{"class":295},[290,33424,481],{"class":461},[290,33426,484],{"class":295},[290,33428,3189],{"class":461},[290,33430,674],{"class":541},[290,33432,500],{"class":295},[290,33434,33435,33437,33439,33441],{"class":163,"line":602},[290,33436,462],{"class":461},[290,33438,465],{"class":295},[290,33440,1119],{"class":461},[290,33442,471],{"class":295},[290,33444,33445],{"class":163,"line":617},[290,33446,620],{"class":295},[290,33448,33449],{"class":163,"line":623},[290,33450,334],{"emptyLinePlaceholder":333},[290,33452,33453],{"class":163,"line":628},[290,33454,33455],{"class":455},"\u002F* Optional: JS-assisted cleanup for complex sequences *\u002F\n",[290,33457,33458,33461],{"class":163,"line":634},[290,33459,33460],{"class":303},".interactive-element:active",[290,33462,450],{"class":295},[290,33464,33465,33467,33469,33471,33473],{"class":163,"line":649},[290,33466,3518],{"class":461},[290,33468,465],{"class":295},[290,33470,250],{"class":461},[290,33472,828],{"class":295},[290,33474,33475],{"class":455},"\u002F* Release GPU memory immediately after interaction *\u002F\n",[290,33477,33478],{"class":163,"line":660},[290,33479,620],{"class":295},[14,33481,33482,33484,33485,33487,33488,33490,33491,33494],{},[62,33483,12661],{}," Wrap ",[18,33486,3797],{}," in a ",[18,33489,2086],{}," query or apply via JS only when ",[18,33492,33493],{},"window.matchMedia('(prefers-reduced-motion: no-preference)').matches"," is true. This respects user accessibility preferences while conserving GPU cycles.",[47,33496],{},[50,33498,33500],{"id":33499},"debugging-profiling-animation-performance","Debugging & Profiling Animation Performance",[14,33502,33503,33504,33507],{},"Identifying jank requires systematic profiling beyond visual inspection. When profiling reveals that declarative motion alone cannot express the logic you need — runtime-computed values, playback control, or sequencing tied to events — weigh the tradeoffs in ",[27,33505,33506],{"href":8822},"CSS animation vs the Web Animations API"," before reaching for a scripted timeline. Use the following workflow to isolate bottlenecks in production environments:",[3017,33509,33510,33519,33525,33540],{},[1396,33511,33512,16194,33515,33518],{},[62,33513,33514],{},"Visualize Layers:",[18,33516,33517],{},"Show layer borders"," in the Rendering tab. Blue borders confirm successful GPU promotion.",[1396,33520,33521,33524],{},[62,33522,33523],{},"Interpret Frame Graphs:"," In the Performance panel, maintain a consistent green frame rate line. Red\u002Fyellow spikes indicate dropped frames.",[1396,33526,33527,33530,33531,33533,33534,569,33537,33539],{},[62,33528,33529],{},"Detect Forced Synchronous Layouts:"," Look for ",[18,33532,13462],{}," events triggered immediately after DOM reads (",[18,33535,33536],{},"offsetHeight",[18,33538,7533],{},"). Batch reads before writes to prevent layout thrashing.",[1396,33541,33542,33545,33546,33549,33550,33553],{},[62,33543,33544],{},"CI\u002FCD Automation:"," Integrate Lighthouse CI or WebPageTest to enforce performance budgets. Fail builds if ",[18,33547,33548],{},"Total Blocking Time"," exceeds 200ms or ",[18,33551,33552],{},"Cumulative Layout Shift"," > 0.1 during animation states.",[2757,33555,33557],{"id":33556},"frame-timing-monitor-raf","Frame Timing Monitor (rAF)",[14,33559,33560],{},"Use this lightweight snippet to log frame drops during development:",[281,33562,33566],{"className":33563,"code":33564,"language":33565,"meta":286,"style":286},"language-javascript shiki shiki-themes github-light github-dark","let lastTime = performance.now();\nlet frameCount = 0;\n\nfunction monitorFrame(timestamp) {\n  const delta = timestamp - lastTime;\n  if (delta > 16.67) {\n    \u002F\u002F >60fps threshold\n    console.warn(`Frame drop detected: ${delta.toFixed(2)}ms`);\n  }\n  lastTime = timestamp;\n  frameCount++;\n  if (frameCount \u003C 300) requestAnimationFrame(monitorFrame);\n}\n\nrequestAnimationFrame(monitorFrame);\n","javascript",[18,33567,33568,33585,33598,33602,33616,33633,33647,33652,33684,33688,33698,33708,33727,33731,33735],{"__ignoreMap":286},[290,33569,33570,33572,33575,33577,33580,33583],{"class":163,"line":292},[290,33571,7902],{"class":541},[290,33573,33574],{"class":295}," lastTime ",[290,33576,307],{"class":541},[290,33578,33579],{"class":295}," performance.",[290,33581,33582],{"class":303},"now",[290,33584,7594],{"class":295},[290,33586,33587,33589,33592,33594,33596],{"class":163,"line":330},[290,33588,7902],{"class":541},[290,33590,33591],{"class":295}," frameCount ",[290,33593,307],{"class":541},[290,33595,1198],{"class":461},[290,33597,471],{"class":295},[290,33599,33600],{"class":163,"line":337},[290,33601,334],{"emptyLinePlaceholder":333},[290,33603,33604,33606,33609,33611,33614],{"class":163,"line":364},[290,33605,7947],{"class":541},[290,33607,33608],{"class":303}," monitorFrame",[290,33610,484],{"class":295},[290,33612,33613],{"class":1561},"timestamp",[290,33615,646],{"class":295},[290,33617,33618,33620,33623,33625,33628,33630],{"class":163,"line":386},[290,33619,7958],{"class":541},[290,33621,33622],{"class":461}," delta",[290,33624,7288],{"class":541},[290,33626,33627],{"class":295}," timestamp ",[290,33629,7444],{"class":541},[290,33631,33632],{"class":295}," lastTime;\n",[290,33634,33635,33637,33640,33642,33645],{"class":163,"line":408},[290,33636,8105],{"class":541},[290,33638,33639],{"class":295}," (delta ",[290,33641,10364],{"class":541},[290,33643,33644],{"class":461}," 16.67",[290,33646,646],{"class":295},[290,33648,33649],{"class":163,"line":428},[290,33650,33651],{"class":455},"    \u002F\u002F >60fps threshold\n",[290,33653,33654,33657,33660,33662,33665,33668,33670,33673,33675,33677,33679,33682],{"class":163,"line":517},[290,33655,33656],{"class":295},"    console.",[290,33658,33659],{"class":303},"warn",[290,33661,484],{"class":295},[290,33663,33664],{"class":310},"`Frame drop detected: ${",[290,33666,33667],{"class":295},"delta",[290,33669,42],{"class":310},[290,33671,33672],{"class":303},"toFixed",[290,33674,484],{"class":310},[290,33676,194],{"class":461},[290,33678,3914],{"class":310},[290,33680,33681],{"class":310},"}ms`",[290,33683,500],{"class":295},[290,33685,33686],{"class":163,"line":523},[290,33687,771],{"class":295},[290,33689,33690,33693,33695],{"class":163,"line":532},[290,33691,33692],{"class":295},"  lastTime ",[290,33694,307],{"class":541},[290,33696,33697],{"class":295}," timestamp;\n",[290,33699,33700,33703,33706],{"class":163,"line":551},[290,33701,33702],{"class":295},"  frameCount",[290,33704,33705],{"class":541},"++",[290,33707,471],{"class":295},[290,33709,33710,33712,33715,33717,33720,33722,33724],{"class":163,"line":586},[290,33711,8105],{"class":541},[290,33713,33714],{"class":295}," (frameCount ",[290,33716,296],{"class":541},[290,33718,33719],{"class":461}," 300",[290,33721,490],{"class":295},[290,33723,13074],{"class":303},[290,33725,33726],{"class":295},"(monitorFrame);\n",[290,33728,33729],{"class":163,"line":602},[290,33730,620],{"class":295},[290,33732,33733],{"class":163,"line":617},[290,33734,334],{"emptyLinePlaceholder":333},[290,33736,33737,33739],{"class":163,"line":623},[290,33738,13074],{"class":303},[290,33740,33726],{"class":295},[47,33742],{},[50,33744,3728],{"id":3727},[2250,33746,33747,33763],{},[2253,33748,33749],{},[2256,33750,33751,33753,33755,33757,33759,33761],{},[2259,33752,3737],{},[2259,33754,2276],{},[2259,33756,2287],{},[2259,33758,2297],{},[2259,33760,2308],{},[2259,33762,2267],{},[2269,33764,33765,33784,33804],{},[2256,33766,33767,33773,33775,33777,33779,33781],{},[2274,33768,33769,1203,33771],{},[18,33770,30596],{},[18,33772,22267],{},[2274,33774,3808],{},[2274,33776,8465],{},[2274,33778,8462],{},[2274,33780,3808],{},[2274,33782,33783],{},"Standardized 3D transform syntax",[2256,33785,33786,33790,33792,33794,33796,33798],{},[2274,33787,33788],{},[18,33789,3797],{},[2274,33791,3800],{},[2274,33793,3800],{},[2274,33795,2300],{},[2274,33797,3767],{},[2274,33799,33800,33801,33803],{},"Safari \u003C15.4 requires ",[18,33802,3834],{}," prefix",[2256,33805,33806,33809,33811,33813,33815,33817],{},[2274,33807,33808],{},"Compositor Thread Optimization",[2274,33810,3821],{},[2274,33812,3821],{},[2274,33814,3821],{},[2274,33816,3821],{},[2274,33818,33819],{},"Varies by engine layer promotion thresholds",[14,33821,33822,33824,33825,33827,33828,2351,33830,33832,33833,33836],{},[62,33823,13313],{}," Always pair hardware-accelerated transforms with a baseline ",[18,33826,887],{}," on ",[18,33829,76],{},[18,33831,18299],{}," for older browsers. Use ",[18,33834,33835],{},"@supports (transform: translate3d(0,0,0))"," to gate advanced patterns.",[47,33838],{},[50,33840,24968],{"id":24967},[2250,33842,33843,33853],{},[2253,33844,33845],{},[2256,33846,33847,33849,33851],{},[2259,33848,2338],{},[2259,33850,3876],{},[2259,33852,24981],{},[2269,33854,33855,33882,33908,33927],{},[2256,33856,33857,33862,33871],{},[2274,33858,33859],{},[62,33860,33861],{},"GPU memory exhaustion",[2274,33863,33864,33865,33867,33868,33870],{},"Global ",[18,33866,3797],{}," or excessive ",[18,33869,22267],{}," on static DOM",[2274,33872,33873,33874,3041,33876,33878,33879,33881],{},"Apply conditionally via ",[18,33875,3689],{},[18,33877,1532],{}," or JS event listeners; reset to ",[18,33880,250],{}," post-animation",[2256,33883,33884,33889,33901],{},[2274,33885,33886],{},[62,33887,33888],{},"Janky layout recalculations",[2274,33890,16092,33891,569,33893,569,33895,569,33897,569,33899],{},[18,33892,1748],{},[18,33894,2722],{},[18,33896,2728],{},[18,33898,2731],{},[18,33900,2725],{},[2274,33902,3920,33903,569,33905,33907],{},[18,33904,2071],{},[18,33906,16108],{},", or CSS Grid\u002FFlexbox layout shifts",[2256,33909,33910,33915,33921],{},[2274,33911,33912],{},[62,33913,33914],{},"Missing fallbacks for legacy engines",[2274,33916,33917,33918,33920],{},"Assuming ",[18,33919,22267],{}," works identically across WebKit\u002FBlink",[2274,33922,33923,33924,33926],{},"Test on Safari 14+ and iOS WebViews; use ",[18,33925,2086],{}," and progressive enhancement",[2256,33928,33929,33934,33941],{},[2274,33930,33931],{},[62,33932,33933],{},"SVG rasterization bottlenecks",[2274,33935,33936,33937,33940],{},"Complex SVG filters or ",[18,33938,33939],{},"vector-effect"," during animation",[2274,33942,33943,33944,16081,33947,3041,33950,33953,33954,33957],{},"Pre-rasterize SVGs to ",[18,33945,33946],{},"\u003Ccanvas>",[18,33948,33949],{},"\u003Cimg>",[18,33951,33952],{},"\u003Cpicture>"," for animated assets; avoid ",[18,33955,33956],{},"filter: blur()"," on SVG paths",[47,33959],{},[50,33961,1316],{"id":1315},[14,33963,33964,33969,33970,33972,33973,33975,33976,33978],{},[62,33965,5714,33966,33968],{},[18,33967,22267],{}," still force GPU acceleration in modern browsers?","\nWhile historically used as a hack to trigger layer promotion, modern engines now rely on ",[18,33971,3797],{}," and explicit ",[18,33974,103],{}," values for intelligent compositing. It remains a valid fallback for older Safari versions but should be paired with explicit ",[18,33977,3797],{}," declarations for predictable behavior across Blink, Gecko, and WebKit.",[14,33980,33981,33984,33985,69,33987,33989],{},[62,33982,33983],{},"How do I know if an animation is running on the compositor thread?","\nEnable ",[18,33986,16161],{},[18,33988,16164],{}," in DevTools → More Tools → Rendering. If an animated element displays a blue border and does not trigger orange paint flashes during state changes, it is being handled by the GPU compositor.",[14,33991,33992,33997,33998,34000,34001,34003],{},[62,33993,33994,33995,5734],{},"When should I avoid using ",[18,33996,3797],{},"\nAvoid applying ",[18,33999,3797],{}," globally or to static elements. Only use it for elements that will animate within the next 100–200ms, and remove it via CSS state changes (",[18,34002,31717],{},") or JavaScript once the animation completes to free GPU memory and prevent texture thrashing.",[47,34005],{},[50,34007,1391],{"id":1390},[1393,34009,34010,34014,34020,34025,34030],{},[1396,34011,34012,16307],{},[27,34013,1481],{"href":1480},[1396,34015,34016,34019],{},[27,34017,34018],{"href":33320},"Will-change and the compositor thread"," — how the hint promotes layers and when it backfires.",[1396,34021,34022,34024],{},[27,34023,33331],{"href":8756}," — practical budgets for staying on the compositor.",[1396,34026,34027,34029],{},[27,34028,33506],{"href":8822}," — choosing declarative motion versus scripted timelines.",[1396,34031,34032,34034],{},[27,34033,4037],{"href":4036}," — scope layout work to components to keep paint and layout cheap.",[1430,34036,34037],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":286,"searchDepth":330,"depth":330,"links":34039},[34040,34043,34047,34051,34054,34055,34056,34057],{"id":33062,"depth":330,"text":33063,"children":34041},[34042],{"id":33093,"depth":337,"text":33094},{"id":33140,"depth":330,"text":34044,"children":34045},"Hardware Acceleration via transform & opacity",[34046],{"id":33164,"depth":337,"text":33165},{"id":33310,"depth":330,"text":33311,"children":34048},[34049],{"id":33334,"depth":337,"text":34050},"Dynamic will-change Management",{"id":33499,"depth":330,"text":33500,"children":34052},[34053],{"id":33556,"depth":337,"text":33557},{"id":3727,"depth":330,"text":3728},{"id":24967,"depth":330,"text":24968},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"CSS performance and GPU acceleration: browser rendering pipeline, compositor thread, hardware-accelerated transforms, and strategic layer promotion for 60fps UIs.",{"seoTitle":34060,"datePublished":1447,"dateModified":1447,"faq":34061},"CSS Performance & GPU Acceleration Guide",[34062,34065,34067],{"q":34063,"a":34064},"Does translateZ(0) still force GPU acceleration in modern browsers?","It was historically used as a hack to trigger layer promotion, but modern engines rely on will-change and explicit transform values for intelligent compositing. It remains a valid fallback for older Safari versions but should be paired with explicit will-change declarations.",{"q":33983,"a":34066},"Enable Paint flashing and Layer borders in DevTools under More Tools then Rendering. If an animated element shows a blue border and does not trigger orange paint flashes during state changes, it is being handled by the GPU compositor.",{"q":34068,"a":34069},"When should I avoid using will-change?","Avoid applying will-change globally or to static elements. Only use it for elements that will animate within the next 100 to 200 milliseconds, then remove it via will-change: auto or JavaScript once the animation completes to free GPU memory.","\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration",{"title":32952,"description":34058},"css-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Findex","9RISP9OaSEkqYEQE01JxyfVeEPT2uF-qFMDD-wLBDrU",{"id":34075,"title":34076,"body":34077,"description":34866,"extension":1444,"meta":34867,"navigation":333,"path":34876,"seo":34877,"stem":34878,"__hash__":34879},"content\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Foptimizing-css-animations-for-60fps\u002Findex.md","Optimizing CSS Animations for 60fps: A Technical Reference for UI Engineers",{"type":7,"value":34078,"toc":34856},[34079,34082,34091,34095,34117,34119,34123,34126,34135,34187,34191,34286,34303,34305,34309,34334,34349,34354,34422,34424,34428,34431,34484,34486,34490,34493,34605,34620,34622,34626,34699,34701,34703,34786,34788,34790,34804,34817,34826,34828,34853],[10,34080,34076],{"id":34081},"optimizing-css-animations-for-60fps-a-technical-reference-for-ui-engineers",[14,34083,34084,34085,34087,34088,34090],{},"Achieving a consistent 60fps frame rate requires strict adherence to the compositor thread. This page is part of the ",[27,34086,1736],{"href":22660}," guide and provides a precision-focused reference for diagnosing layout thrashing, leveraging hardware acceleration, and implementing fallbacks for complex UI states. By isolating animation properties to the ",[27,34089,1481],{"href":1480}," paradigm, developers can eliminate main-thread blocking and ensure buttery-smooth transitions across modern viewports.",[14,34092,34093],{},[62,34094,21697],{},[1393,34096,34097,34105,34111,34114],{},[1396,34098,34099,34100,569,34102,34104],{},"Compositor-only properties (",[18,34101,103],{},[18,34103,76],{},") bypass layout and paint",[1396,34106,34107,34108,34110],{},"Strategic layer promotion via ",[18,34109,3797],{}," and CSS containment",[1396,34112,34113],{},"Avoiding forced synchronous layouts and style recalculations",[1396,34115,34116],{},"Graceful degradation for low-power devices and reduced motion preferences",[47,34118],{},[50,34120,34122],{"id":34121},"the-compositor-thread-layer-promotion","The Compositor Thread & Layer Promotion",[14,34124,34125],{},"The browser rendering pipeline splits execution between the main thread (JavaScript, style recalculation, layout, paint) and the compositor thread (layer compositing, rasterization, display). When an element animates on the main thread, every frame triggers expensive DOM recalculations. Promoting an element to its own GPU layer allows the compositor to manipulate it independently, bypassing the main thread entirely.",[14,34127,34128,34129,34134],{},"Layer promotion occurs automatically for certain properties, but explicit hints are often required for predictable promotion, which is exactly what ",[27,34130,34131,34133],{"href":33320},[18,34132,3797],{}," and the compositor thread"," covers in depth. Each promoted layer consumes GPU VRAM. Over-promotion triggers memory pressure, causing layer eviction and sudden frame drops.",[133,34136,140,34139,140,34142,140,34145,140,34148,140,34150,140,34154,140,34156,140,34159,140,34161,140,34163,140,34166,140,34168,140,34171,140,34175,140,34179,140,34181,140,34183,140,34185],{"viewBox":34137,"role":136,"ariaLabel":34138,"xmlns":138,"style":139},"0 0 720 220","Rendering pipeline split between main thread and compositor thread",[142,34140,34141],{},"Main thread versus compositor thread",[146,34143,34144],{},"Style, layout, and paint run on the main thread; only transform and opacity are handled directly by the compositor thread.",[150,34146,34147],{"x":152,"y":153,"style":1781},"Where each frame is built",[171,34149],{"x":158,"y":1788,"width":26459,"height":4195,"rx":836,"fill":72,"stroke":167,"strokeWidth":1789},[150,34151,34153],{"x":34152,"y":9105,"style":5883},"239","Main thread",[171,34155],{"x":8879,"y":6642,"width":4147,"height":4146,"rx":5901,"fill":177,"opacity":1883,"stroke":167,"strokeWidth":468},[150,34157,32984],{"x":4180,"y":34158,"style":6653},"121",[171,34160],{"x":2602,"y":6642,"width":4147,"height":4146,"rx":5901,"fill":177,"opacity":1883,"stroke":167,"strokeWidth":468},[150,34162,13462],{"x":8947,"y":34158,"style":6653},[171,34164],{"x":34165,"y":6642,"width":4147,"height":4146,"rx":5901,"fill":177,"opacity":1883,"stroke":167,"strokeWidth":468},"316",[150,34167,13465],{"x":10266,"y":34158,"style":6653},[150,34169,34170],{"x":34152,"y":6673,"style":1808},"animate here = jank risk",[171,34172],{"x":34173,"y":1788,"width":34174,"height":4195,"rx":836,"fill":177,"opacity":11349,"stroke":167,"strokeWidth":1789},"478","218",[150,34176,34178],{"x":34177,"y":9105,"style":5883},"587","Compositor thread",[150,34180,103],{"x":34177,"y":4158,"style":183},[150,34182,76],{"x":34177,"y":4195,"style":183},[150,34184,21774],{"x":34177,"y":6673,"style":1808},[163,34186],{"x1":26462,"y1":5163,"x2":34173,"y2":5163,"stroke":167,"strokeWidth":1824},[14,34188,34189],{},[62,34190,1544],{},[281,34192,34194],{"className":438,"code":34193,"language":440,"meta":286,"style":286},".micro-interaction {\n  transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);\n  will-change: transform;\n}\n.micro-interaction:hover {\n  transform: translate3d(0, -4px, 0) scale(1.02);\n}\n",[18,34195,34196,34203,34233,34239,34243,34250,34282],{"__ignoreMap":286},[290,34197,34198,34201],{"class":163,"line":292},[290,34199,34200],{"class":303},".micro-interaction",[290,34202,450],{"class":295},[290,34204,34205,34207,34209,34211,34213,34215,34217,34219,34221,34223,34225,34227,34229,34231],{"class":163,"line":330},[290,34206,526],{"class":461},[290,34208,1880],{"class":295},[290,34210,199],{"class":461},[290,34212,1886],{"class":541},[290,34214,561],{"class":461},[290,34216,484],{"class":295},[290,34218,566],{"class":461},[290,34220,569],{"class":295},[290,34222,572],{"class":461},[290,34224,569],{"class":295},[290,34226,566],{"class":461},[290,34228,569],{"class":295},[290,34230,468],{"class":461},[290,34232,500],{"class":295},[290,34234,34235,34237],{"class":163,"line":337},[290,34236,3518],{"class":461},[290,34238,15503],{"class":295},[290,34240,34241],{"class":163,"line":364},[290,34242,620],{"class":295},[290,34244,34245,34248],{"class":163,"line":386},[290,34246,34247],{"class":303},".micro-interaction:hover",[290,34249,450],{"class":295},[290,34251,34252,34254,34256,34258,34260,34262,34264,34266,34268,34270,34272,34274,34276,34278,34280],{"class":163,"line":408},[290,34253,476],{"class":461},[290,34255,465],{"class":295},[290,34257,30731],{"class":461},[290,34259,484],{"class":295},[290,34261,487],{"class":461},[290,34263,569],{"class":295},[290,34265,3189],{"class":461},[290,34267,674],{"class":541},[290,34269,569],{"class":295},[290,34271,487],{"class":461},[290,34273,490],{"class":295},[290,34275,493],{"class":461},[290,34277,484],{"class":295},[290,34279,2057],{"class":461},[290,34281,500],{"class":295},[290,34283,34284],{"class":163,"line":428},[290,34285,620],{"class":295},[14,34287,34288],{},[86,34289,34290,34291,34293,34294,34296,34297,34299,34300,34302],{},"Note: ",[18,34292,30731],{}," forces layer promotion on legacy browsers. Modern engines accept ",[18,34295,16108],{},". Always strip ",[18,34298,3797],{}," after ",[18,34301,2769],{}," to release VRAM.",[47,34304],{},[50,34306,34308],{"id":34307},"isolating-animation-properties","Isolating Animation Properties",[14,34310,33148,34311,69,34313,34315,34316,569,34318,569,34320,569,34322,569,34324,569,34326,34328,34329,2351,34331,34333],{},[18,34312,103],{},[18,34314,76],{}," are guaranteed to run exclusively on the compositor thread. Any property that affects document flow (",[18,34317,2728],{},[18,34319,2731],{},[18,34321,1748],{},[18,34323,2722],{},[18,34325,2725],{},[18,34327,793],{},") triggers layout recalculation. Visual properties like ",[18,34330,2675],{},[18,34332,831],{}," trigger paint.",[14,34335,34336,34337,569,34339,34342,34343,34345,34346,34348],{},"Complex visual effects (",[18,34338,6792],{},[18,34340,34341],{},"backdrop-filter",") force the browser to rasterize a new bitmap per frame, bypassing the compositor thread and introducing measurable overhead. For micro-interactions, map state changes strictly to ",[18,34344,103],{}," matrices and ",[18,34347,76],{}," values.",[14,34350,34351],{},[62,34352,34353],{},"Property Mapping:",[2250,34355,34356,34369],{},[2253,34357,34358],{},[2256,34359,34360,34363,34366],{},[2259,34361,34362],{},"Target Effect",[2259,34364,34365],{},"❌ Layout\u002FPaint Trigger",[2259,34367,34368],{},"✅ Compositor Safe",[2269,34370,34371,34389,34404],{},[2256,34372,34373,34376,34384],{},[2274,34374,34375],{},"Position Shift",[2274,34377,34378,569,34380,569,34382],{},[18,34379,2728],{},[18,34381,2731],{},[18,34383,2725],{},[2274,34385,34386],{},[18,34387,34388],{},"transform: translate()",[2256,34390,34391,34394,34400],{},[2274,34392,34393],{},"Size\u002FScale",[2274,34395,34396,569,34398],{},[18,34397,1748],{},[18,34399,2722],{},[2274,34401,34402],{},[18,34403,2071],{},[2256,34405,34406,34409,34415],{},[2274,34407,34408],{},"Visibility",[2274,34410,34411,569,34413],{},[18,34412,59],{},[18,34414,18299],{},[2274,34416,34417,21541,34419],{},[18,34418,76],{},[18,34420,34421],{},"pointer-events",[47,34423],{},[50,34425,34427],{"id":34426},"debugging-jank-frame-drops","Debugging Jank & Frame Drops",[14,34429,34430],{},"Use browser DevTools to isolate the exact pipeline bottleneck. Follow this diagnostic workflow:",[3017,34432,34433,34439,34452,34458,34475],{},[1396,34434,34435,34438],{},[62,34436,34437],{},"Open the Performance Panel:"," Record a 3-second trace during the target interaction.",[1396,34440,34441,34444,34445,569,34448,8393,34450,42],{},[62,34442,34443],{},"Enable Rendering Overlays:"," In the Rendering tab, toggle ",[18,34446,34447],{},"FPS meter",[18,34449,16164],{},[18,34451,16161],{},[1396,34453,34454,34457],{},[62,34455,34456],{},"Identify Frame Drops:"," Look for red\u002Fyellow frames in the FPS track. Hover over them to see the bottleneck.",[1396,34459,34460,34463,34464,2351,34466,34469,34470,569,34472,34474],{},[62,34461,34462],{},"Detect Forced Reflows:"," Check the main thread flame chart for ",[18,34465,13462],{},[18,34467,34468],{},"Recalculate Style"," blocks during animation. If JavaScript reads a layout property (",[18,34471,33536],{},[18,34473,7533],{},") immediately after writing a style, it forces synchronous layout.",[1396,34476,34477,34480,34481,34483],{},[62,34478,34479],{},"Isolate Main-Thread Blocking:"," Any long task exceeding 50ms will drop frames. Move state toggles to ",[18,34482,13074],{}," or delegate entirely to CSS class switching.",[47,34485],{},[50,34487,34489],{"id":34488},"fallbacks-reduced-motion","Fallbacks & Reduced Motion",[14,34491,34492],{},"Accessibility and hardware constraints require graceful degradation. Mobile SoCs enforce strict VRAM budgets, and users with vestibular disorders require motion reduction. Implement static alternatives that preserve UI feedback without triggering the compositor.",[281,34494,34496],{"className":438,"code":34495,"language":440,"meta":286,"style":286},"@media (prefers-reduced-motion: no-preference) {\n  .fade-element {\n    transition: opacity 0.25s ease;\n  }\n  .fade-element.hidden {\n    opacity: 0;\n  }\n}\n@media (prefers-reduced-motion: reduce) {\n  .fade-element {\n    transition: none;\n  }\n  .fade-element.hidden {\n    display: none;\n  }\n}\n",[18,34497,34498,34504,34511,34525,34529,34536,34546,34550,34554,34560,34566,34576,34580,34586,34597,34601],{"__ignoreMap":286},[290,34499,34500,34502],{"class":163,"line":292},[290,34501,874],{"class":541},[290,34503,16018],{"class":295},[290,34505,34506,34509],{"class":163,"line":330},[290,34507,34508],{"class":303},"  .fade-element",[290,34510,450],{"class":295},[290,34512,34513,34515,34517,34519,34521,34523],{"class":163,"line":337},[290,34514,4745],{"class":461},[290,34516,6384],{"class":295},[290,34518,1668],{"class":461},[290,34520,1886],{"class":541},[290,34522,545],{"class":461},[290,34524,471],{"class":295},[290,34526,34527],{"class":163,"line":364},[290,34528,771],{"class":295},[290,34530,34531,34534],{"class":163,"line":386},[290,34532,34533],{"class":303},"  .fade-element.hidden",[290,34535,450],{"class":295},[290,34537,34538,34540,34542,34544],{"class":163,"line":408},[290,34539,733],{"class":461},[290,34541,465],{"class":295},[290,34543,487],{"class":461},[290,34545,471],{"class":295},[290,34547,34548],{"class":163,"line":428},[290,34549,771],{"class":295},[290,34551,34552],{"class":163,"line":517},[290,34553,620],{"class":295},[290,34555,34556,34558],{"class":163,"line":523},[290,34557,874],{"class":541},[290,34559,877],{"class":295},[290,34561,34562,34564],{"class":163,"line":532},[290,34563,34508],{"class":303},[290,34565,450],{"class":295},[290,34567,34568,34570,34572,34574],{"class":163,"line":551},[290,34569,4745],{"class":461},[290,34571,465],{"class":295},[290,34573,72],{"class":461},[290,34575,471],{"class":295},[290,34577,34578],{"class":163,"line":586},[290,34579,771],{"class":295},[290,34581,34582,34584],{"class":163,"line":602},[290,34583,34533],{"class":303},[290,34585,450],{"class":295},[290,34587,34588,34591,34593,34595],{"class":163,"line":617},[290,34589,34590],{"class":461},"    display",[290,34592,465],{"class":295},[290,34594,72],{"class":461},[290,34596,471],{"class":295},[290,34598,34599],{"class":163,"line":623},[290,34600,771],{"class":295},[290,34602,34603],{"class":163,"line":628},[290,34604,620],{"class":295},[14,34606,34607],{},[86,34608,34609,34610,2351,34612,34614,34615,3942,34617,34619],{},"Note: Never animate ",[18,34611,59],{},[18,34613,18299],{}," directly. Pair ",[18,34616,907],{},[18,34618,2365],{}," to prevent interaction with hidden elements.",[47,34621],{},[50,34623,34625],{"id":34624},"browser-support-matrix","Browser Support Matrix",[2250,34627,34628,34641],{},[2253,34629,34630],{},[2256,34631,34632,34635,34638],{},[2259,34633,34634],{},"Engine",[2259,34636,34637],{},"Compositor Isolation",[2259,34639,34640],{},"Layer Promotion Notes",[2269,34642,34643,34658,34673,34684],{},[2256,34644,34645,34650,34653],{},[2274,34646,34647],{},[62,34648,34649],{},"Chromium",[2274,34651,34652],{},"Full since v60",[2274,34654,34655,34657],{},[18,34656,3797],{}," stabilized in v80+",[2256,34659,34660,34664,34667],{},[2274,34661,34662],{},[62,34663,2287],{},[2274,34665,34666],{},"Full",[2274,34668,34669,34670,34672],{},"Explicit ",[18,34671,3797],{}," required for consistent promotion on legacy versions",[2256,34674,34675,34679,34681],{},[2274,34676,34677],{},[62,34678,2297],{},[2274,34680,34666],{},[2274,34682,34683],{},"iOS Safari aggressively promotes layers but enforces strict VRAM limits on older devices",[2256,34685,34686,34690,34693],{},[2274,34687,34688],{},[62,34689,2308],{},[2274,34691,34692],{},"Full (Chromium)",[2274,34694,34695,34696,34698],{},"Legacy EdgeHTML requires ",[18,34697,30731],{}," for hardware acceleration",[47,34700],{},[50,34702,13329],{"id":13328},[2250,34704,34705,34716],{},[2253,34706,34707],{},[2256,34708,34709,34712,34714],{},[2259,34710,34711],{},"Symptom",[2259,34713,3876],{},[2259,34715,8563],{},[2269,34717,34718,34748,34767],{},[2256,34719,34720,34725,34738],{},[2274,34721,34722],{},[62,34723,34724],{},"Jank on scroll or rapid hover",[2274,34726,34727,34728,569,34730,569,34732,1745,34734,3041,34736],{},"Main-thread layout thrashing from animating ",[18,34729,1748],{},[18,34731,2722],{},[18,34733,2725],{},[18,34735,2728],{},[18,34737,2731],{},[2274,34739,16103,34740,1203,34742,29327,34744,34747],{},[18,34741,34388],{},[18,34743,25012],{},[18,34745,34746],{},"contain: layout style"," to parent containers.",[2256,34749,34750,34755,34760],{},[2274,34751,34752],{},[62,34753,34754],{},"Excessive memory \u002F GPU crashes",[2274,34756,34757,34759],{},[18,34758,2075],{}," applied to too many DOM nodes simultaneously, exhausting VRAM",[2274,34761,29271,34762,34764,34765,42],{},[18,34763,3797],{}," only during active states via class toggling. Remove immediately on ",[18,34766,2769],{},[2256,34768,34769,34774,34777],{},[2274,34770,34771],{},[62,34772,34773],{},"Blurry text \u002F subpixel artifacts",[2274,34775,34776],{},"Hardware acceleration forces rasterization at non-integer scales or with specific transform matrices",[2274,34778,1499,34779,34781,34782,34785],{},[18,34780,22267],{}," sparingly. Apply ",[18,34783,34784],{},"backface-visibility: hidden"," and align scale values to the device pixel ratio.",[47,34787],{},[50,34789,1316],{"id":1315},[14,34791,34792,34797,34798,34800,34801,34803],{},[62,34793,6487,34794,34796],{},[18,34795,2075],{}," still recommended for 60fps animations?","\nYes, but strictly as a temporary hint. Apply it via a class immediately before the animation triggers and remove it on ",[18,34799,2769],{},". Persistent ",[18,34802,3797],{}," causes VRAM bloat and degrades overall page performance.",[14,34805,34806,34809,34810,2351,34813,34816],{},[62,34807,34808],{},"Why do CSS filters cause frame drops even when using transform?","\nFilters trigger a paint operation before compositing. Complex filters like ",[18,34811,34812],{},"blur()",[18,34814,34815],{},"drop-shadow()"," force the browser to rasterize a new bitmap each frame, bypassing the compositor thread and consuming significant GPU cycles.",[14,34818,34819,34822,34823,34825],{},[62,34820,34821],{},"Can I achieve 60fps without JavaScript?","\nAbsolutely. Pure CSS transitions and ",[18,34824,3968],{}," run natively on the compositor thread. JavaScript should only be used for state toggling, event delegation, or dynamic property calculation—not for frame-by-frame animation loops.",[50,34827,1391],{"id":1390},[1393,34829,34830,34835,34843,34848],{},[1396,34831,34832,34834],{},[27,34833,1736],{"href":22660}," — the parent guide for compositor-thread rendering.",[1396,34836,34837,34842],{},[27,34838,34839,34841],{"href":33320},[18,34840,3797],{}," and the Compositor Thread"," — when and how to hint layer promotion without exhausting VRAM.",[1396,34844,34845,34847],{},[27,34846,2511],{"href":1412}," — disable compositor-bound animation when users request it.",[1396,34849,34850,34852],{},[27,34851,5778],{"href":5777}," — the responsive-layout area, where 60fps transitions accompany size-driven component changes.",[1430,34854,34855],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":286,"searchDepth":330,"depth":330,"links":34857},[34858,34859,34860,34861,34862,34863,34864,34865],{"id":34121,"depth":330,"text":34122},{"id":34307,"depth":330,"text":34308},{"id":34426,"depth":330,"text":34427},{"id":34488,"depth":330,"text":34489},{"id":34624,"depth":330,"text":34625},{"id":13328,"depth":330,"text":13329},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Optimise CSS animations for 60fps: diagnose layout thrashing, leverage hardware acceleration, and implement compositor-thread-only animation patterns.",{"seoTitle":8757,"datePublished":1447,"dateModified":1447,"faq":34868},[34869,34872,34874],{"q":34870,"a":34871},"Is will-change: transform still recommended for 60fps animations?","Yes, but strictly as a temporary hint. Apply it via a class immediately before the animation triggers and remove it on transitionend. Persistent will-change causes VRAM bloat and degrades overall page performance.",{"q":34808,"a":34873},"Filters trigger a paint operation before compositing. Complex filters like blur() or drop-shadow() force the browser to rasterize a new bitmap each frame, bypassing the compositor thread and consuming significant GPU cycles.",{"q":34821,"a":34875},"Yes. Pure CSS transitions and keyframes run natively on the compositor thread. JavaScript should only handle state toggling, event delegation, or dynamic property calculation, not frame-by-frame animation loops.","\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Foptimizing-css-animations-for-60fps",{"title":34076,"description":34866},"css-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Foptimizing-css-animations-for-60fps\u002Findex","JlYBov92jwCcxE5DP0AsJ13kDWdUFW3gJGLZPfMv1zY",{"id":34881,"title":34882,"body":34883,"description":35663,"extension":1444,"meta":35664,"navigation":333,"path":35678,"seo":35679,"stem":35680,"__hash__":35681},"content\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fwill-change-and-the-compositor-thread\u002Findex.md","will-change and the Compositor Thread: Layer Promotion Without the Footguns",{"type":7,"value":34884,"toc":35654},[34885,34888,34901,34906,34926,34930,34954,34970,35025,35027,35036,35089,35336,35351,35353,35358,35362,35368,35478,35481,35541,35546,35548,35564,35566,35575,35587,35608,35623,35625,35652],[10,34886,34882],{"id":34887},"will-change-and-the-compositor-thread-layer-promotion-without-the-footguns",[14,34889,34890,34892,34893,34895,34896,34898,34899,42],{},[18,34891,2075],{}," is one of the most cargo-culted declarations in CSS. It is pasted onto elements as a generic \"make it fast\" charm, and just as often it makes things slower. The narrow problem this guide solves is understanding what ",[18,34894,3797],{}," does at the rendering-pipeline level — layer promotion onto the compositor thread — so you apply it deliberately, on the right properties, for the right duration, and avoid the memory blowups that overuse causes. This page sits under ",[27,34897,1736],{"href":22660},", and it goes a layer deeper than the throughput advice in ",[27,34900,8757],{"href":8756},[14,34902,34903],{},[62,34904,34905],{},"What you will understand by the end:",[1393,34907,34908,34911,34917,34923],{},[1396,34909,34910],{},"The split between the main thread and the compositor thread",[1396,34912,34913,34914,34916],{},"How ",[18,34915,3797],{}," promotes an element to its own layer",[1396,34918,34919,34920,34922],{},"Why permanent or scattershot ",[18,34921,3797],{}," backfires",[1396,34924,34925],{},"Which properties stay composited vs trigger layout\u002Fpaint",[50,34927,34929],{"id":34928},"two-threads-one-frame","Two threads, one frame",[14,34931,34932,34933,34935,34936,34939,34940,34943,34944,34946,34947,34950,34951,34953],{},"A browser produces each frame through a pipeline: ",[62,34934,1430],{}," (resolve which rules apply), ",[62,34937,34938],{},"layout"," (compute geometry), ",[62,34941,34942],{},"paint"," (fill pixels into layers), and ",[62,34945,33082],{}," (assemble the layers into the final image). The first three run on the ",[62,34948,34949],{},"main thread",", the same thread that runs your JavaScript and handles events. Compositing runs on a separate ",[62,34952,33086],{}," that can keep producing frames even while the main thread is busy.",[14,34955,34956,34957,34959,34960,34962,34963,569,34965,569,34967,34969],{},"The crucial consequence: if an animation only needs the compositor — moving an already-painted layer with ",[18,34958,103],{},", or blending it with ",[18,34961,76],{}," — the browser can animate it entirely off the main thread, at the display's refresh rate, without re-running layout or paint. If an animation changes a property that affects geometry (",[18,34964,1748],{},[18,34966,2728],{},[18,34968,2725],{},") it must re-run layout and paint on the main thread every frame, competing with your scripts and stalling when they are busy. The diagram lays out the two lanes and where each property family lives.",[133,34971,140,34973,140,34976,140,34979,140,34982,140,34985,140,34988,140,34990,140,34993,140,34995,140,34998,140,35001,140,35004,140,35008,140,35010,140,35012,140,35014,140,35018,140,35021],{"viewBox":2588,"role":136,"ariaLabel":34972,"xmlns":138,"style":139},"Main thread runs style layout and paint while the compositor thread handles transform and opacity",[142,34974,34975],{},"Main thread versus compositor thread lanes",[146,34977,34978],{},"The main thread lane runs style, layout, and paint for layout-triggering properties; the compositor lane animates transform and opacity independently.",[150,34980,34981],{"x":152,"y":153,"style":1781},"Main thread vs compositor thread",[171,34983],{"x":5891,"y":1788,"width":34984,"height":4180,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},"656",[150,34986,34153],{"x":5900,"y":9105,"style":34987},"text-anchor:start;fill:currentColor;font:600 13px sans-serif",[171,34989],{"x":1788,"y":6642,"width":4147,"height":8879,"rx":5901,"fill":177,"opacity":1883},[150,34991,1430],{"x":1843,"y":34992,"style":183},"123",[171,34994],{"x":6721,"y":6642,"width":4147,"height":8879,"rx":5901,"fill":177,"opacity":1883},[150,34996,34938],{"x":34997,"y":34992,"style":183},"256",[171,34999],{"x":35000,"y":6642,"width":4147,"height":8879,"rx":5901,"fill":177,"opacity":1883},"336",[150,35002,34942],{"x":35003,"y":34992,"style":183},"396",[150,35005,35007],{"x":35006,"y":34992,"style":5915},"572","width, top, margin",[171,35009],{"x":5891,"y":4184,"width":34984,"height":4180,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},[150,35011,34178],{"x":5900,"y":2622,"style":34987},[171,35013],{"x":1788,"y":5904,"width":12480,"height":8879,"rx":5901,"fill":177,"opacity":199},[150,35015,35017],{"x":4184,"y":35016,"style":183},"253","composite layers",[150,35019,35020],{"x":18875,"y":35016,"style":4181},"transform, opacity",[150,35022,35024],{"x":18875,"y":35023,"style":22612},"272","runs even when main thread is busy",[50,35026,4203],{"id":4202},[14,35028,35029,35030,35032,35033,35035],{},"Here ",[18,35031,3797],{}," is applied with intent: it is added only while the element is in the hover state (the moment a ",[18,35034,103],{}," is imminent) and is absent the rest of the time, so no layer is kept alive needlessly. The animation itself uses only composited properties.",[281,35037,35039],{"className":283,"code":35038,"language":285,"meta":286,"style":286},"\u003Carticle class=\"card\">\n  \u003Ch3>Promoted on demand\u003C\u002Fh3>\n  \u003Cp>A layer is created when interaction is imminent, not for the page's life.\u003C\u002Fp>\n\u003C\u002Farticle>\n",[18,35040,35041,35055,35068,35081],{"__ignoreMap":286},[290,35042,35043,35045,35047,35049,35051,35053],{"class":163,"line":292},[290,35044,296],{"class":295},[290,35046,11445],{"class":299},[290,35048,314],{"class":303},[290,35050,307],{"class":295},[290,35052,9295],{"class":310},[290,35054,327],{"class":295},[290,35056,35057,35059,35061,35064,35066],{"class":163,"line":330},[290,35058,367],{"class":295},[290,35060,2757],{"class":299},[290,35062,35063],{"class":295},">Promoted on demand\u003C\u002F",[290,35065,2757],{"class":299},[290,35067,327],{"class":295},[290,35069,35070,35072,35074,35077,35079],{"class":163,"line":337},[290,35071,367],{"class":295},[290,35073,14],{"class":299},[290,35075,35076],{"class":295},">A layer is created when interaction is imminent, not for the page's life.\u003C\u002F",[290,35078,14],{"class":299},[290,35080,327],{"class":295},[290,35082,35083,35085,35087],{"class":163,"line":364},[290,35084,431],{"class":295},[290,35086,11445],{"class":299},[290,35088,327],{"class":295},[281,35090,35092],{"className":438,"code":35091,"language":440,"meta":286,"style":286},".card {\n  border-radius: 12px;\n  padding: 1.25rem;\n  background: #fff;\n  \u002F* Composited-only animation: transform + opacity, never layout props. *\u002F\n  transition: transform 0.3s cubic-bezier(0.25, 1, 0.5, 1),\n              opacity 0.3s ease;\n  \u002F* NOTE: no will-change here in the base rule. *\u002F\n}\n\n\u002F* Promote the layer right before it is needed. :hover is a reasonable\n   \"imminent\" signal; the layer is torn down when hover ends. *\u002F\n.card:hover {\n  will-change: transform;\n  transform: translateY(-4px) scale(1.02);\n}\n\n\u002F* For an actively running keyframe animation, scoping will-change to the\n   animating state is also correct: it lives only while the class is on. *\u002F\n.card.is-animating {\n  will-change: transform, opacity;\n  animation: pulse 1s ease-in-out infinite;\n}\n\n@keyframes pulse {\n  50% { transform: scale(1.03); opacity: 0.9; }\n}\n",[18,35093,35094,35100,35112,35125,35135,35140,35170,35183,35188,35192,35196,35201,35206,35212,35218,35242,35246,35250,35255,35260,35267,35273,35289,35293,35297,35305,35332],{"__ignoreMap":286},[290,35095,35096,35098],{"class":163,"line":292},[290,35097,11528],{"class":303},[290,35099,450],{"class":295},[290,35101,35102,35104,35106,35108,35110],{"class":163,"line":330},[290,35103,1663],{"class":461},[290,35105,465],{"class":295},[290,35107,5894],{"class":461},[290,35109,674],{"class":541},[290,35111,471],{"class":295},[290,35113,35114,35116,35118,35121,35123],{"class":163,"line":337},[290,35115,10433],{"class":461},[290,35117,465],{"class":295},[290,35119,35120],{"class":461},"1.25",[290,35122,801],{"class":541},[290,35124,471],{"class":295},[290,35126,35127,35129,35131,35133],{"class":163,"line":364},[290,35128,1186],{"class":461},[290,35130,465],{"class":295},[290,35132,9138],{"class":461},[290,35134,471],{"class":295},[290,35136,35137],{"class":163,"line":386},[290,35138,35139],{"class":455},"  \u002F* Composited-only animation: transform + opacity, never layout props. *\u002F\n",[290,35141,35142,35144,35146,35148,35150,35152,35154,35156,35158,35160,35162,35164,35166,35168],{"class":163,"line":408},[290,35143,526],{"class":461},[290,35145,1880],{"class":295},[290,35147,199],{"class":461},[290,35149,1886],{"class":541},[290,35151,561],{"class":461},[290,35153,484],{"class":295},[290,35155,1668],{"class":461},[290,35157,569],{"class":295},[290,35159,468],{"class":461},[290,35161,569],{"class":295},[290,35163,798],{"class":461},[290,35165,569],{"class":295},[290,35167,468],{"class":461},[290,35169,583],{"class":295},[290,35171,35172,35175,35177,35179,35181],{"class":163,"line":428},[290,35173,35174],{"class":295},"              opacity ",[290,35176,199],{"class":461},[290,35178,1886],{"class":541},[290,35180,545],{"class":461},[290,35182,471],{"class":295},[290,35184,35185],{"class":163,"line":517},[290,35186,35187],{"class":455},"  \u002F* NOTE: no will-change here in the base rule. *\u002F\n",[290,35189,35190],{"class":163,"line":523},[290,35191,620],{"class":295},[290,35193,35194],{"class":163,"line":532},[290,35195,334],{"emptyLinePlaceholder":333},[290,35197,35198],{"class":163,"line":551},[290,35199,35200],{"class":455},"\u002F* Promote the layer right before it is needed. :hover is a reasonable\n",[290,35202,35203],{"class":163,"line":586},[290,35204,35205],{"class":455},"   \"imminent\" signal; the layer is torn down when hover ends. *\u002F\n",[290,35207,35208,35210],{"class":163,"line":602},[290,35209,11814],{"class":303},[290,35211,450],{"class":295},[290,35213,35214,35216],{"class":163,"line":617},[290,35215,3518],{"class":461},[290,35217,15503],{"class":295},[290,35219,35220,35222,35224,35226,35228,35230,35232,35234,35236,35238,35240],{"class":163,"line":623},[290,35221,476],{"class":461},[290,35223,465],{"class":295},[290,35225,481],{"class":461},[290,35227,484],{"class":295},[290,35229,3189],{"class":461},[290,35231,674],{"class":541},[290,35233,490],{"class":295},[290,35235,493],{"class":461},[290,35237,484],{"class":295},[290,35239,2057],{"class":461},[290,35241,500],{"class":295},[290,35243,35244],{"class":163,"line":628},[290,35245,620],{"class":295},[290,35247,35248],{"class":163,"line":634},[290,35249,334],{"emptyLinePlaceholder":333},[290,35251,35252],{"class":163,"line":649},[290,35253,35254],{"class":455},"\u002F* For an actively running keyframe animation, scoping will-change to the\n",[290,35256,35257],{"class":163,"line":660},[290,35258,35259],{"class":455},"   animating state is also correct: it lives only while the class is on. *\u002F\n",[290,35261,35262,35265],{"class":163,"line":688},[290,35263,35264],{"class":303},".card.is-animating",[290,35266,450],{"class":295},[290,35268,35269,35271],{"class":163,"line":693},[290,35270,3518],{"class":461},[290,35272,3521],{"class":295},[290,35274,35275,35277,35279,35281,35283,35285,35287],{"class":163,"line":698},[290,35276,6035],{"class":461},[290,35278,30290],{"class":295},[290,35280,468],{"class":461},[290,35282,1886],{"class":541},[290,35284,21329],{"class":461},[290,35286,21326],{"class":461},[290,35288,471],{"class":295},[290,35290,35291],{"class":163,"line":704},[290,35292,620],{"class":295},[290,35294,35295],{"class":163,"line":710},[290,35296,334],{"emptyLinePlaceholder":333},[290,35298,35299,35301,35303],{"class":163,"line":717},[290,35300,3968],{"class":541},[290,35302,30345],{"class":1561},[290,35304,450],{"class":295},[290,35306,35307,35309,35311,35313,35315,35317,35319,35322,35324,35326,35328,35330],{"class":163,"line":730},[290,35308,21075],{"class":303},[290,35310,790],{"class":295},[290,35312,103],{"class":461},[290,35314,465],{"class":295},[290,35316,493],{"class":461},[290,35318,484],{"class":295},[290,35320,35321],{"class":461},"1.03",[290,35323,14061],{"class":295},[290,35325,76],{"class":461},[290,35327,465],{"class":295},[290,35329,3589],{"class":461},[290,35331,809],{"class":295},[290,35333,35334],{"class":163,"line":742},[290,35335,620],{"class":295},[14,35337,35338,35339,35341,35342,35344,35345,35347,35348,35350],{},"The base ",[18,35340,11528],{}," rule deliberately omits ",[18,35343,3797],{},". Declaring it on ",[18,35346,3689],{}," means the browser allocates the layer as the pointer arrives and frees it as the pointer leaves — the hint exists exactly during the window the change is likely, which is what ",[18,35349,3797],{}," was designed for.",[50,35352,4795],{"id":4794},[14,35354,35355,35357],{},[18,35356,3797],{}," is a pre-promotion hint. Without it, the browser typically promotes an element to its own compositor layer at the instant an animation starts, which costs one frame to set up — visible as a tiny hitch on the first interaction. By naming the property ahead of time, you tell the engine \"prepare a layer for this,\" so the promotion happens before the animation rather than during it. The footgun is treating it as a permanent optimisation. Each layer carries GPU memory and compositing bookkeeping; promote dozens of elements, or leave the hint on forever, and you trade a one-frame setup cost for sustained memory pressure that can slow the whole page. The discipline is duration: add the hint near the change, scope it to the interactive or animating state, and let it be removed automatically when that state ends.",[50,35359,35361],{"id":35360},"variation-removing-the-hint-after-a-js-driven-animation","Variation: removing the hint after a JS-driven animation",[14,35363,35364,35365,35367],{},"When you trigger an animation in script, set ",[18,35366,3797],{}," just before and clear it on completion so the layer does not linger.",[281,35369,35371],{"className":2904,"code":35370,"language":2906,"meta":286,"style":286},"const card = document.querySelector(\".card\");\ncard.style.willChange = \"transform\";          \u002F\u002F promote\ncard.classList.add(\"is-animating\");\ncard.addEventListener(\"animationend\", () => {\n  card.style.willChange = \"auto\";             \u002F\u002F demote, free the layer\n  card.classList.remove(\"is-animating\");\n}, { once: true });\n",[18,35372,35373,35393,35408,35422,35440,35456,35469],{"__ignoreMap":286},[290,35374,35375,35377,35380,35382,35384,35386,35388,35391],{"class":163,"line":292},[290,35376,7282],{"class":541},[290,35378,35379],{"class":461}," card",[290,35381,7288],{"class":541},[290,35383,7291],{"class":295},[290,35385,7416],{"class":303},[290,35387,484],{"class":295},[290,35389,35390],{"class":310},"\".card\"",[290,35392,500],{"class":295},[290,35394,35395,35398,35400,35403,35405],{"class":163,"line":330},[290,35396,35397],{"class":295},"card.style.willChange ",[290,35399,307],{"class":541},[290,35401,35402],{"class":310}," \"transform\"",[290,35404,6144],{"class":295},[290,35406,35407],{"class":455},"\u002F\u002F promote\n",[290,35409,35410,35413,35415,35417,35420],{"class":163,"line":337},[290,35411,35412],{"class":295},"card.classList.",[290,35414,2935],{"class":303},[290,35416,484],{"class":295},[290,35418,35419],{"class":310},"\"is-animating\"",[290,35421,500],{"class":295},[290,35423,35424,35427,35429,35431,35434,35436,35438],{"class":163,"line":364},[290,35425,35426],{"class":295},"card.",[290,35428,2972],{"class":303},[290,35430,484],{"class":295},[290,35432,35433],{"class":310},"\"animationend\"",[290,35435,9454],{"class":295},[290,35437,2988],{"class":541},[290,35439,450],{"class":295},[290,35441,35442,35445,35447,35450,35453],{"class":163,"line":386},[290,35443,35444],{"class":295},"  card.style.willChange ",[290,35446,307],{"class":541},[290,35448,35449],{"class":310}," \"auto\"",[290,35451,35452],{"class":295},";             ",[290,35454,35455],{"class":455},"\u002F\u002F demote, free the layer\n",[290,35457,35458,35461,35463,35465,35467],{"class":163,"line":408},[290,35459,35460],{"class":295},"  card.classList.",[290,35462,7671],{"class":303},[290,35464,484],{"class":295},[290,35466,35419],{"class":310},[290,35468,500],{"class":295},[290,35470,35471,35474,35476],{"class":163,"line":428},[290,35472,35473],{"class":295},"}, { once: ",[290,35475,5083],{"class":461},[290,35477,7574],{"class":295},[14,35479,35480],{},"For reduced-motion users, skip both the promotion and the animation entirely, since there is nothing to composite:",[281,35482,35484],{"className":438,"code":35483,"language":440,"meta":286,"style":286},"@media (prefers-reduced-motion: reduce) {\n  .card:hover { will-change: auto; transform: none; }\n  .card.is-animating { will-change: auto; animation: none; }\n}\n",[18,35485,35486,35492,35514,35537],{"__ignoreMap":286},[290,35487,35488,35490],{"class":163,"line":292},[290,35489,874],{"class":541},[290,35491,877],{"class":295},[290,35493,35494,35496,35498,35500,35502,35504,35506,35508,35510,35512],{"class":163,"line":330},[290,35495,23491],{"class":303},[290,35497,790],{"class":295},[290,35499,3797],{"class":461},[290,35501,465],{"class":295},[290,35503,250],{"class":461},[290,35505,828],{"class":295},[290,35507,103],{"class":461},[290,35509,465],{"class":295},[290,35511,72],{"class":461},[290,35513,809],{"class":295},[290,35515,35516,35519,35521,35523,35525,35527,35529,35531,35533,35535],{"class":163,"line":337},[290,35517,35518],{"class":303},"  .card.is-animating",[290,35520,790],{"class":295},[290,35522,3797],{"class":461},[290,35524,465],{"class":295},[290,35526,250],{"class":461},[290,35528,828],{"class":295},[290,35530,5178],{"class":461},[290,35532,465],{"class":295},[290,35534,72],{"class":461},[290,35536,809],{"class":295},[290,35538,35539],{"class":163,"line":364},[290,35540,620],{"class":295},[14,35542,35543,35544,42],{},"This honours the same preference handled in detail in ",[27,35545,2511],{"href":1412},[50,35547,4904],{"id":2247},[14,35549,35550,35552,35553,69,35555,35557,35558,35560,35561,35563],{},[18,35551,3797],{}," is supported in Chrome 36+, Edge 79+, Firefox 36+, and Safari 9.1+, so it is universally available on evergreen browsers; unsupporting engines simply ignore it and fall back to on-demand promotion. The compositor-thread behaviour for ",[18,35554,103],{},[18,35556,76],{}," is consistent across all of them. No ",[18,35559,2086],{}," guard is needed, because an ignored ",[18,35562,3797],{}," is harmless.",[50,35565,1316],{"id":1315},[14,35567,35568,35574],{},[62,35569,35570,35571,35573],{},"What does ",[18,35572,3797],{}," actually do?","\nIt hints to the browser that a property is about to change, prompting it to promote the element to its own compositor layer ahead of time. That avoids the one-frame stall of promoting the layer at the moment the animation starts.",[14,35576,35577,35583,35584,35586],{},[62,35578,35579,35580,35582],{},"Why is overusing ",[18,35581,3797],{}," bad?","\nEach promoted layer consumes GPU memory and bookkeeping. Applying ",[18,35585,3797],{}," to many elements, or leaving it on permanently, can exhaust memory, slow compositing, and make the page slower than if you had never used it.",[14,35588,35589,35592,69,35594,35596,35597,569,35599,569,35601,569,35603,8393,35605,35607],{},[62,35590,35591],{},"Which properties animate on the compositor thread?",[18,35593,103],{},[18,35595,76],{}," are handled by the compositor and do not trigger layout or paint. Properties like ",[18,35598,1748],{},[18,35600,2722],{},[18,35602,2728],{},[18,35604,2731],{},[18,35606,2725],{}," trigger layout on the main thread and cannot be composited cheaply.",[14,35609,35610,35616,35617,35619,35620,35622],{},[62,35611,35612,35613,35615],{},"Should I add ",[18,35614,3797],{}," in my base CSS rule?","\nUsually no. Prefer adding it just before the change, for example on ",[18,35618,3689],{}," or via a class, and removing it after. A permanent ",[18,35621,3797],{}," in the base rule keeps a layer alive for the element's whole lifetime.",[50,35624,1391],{"id":1390},[1393,35626,35627,35632,35637,35642,35647],{},[1396,35628,35629,35631],{},[27,35630,1736],{"href":22660}," — the parent guide on rendering performance.",[1396,35633,35634,35636],{},[27,35635,8757],{"href":8756}," — diagnosing jank and keeping to composited properties.",[1396,35638,35639,35641],{},[27,35640,18535],{"href":18534}," — applying composited transitions to interactive states.",[1396,35643,35644,35646],{},[27,35645,2511],{"href":1412}," — skipping promotion and animation when motion is off.",[1396,35648,35649,35651],{},[27,35650,18710],{"href":1426}," — composited animation inside container-aware components.",[1430,35653,32924],{},{"title":286,"searchDepth":330,"depth":330,"links":35655},[35656,35657,35658,35659,35660,35661,35662],{"id":34928,"depth":330,"text":34929},{"id":4202,"depth":330,"text":4203},{"id":4794,"depth":330,"text":4795},{"id":35360,"depth":330,"text":35361},{"id":2247,"depth":330,"text":4904},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"How will-change promotes layers onto the compositor thread, why overusing it backfires, and which properties stay off the main thread for jank-free animation.",{"seoTitle":35665,"datePublished":1447,"dateModified":1447,"faq":35666},"will-change & the Compositor Thread",[35667,35670,35673,35675],{"q":35668,"a":35669},"What does will-change actually do?","It hints to the browser that a property is about to change, prompting it to promote the element to its own compositor layer ahead of time. That avoids the one-frame stall of promoting the layer at the moment the animation starts.",{"q":35671,"a":35672},"Why is overusing will-change bad?","Each promoted layer consumes GPU memory and bookkeeping. Applying will-change to many elements, or leaving it on permanently, can exhaust memory, slow compositing, and make the page slower than if you had never used it.",{"q":35591,"a":35674},"transform and opacity are handled by the compositor and do not trigger layout or paint. Properties like width, height, top, left, and margin trigger layout on the main thread and cannot be composited cheaply.",{"q":35676,"a":35677},"Should I add will-change in my base CSS rule?","Usually no. Prefer adding it just before the change, for example on hover or via a class, and removing it after. A permanent will-change in the base rule keeps a layer alive for the element's whole lifetime.","\u002Fcss-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fwill-change-and-the-compositor-thread",{"title":34882,"description":35663},"css-only-micro-interactions-animations\u002Fperformance-gpu-acceleration\u002Fwill-change-and-the-compositor-thread\u002Findex","EF9GKz9gKaMlvRPgqwPPqGREOrwnPLNw_BHYAWincvo",{"id":35683,"title":35684,"body":35685,"description":35767,"extension":1444,"meta":35768,"navigation":333,"path":3041,"seo":35769,"stem":35770,"__hash__":35771},"content\u002Findex.md","Modern CSS Layouts & Micro-Interactions",{"type":7,"value":35686,"toc":35760},[35687,35690,35693,35697,35700,35714,35718,35728,35732,35743,35745,35749],[10,35688,35684],{"id":35689},"modern-css-layouts-micro-interactions",[14,35691,35692],{},"A production-focused resource for modern CSS patterns, animations, and responsive architecture.",[50,35694,35696],{"id":35695},"why-this-site-exists","Why this site exists",[14,35698,35699],{},"This site helps frontend engineers, UI\u002FUX developers, and designers who code:",[1393,35701,35702,35705,35708,35711],{},[1396,35703,35704],{},"Master modern CSS specs, including Container Queries, Scroll-Driven Animations, and advanced layout APIs",[1396,35706,35707],{},"Build production-ready responsive components and micro-interactions with minimal JavaScript",[1396,35709,35710],{},"Implement performant, accessible, and spec-compliant interface patterns",[1396,35712,35713],{},"Debug cascade conflicts, layout shifts, and cross-browser gaps quickly",[50,35715,35717],{"id":35716},"start-with-these-guides","Start with these guides",[1393,35719,35720,35724],{},[1396,35721,35722],{},[27,35723,11296],{"href":5777},[1396,35725,35726],{},[27,35727,1481],{"href":1480},[50,35729,35731],{"id":35730},"editorial-principles","Editorial principles",[1393,35733,35734,35737,35740],{},[1396,35735,35736],{},"Spec accuracy and progressive enhancement over legacy hacks",[1396,35738,35739],{},"Performance and accessibility as first-class requirements",[1396,35741,35742],{},"Real-world patterns that are maintainable and copy-paste-friendly",[47,35744],{},[2757,35746,35748],{"id":35747},"who-this-is-for","Who this is for",[1393,35750,35751,35754,35757],{},[1396,35752,35753],{},"Frontend engineers and UI\u002FUX developers building modern component systems",[1396,35755,35756],{},"Designers who code and want architecture-level CSS guidance",[1396,35758,35759],{},"Full-stack developers shipping responsive interfaces in production",{"title":286,"searchDepth":330,"depth":330,"links":35761},[35762,35763,35764],{"id":35695,"depth":330,"text":35696},{"id":35716,"depth":330,"text":35717},{"id":35730,"depth":330,"text":35731,"children":35765},[35766],{"id":35747,"depth":337,"text":35748},"A production-focused resource for modern CSS patterns, responsive architecture, container queries, GPU animations, and accessibility-first micro-interactions.",{},{"title":35684,"description":35767},"index","vzyKfmLF3lZGz4kn_4nfR3kY9LlrL77yhdRztLNPzSs",{"id":35773,"title":35774,"body":35775,"description":36810,"extension":1444,"meta":36811,"navigation":333,"path":36822,"seo":36823,"stem":36824,"__hash__":36825},"content\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Ffeature-detection-with-supports\u002Findex.md","Feature Detection with @supports: Container Queries plus a Viewport Fallback",{"type":7,"value":35776,"toc":36800},[35777,35780,35784,35806,35810,35833,35844,35846,35902,36531,36535,36571,36575,36589,36649,36702,36704,36717,36719,36730,36742,36754,36767,36769,36797],[10,35778,35774],{"id":35779},"feature-detection-with-supports-container-queries-plus-a-viewport-fallback",[50,35781,35783],{"id":35782},"problem-statement","Problem statement",[14,35785,35786,35787,35789,35790,35792,35793,35795,35796,35798,35799,35803,35804,42],{},"You want to ship a component that uses container queries today but still renders a sensible layout in an engine that does not support them, without maintaining two separate stylesheets or shipping a heavy polyfill. The naive approach — writing ",[18,35788,12001],{}," rules and hoping older browsers ignore them — half works, because unsupported browsers do skip the ",[18,35791,12001],{}," block, but it leaves those users with only the unstyled baseline and no responsive behavior at all. The clean solution is to feature-detect with ",[18,35794,26207],{},", route supporting browsers down the container path, and give everyone else a viewport-based ",[18,35797,874],{}," fallback. This guide belongs to ",[27,35800,35802],{"href":35801},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002F","Container Query Fallbacks",", part of ",[27,35805,11296],{"href":5777},[50,35807,35809],{"id":35808},"approach-rationale","Approach rationale",[14,35811,35812,35814,35815,35817,35818,35820,35821,35823,35824,35826,35827,35829,35830,35832],{},[18,35813,2086],{}," evaluates a property\u002Fvalue pair and applies its block only when the browser considers that pair valid. Testing ",[18,35816,25409],{}," is the canonical probe because ",[18,35819,24401],{}," is the gateway property for size queries: any engine that understands it also understands ",[18,35822,12001],{},". The reason to detect the ",[86,35825,15063],{}," rather than the at-rule is that ",[18,35828,2086],{}," has no portable, reliably-implemented syntax for testing at-rule support across all the engines you care about, whereas the property test has worked since ",[18,35831,2086],{}," itself shipped.",[14,35834,35835,35836,35838,35839,21541,35841,35843],{},"The alternative strategies are worse for different reasons. A JavaScript polyfill that watches every container with ",[18,35837,25390],{}," adds main-thread work and a script dependency, and it can produce a visible reflow as classes are applied after first paint — a real accessibility problem for users on slow hardware who see content jump. Doing nothing and relying on the implicit skip leaves legacy users with a broken layout. The ",[18,35840,2086],{},[18,35842,874],{}," pairing keeps everything in CSS, costs nothing at runtime, and degrades to a perfectly usable viewport-driven layout. Its only tradeoff is that the fallback uses the viewport rather than the component's own width, so a component in a narrow sidebar on a wide screen falls back to its wide layout — usually acceptable, and the price of zero JavaScript.",[50,35845,4203],{"id":4202},[133,35847,140,35849,140,35852,140,35855,140,35858,140,35860,140,35862,140,35866,140,35868,140,35870,140,35872,140,35875,140,35877,140,35880,140,35882,140,35884,140,35887,140,35890,140,35892,140,35895,140,35899],{"viewBox":32013,"role":136,"ariaLabel":35848,"xmlns":138,"style":139},"A decision flow: test @supports container-type inline-size; if true take the modern container query path, if false take the @media viewport fallback path",[142,35850,35851],{},"Feature detection decision flow",[146,35853,35854],{},"Flow from an @supports test to either the modern container query path or the @media viewport fallback path.",[150,35856,35857],{"x":152,"y":153,"style":154},"Routing support detection to two layout paths",[171,35859],{"x":8947,"y":1831,"width":8947,"height":1788,"rx":836,"fill":177,"stroke":167,"strokeWidth":168,"opacity":566},[150,35861,2086],{"x":152,"y":6634,"style":22599},[150,35863,35865],{"x":152,"y":4196,"style":35864},"text-anchor:middle;fill:currentColor;font:11px ui-monospace,monospace;opacity:0.85","(container-type: inline-size)",[163,35867],{"x1":5397,"y1":6645,"x2":1822,"y2":1830,"stroke":167,"strokeWidth":168},[163,35869],{"x1":15019,"y1":6645,"x2":1812,"y2":1830,"stroke":167,"strokeWidth":168},[150,35871,5083],{"x":209,"y":5149,"style":6646},[150,35873,10852],{"x":35874,"y":5149,"style":6646},"498",[171,35876],{"x":1786,"y":4166,"width":538,"height":2630,"rx":836,"fill":72,"stroke":177,"strokeWidth":168},[150,35878,35879],{"x":1822,"y":2615,"style":2606},"Modern path",[150,35881,26247],{"x":1822,"y":2622,"style":35864},[171,35883],{"x":4194,"y":4166,"width":538,"height":2630,"rx":836,"fill":72,"stroke":167,"strokeWidth":168},[150,35885,35886],{"x":1812,"y":2615,"style":2606},"Viewport fallback",[150,35888,35889],{"x":1812,"y":2622,"style":35864},"@media (min-width)",[171,35891],{"x":8947,"y":220,"width":8947,"height":5139,"rx":836,"fill":177,"stroke":167,"strokeWidth":168,"opacity":178},[150,35893,35894],{"x":152,"y":19657,"style":22565},"shared mobile-first baseline",[163,35896],{"x1":1822,"y1":2633,"x2":5397,"y2":35897,"stroke":167,"strokeWidth":168,"strokeDashArray":35898},"276",[249,249],[163,35900],{"x1":1812,"y1":2633,"x2":15019,"y2":35897,"stroke":167,"strokeWidth":168,"strokeDashArray":35901},[249,249],[281,35903,35905],{"className":283,"code":35904,"language":285,"meta":286,"style":286},"\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\u003Cstyle>\n  \u002F* 1. Shared mobile-first baseline. EVERY browser gets this. *\u002F\n  .panel {\n    display: grid;\n    grid-template-columns: 1fr; \u002F* stacked by default *\u002F\n    gap: 1rem;\n    padding: 1rem;\n    background: #11141c;\n    color: #e8ecf4;\n    border-radius: 12px;\n  }\n\n  \u002F* 2. Viewport fallback for engines WITHOUT container queries.            *\u002F\n  \u002F*    Comes first so supporting browsers can override it on equal weight. *\u002F\n  @media (min-width: 720px) {\n    .panel {\n      grid-template-columns: 200px 1fr; \u002F* media beside body on wide viewports *\u002F\n      align-items: start;\n    }\n  }\n\n  \u002F* 3. Modern path, gated by feature detection. Skipped entirely by        *\u002F\n  \u002F*    browsers that do not understand container-type: inline-size.        *\u002F\n  @supports (container-type: inline-size) {\n    .panel-wrap {\n      container: panel \u002F inline-size;\n    }\n\n    \u002F* Reset the viewport fallback so it cannot fight the container query.   *\u002F\n    .panel {\n      grid-template-columns: 1fr;\n    }\n\n    \u002F* Now react to the COMPONENT's width, not the viewport's. *\u002F\n    @container panel (min-width: 460px) {\n      .panel {\n        grid-template-columns: 200px 1fr;\n        align-items: start;\n      }\n    }\n  }\n\n  .panel img {\n    width: 100%;\n    border-radius: 8px;\n    aspect-ratio: 4 \u002F 3;\n    object-fit: cover;\n  }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv class=\"panel-wrap\">\n    \u003Csection class=\"panel\">\n      \u003Cimg src=\"https:\u002F\u002Fplacehold.co\u002F200x150\" alt=\"Illustration\">\n      \u003Cdiv>\n        \u003Ch2>Progressive panel\u003C\u002Fh2>\n        \u003Cp>Container-query browsers lay this out by the panel's own width; older\n           browsers fall back to a viewport breakpoint. Both render usefully.\u003C\u002Fp>\n      \u003C\u002Fdiv>\n    \u003C\u002Fsection>\n  \u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,35906,35907,35918,35932,35940,35954,35978,35986,35991,35997,36007,36023,36036,36049,36060,36072,36084,36088,36092,36097,36102,36119,36126,36146,36157,36161,36165,36169,36174,36179,36190,36197,36205,36209,36213,36218,36224,36236,36240,36244,36249,36257,36264,36281,36292,36297,36301,36305,36309,36318,36330,36342,36357,36369,36373,36381,36389,36397,36412,36426,36449,36457,36471,36480,36489,36498,36507,36515,36523],{"__ignoreMap":286},[290,35908,35909,35911,35914,35916],{"class":163,"line":292},[290,35910,8982],{"class":295},[290,35912,35913],{"class":299},"doctype",[290,35915,8988],{"class":303},[290,35917,327],{"class":295},[290,35919,35920,35922,35924,35926,35928,35930],{"class":163,"line":330},[290,35921,296],{"class":295},[290,35923,285],{"class":299},[290,35925,8999],{"class":303},[290,35927,307],{"class":295},[290,35929,9004],{"class":310},[290,35931,327],{"class":295},[290,35933,35934,35936,35938],{"class":163,"line":337},[290,35935,296],{"class":295},[290,35937,9013],{"class":299},[290,35939,327],{"class":295},[290,35941,35942,35944,35946,35948,35950,35952],{"class":163,"line":364},[290,35943,296],{"class":295},[290,35945,9022],{"class":299},[290,35947,9025],{"class":303},[290,35949,307],{"class":295},[290,35951,9030],{"class":310},[290,35953,327],{"class":295},[290,35955,35956,35958,35960,35963,35965,35968,35971,35973,35976],{"class":163,"line":386},[290,35957,296],{"class":295},[290,35959,9022],{"class":299},[290,35961,35962],{"class":303}," name",[290,35964,307],{"class":295},[290,35966,35967],{"class":310},"\"viewport\"",[290,35969,35970],{"class":303}," content",[290,35972,307],{"class":295},[290,35974,35975],{"class":310},"\"width=device-width, initial-scale=1\"",[290,35977,327],{"class":295},[290,35979,35980,35982,35984],{"class":163,"line":408},[290,35981,296],{"class":295},[290,35983,1430],{"class":299},[290,35985,327],{"class":295},[290,35987,35988],{"class":163,"line":428},[290,35989,35990],{"class":455},"  \u002F* 1. Shared mobile-first baseline. EVERY browser gets this. *\u002F\n",[290,35992,35993,35995],{"class":163,"line":517},[290,35994,6431],{"class":303},[290,35996,450],{"class":295},[290,35998,35999,36001,36003,36005],{"class":163,"line":523},[290,36000,34590],{"class":461},[290,36002,465],{"class":295},[290,36004,9147],{"class":461},[290,36006,471],{"class":295},[290,36008,36009,36012,36014,36016,36018,36020],{"class":163,"line":532},[290,36010,36011],{"class":461},"    grid-template-columns",[290,36013,465],{"class":295},[290,36015,468],{"class":461},[290,36017,11964],{"class":541},[290,36019,828],{"class":295},[290,36021,36022],{"class":455},"\u002F* stacked by default *\u002F\n",[290,36024,36025,36028,36030,36032,36034],{"class":163,"line":551},[290,36026,36027],{"class":461},"    gap",[290,36029,465],{"class":295},[290,36031,468],{"class":461},[290,36033,801],{"class":541},[290,36035,471],{"class":295},[290,36037,36038,36041,36043,36045,36047],{"class":163,"line":586},[290,36039,36040],{"class":461},"    padding",[290,36042,465],{"class":295},[290,36044,468],{"class":461},[290,36046,801],{"class":541},[290,36048,471],{"class":295},[290,36050,36051,36053,36055,36058],{"class":163,"line":602},[290,36052,9124],{"class":461},[290,36054,465],{"class":295},[290,36056,36057],{"class":461},"#11141c",[290,36059,471],{"class":295},[290,36061,36062,36065,36067,36070],{"class":163,"line":617},[290,36063,36064],{"class":461},"    color",[290,36066,465],{"class":295},[290,36068,36069],{"class":461},"#e8ecf4",[290,36071,471],{"class":295},[290,36073,36074,36076,36078,36080,36082],{"class":163,"line":623},[290,36075,12759],{"class":461},[290,36077,465],{"class":295},[290,36079,5894],{"class":461},[290,36081,674],{"class":541},[290,36083,471],{"class":295},[290,36085,36086],{"class":163,"line":628},[290,36087,771],{"class":295},[290,36089,36090],{"class":163,"line":634},[290,36091,334],{"emptyLinePlaceholder":333},[290,36093,36094],{"class":163,"line":649},[290,36095,36096],{"class":455},"  \u002F* 2. Viewport fallback for engines WITHOUT container queries.            *\u002F\n",[290,36098,36099],{"class":163,"line":660},[290,36100,36101],{"class":455},"  \u002F*    Comes first so supporting browsers can override it on equal weight. *\u002F\n",[290,36103,36104,36106,36108,36110,36112,36115,36117],{"class":163,"line":688},[290,36105,26109],{"class":541},[290,36107,3595],{"class":295},[290,36109,26114],{"class":461},[290,36111,465],{"class":295},[290,36113,36114],{"class":461},"720",[290,36116,674],{"class":541},[290,36118,646],{"class":295},[290,36120,36121,36124],{"class":163,"line":693},[290,36122,36123],{"class":303},"    .panel",[290,36125,450],{"class":295},[290,36127,36128,36131,36133,36135,36137,36139,36141,36143],{"class":163,"line":698},[290,36129,36130],{"class":461},"      grid-template-columns",[290,36132,465],{"class":295},[290,36134,174],{"class":461},[290,36136,674],{"class":541},[290,36138,804],{"class":461},[290,36140,11964],{"class":541},[290,36142,828],{"class":295},[290,36144,36145],{"class":455},"\u002F* media beside body on wide viewports *\u002F\n",[290,36147,36148,36151,36153,36155],{"class":163,"line":704},[290,36149,36150],{"class":461},"      align-items",[290,36152,465],{"class":295},[290,36154,7482],{"class":461},[290,36156,471],{"class":295},[290,36158,36159],{"class":163,"line":710},[290,36160,8200],{"class":295},[290,36162,36163],{"class":163,"line":717},[290,36164,771],{"class":295},[290,36166,36167],{"class":163,"line":730},[290,36168,334],{"emptyLinePlaceholder":333},[290,36170,36171],{"class":163,"line":742},[290,36172,36173],{"class":455},"  \u002F* 3. Modern path, gated by feature detection. Skipped entirely by        *\u002F\n",[290,36175,36176],{"class":163,"line":768},[290,36177,36178],{"class":455},"  \u002F*    browsers that do not understand container-type: inline-size.        *\u002F\n",[290,36180,36181,36184,36186,36188],{"class":163,"line":774},[290,36182,36183],{"class":541},"  @supports",[290,36185,3595],{"class":295},[290,36187,24401],{"class":461},[290,36189,26104],{"class":295},[290,36191,36192,36195],{"class":163,"line":779},[290,36193,36194],{"class":303},"    .panel-wrap",[290,36196,450],{"class":295},[290,36198,36199,36202],{"class":163,"line":784},[290,36200,36201],{"class":461},"      container",[290,36203,36204],{"class":295},": panel \u002F inline-size;\n",[290,36206,36207],{"class":163,"line":812},[290,36208,8200],{"class":295},[290,36210,36211],{"class":163,"line":860},[290,36212,334],{"emptyLinePlaceholder":333},[290,36214,36215],{"class":163,"line":865},[290,36216,36217],{"class":455},"    \u002F* Reset the viewport fallback so it cannot fight the container query.   *\u002F\n",[290,36219,36220,36222],{"class":163,"line":871},[290,36221,36123],{"class":303},[290,36223,450],{"class":295},[290,36225,36226,36228,36230,36232,36234],{"class":163,"line":880},[290,36227,36130],{"class":461},[290,36229,465],{"class":295},[290,36231,468],{"class":461},[290,36233,11964],{"class":541},[290,36235,471],{"class":295},[290,36237,36238],{"class":163,"line":896},[290,36239,8200],{"class":295},[290,36241,36242],{"class":163,"line":4734},[290,36243,334],{"emptyLinePlaceholder":333},[290,36245,36246],{"class":163,"line":4742},[290,36247,36248],{"class":455},"    \u002F* Now react to the COMPONENT's width, not the viewport's. *\u002F\n",[290,36250,36251,36254],{"class":163,"line":4761},[290,36252,36253],{"class":541},"    @container",[290,36255,36256],{"class":295}," panel (min-width: 460px) {\n",[290,36258,36259,36262],{"class":163,"line":4766},[290,36260,36261],{"class":303},"      .panel",[290,36263,450],{"class":295},[290,36265,36266,36269,36271,36273,36275,36277,36279],{"class":163,"line":4786},[290,36267,36268],{"class":461},"        grid-template-columns",[290,36270,465],{"class":295},[290,36272,174],{"class":461},[290,36274,674],{"class":541},[290,36276,804],{"class":461},[290,36278,11964],{"class":541},[290,36280,471],{"class":295},[290,36282,36283,36286,36288,36290],{"class":163,"line":9635},[290,36284,36285],{"class":461},"        align-items",[290,36287,465],{"class":295},[290,36289,7482],{"class":461},[290,36291,471],{"class":295},[290,36293,36294],{"class":163,"line":9641},[290,36295,36296],{"class":295},"      }\n",[290,36298,36299],{"class":163,"line":9647},[290,36300,8200],{"class":295},[290,36302,36303],{"class":163,"line":9665},[290,36304,771],{"class":295},[290,36306,36307],{"class":163,"line":9683},[290,36308,334],{"emptyLinePlaceholder":333},[290,36310,36311,36313,36316],{"class":163,"line":9688},[290,36312,6431],{"class":303},[290,36314,36315],{"class":299}," img",[290,36317,450],{"class":295},[290,36319,36320,36322,36324,36326,36328],{"class":163,"line":9694},[290,36321,9090],{"class":461},[290,36323,465],{"class":295},[290,36325,165],{"class":461},[290,36327,11018],{"class":541},[290,36329,471],{"class":295},[290,36331,36332,36334,36336,36338,36340],{"class":163,"line":9700},[290,36333,12759],{"class":461},[290,36335,465],{"class":295},[290,36337,176],{"class":461},[290,36339,674],{"class":541},[290,36341,471],{"class":295},[290,36343,36344,36347,36349,36351,36353,36355],{"class":163,"line":9710},[290,36345,36346],{"class":461},"    aspect-ratio",[290,36348,465],{"class":295},[290,36350,249],{"class":461},[290,36352,1203],{"class":295},[290,36354,1579],{"class":461},[290,36356,471],{"class":295},[290,36358,36359,36362,36364,36367],{"class":163,"line":9716},[290,36360,36361],{"class":461},"    object-fit",[290,36363,465],{"class":295},[290,36365,36366],{"class":461},"cover",[290,36368,471],{"class":295},[290,36370,36371],{"class":163,"line":9738},[290,36372,771],{"class":295},[290,36374,36375,36377,36379],{"class":163,"line":9748},[290,36376,431],{"class":295},[290,36378,1430],{"class":299},[290,36380,327],{"class":295},[290,36382,36383,36385,36387],{"class":163,"line":9754},[290,36384,431],{"class":295},[290,36386,9013],{"class":299},[290,36388,327],{"class":295},[290,36390,36391,36393,36395],{"class":163,"line":9760},[290,36392,296],{"class":295},[290,36394,9239],{"class":299},[290,36396,327],{"class":295},[290,36398,36399,36401,36403,36405,36407,36410],{"class":163,"line":9777},[290,36400,367],{"class":295},[290,36402,342],{"class":299},[290,36404,314],{"class":303},[290,36406,307],{"class":295},[290,36408,36409],{"class":310},"\"panel-wrap\"",[290,36411,327],{"class":295},[290,36413,36414,36416,36418,36420,36422,36424],{"class":163,"line":9787},[290,36415,4290],{"class":295},[290,36417,5944],{"class":299},[290,36419,314],{"class":303},[290,36421,307],{"class":295},[290,36423,13864],{"class":310},[290,36425,327],{"class":295},[290,36427,36428,36431,36433,36435,36437,36440,36442,36444,36447],{"class":163,"line":9793},[290,36429,36430],{"class":295},"      \u003C",[290,36432,136],{"class":299},[290,36434,4296],{"class":303},[290,36436,307],{"class":295},[290,36438,36439],{"class":310},"\"https:\u002F\u002Fplacehold.co\u002F200x150\"",[290,36441,4341],{"class":303},[290,36443,307],{"class":295},[290,36445,36446],{"class":310},"\"Illustration\"",[290,36448,327],{"class":295},[290,36450,36451,36453,36455],{"class":163,"line":9799},[290,36452,36430],{"class":295},[290,36454,342],{"class":299},[290,36456,327],{"class":295},[290,36458,36459,36462,36464,36467,36469],{"class":163,"line":9805},[290,36460,36461],{"class":295},"        \u003C",[290,36463,50],{"class":299},[290,36465,36466],{"class":295},">Progressive panel\u003C\u002F",[290,36468,50],{"class":299},[290,36470,327],{"class":295},[290,36472,36473,36475,36477],{"class":163,"line":9811},[290,36474,36461],{"class":295},[290,36476,14],{"class":299},[290,36478,36479],{"class":295},">Container-query browsers lay this out by the panel's own width; older\n",[290,36481,36482,36485,36487],{"class":163,"line":9820},[290,36483,36484],{"class":295},"           browsers fall back to a viewport breakpoint. Both render usefully.\u003C\u002F",[290,36486,14],{"class":299},[290,36488,327],{"class":295},[290,36490,36491,36494,36496],{"class":163,"line":9829},[290,36492,36493],{"class":295},"      \u003C\u002F",[290,36495,342],{"class":299},[290,36497,327],{"class":295},[290,36499,36500,36503,36505],{"class":163,"line":27197},[290,36501,36502],{"class":295},"    \u003C\u002F",[290,36504,5944],{"class":299},[290,36506,327],{"class":295},[290,36508,36509,36511,36513],{"class":163,"line":27202},[290,36510,4315],{"class":295},[290,36512,342],{"class":299},[290,36514,327],{"class":295},[290,36516,36517,36519,36521],{"class":163,"line":27209},[290,36518,431],{"class":295},[290,36520,9239],{"class":299},[290,36522,327],{"class":295},[290,36524,36525,36527,36529],{"class":163,"line":27220},[290,36526,431],{"class":295},[290,36528,285],{"class":299},[290,36530,327],{"class":295},[50,36532,36534],{"id":36533},"key-technique-callout","Key technique callout",[14,36536,36537,36538,36540,36541,36544,36545,36547,36548,36550,36551,36554,36555,36557,36558,36560,36561,36564,36565,36567,36568,36570],{},"The pivotal detail is the ordering and the reset inside the ",[18,36539,2086],{}," block. A browser that supports container queries reads ",[86,36542,36543],{},"all three"," layers: it applies the baseline, then the ",[18,36546,874],{}," fallback (because that media query may still match the viewport), and finally the ",[18,36549,2086],{}," block — which must explicitly set ",[18,36552,36553],{},"grid-template-columns: 1fr"," again to undo the ",[18,36556,874],{}," rule before the ",[18,36559,12001],{}," query takes over. Without that reset, on a wide screen the ",[18,36562,36563],{},"@media (min-width: 720px)"," rule would already have applied the two-column layout, and a narrow container's ",[18,36566,12001],{}," rule could not pull it back, because the two rules have equal specificity and the media one is sometimes the later match depending on source order. By resetting to the stacked baseline at the top of the ",[18,36569,2086],{}," block, you guarantee the container query is the sole authority on layout for supporting browsers, while unsupported browsers — which never enter the block — keep the viewport behavior untouched.",[50,36572,36574],{"id":36573},"variation-or-extension","Variation or extension",[14,36576,36577,36578,36581,36582,36584,36585,36588],{},"You can probe a polyfill load in JavaScript with the matching ",[18,36579,36580],{},"CSS.supports()"," call, and pair the whole pattern with ",[18,36583,2584],{}," so any transition introduced by the layout swap is suppressed for users who ask for less motion. This connects the responsive layer to the ",[27,36586,36587],{"href":1475},"accessibility motion guidance"," for animations.",[281,36590,36592],{"className":438,"code":36591,"language":440,"meta":286,"style":286},"\u002F* Animate the layout swap, but only when the user has not requested reduced motion *\u002F\n@supports (container-type: inline-size) {\n  @media (prefers-reduced-motion: no-preference) {\n    .panel {\n      transition: grid-template-columns 0.2s ease;\n    }\n  }\n}\n",[18,36593,36594,36599,36609,36615,36621,36637,36641,36645],{"__ignoreMap":286},[290,36595,36596],{"class":163,"line":292},[290,36597,36598],{"class":455},"\u002F* Animate the layout swap, but only when the user has not requested reduced motion *\u002F\n",[290,36600,36601,36603,36605,36607],{"class":163,"line":330},[290,36602,2086],{"class":541},[290,36604,3595],{"class":295},[290,36606,24401],{"class":461},[290,36608,26104],{"class":295},[290,36610,36611,36613],{"class":163,"line":337},[290,36612,26109],{"class":541},[290,36614,16018],{"class":295},[290,36616,36617,36619],{"class":163,"line":364},[290,36618,36123],{"class":303},[290,36620,450],{"class":295},[290,36622,36623,36626,36629,36631,36633,36635],{"class":163,"line":386},[290,36624,36625],{"class":461},"      transition",[290,36627,36628],{"class":295},": grid-template-columns ",[290,36630,566],{"class":461},[290,36632,1886],{"class":541},[290,36634,545],{"class":461},[290,36636,471],{"class":295},[290,36638,36639],{"class":163,"line":408},[290,36640,8200],{"class":295},[290,36642,36643],{"class":163,"line":428},[290,36644,771],{"class":295},[290,36646,36647],{"class":163,"line":517},[290,36648,620],{"class":295},[281,36650,36652],{"className":2904,"code":36651,"language":2906,"meta":286,"style":286},"\u002F\u002F Conditionally load a polyfill only where support is missing.\nif (!CSS.supports('container-type', 'inline-size')) {\n  import('https:\u002F\u002Fcdn.example.com\u002Fcontainer-query-polyfill.modern.js');\n}\n",[18,36653,36654,36659,36686,36698],{"__ignoreMap":286},[290,36655,36656],{"class":163,"line":292},[290,36657,36658],{"class":455},"\u002F\u002F Conditionally load a polyfill only where support is missing.\n",[290,36660,36661,36663,36665,36667,36669,36671,36674,36676,36679,36681,36684],{"class":163,"line":330},[290,36662,2913],{"class":541},[290,36664,3595],{"class":295},[290,36666,8006],{"class":541},[290,36668,6627],{"class":461},[290,36670,42],{"class":295},[290,36672,36673],{"class":303},"supports",[290,36675,484],{"class":295},[290,36677,36678],{"class":310},"'container-type'",[290,36680,569],{"class":295},[290,36682,36683],{"class":310},"'inline-size'",[290,36685,14796],{"class":295},[290,36687,36688,36691,36693,36696],{"class":163,"line":337},[290,36689,36690],{"class":541},"  import",[290,36692,484],{"class":295},[290,36694,36695],{"class":310},"'https:\u002F\u002Fcdn.example.com\u002Fcontainer-query-polyfill.modern.js'",[290,36697,500],{"class":295},[290,36699,36700],{"class":163,"line":364},[290,36701,620],{"class":295},[50,36703,1299],{"id":1298},[14,36705,36706,36708,36709,36711,36712,36714,36715,42],{},[18,36707,2086],{}," itself is universally available (Chrome and Edge 28+, Safari 9+, Firefox 22+), so the detection wrapper carries no risk. Size container queries — the feature being detected — are baseline in Chrome and Edge 105+, Safari 16.0+, and Firefox 110+. The ",[18,36710,36580],{}," JavaScript API has matching reach. Because every browser that lacks container queries skips the ",[18,36713,2086],{}," block automatically, this pattern needs no further guard, which is why it pairs naturally with the broader strategies in the guide to ",[27,36716,26189],{"href":26188},[50,36718,1316],{"id":1315},[14,36720,36721,36724,36726,36727,36729],{},[62,36722,36723],{},"Why test container-type instead of @container directly?",[18,36725,2086],{}," cannot test an at-rule's existence reliably across engines, but it can test whether a property and value pair is valid. ",[18,36728,26207],{}," is the canonical, widely supported probe for size container query support.",[14,36731,36732,36735,36736,36738,36739,36741],{},[62,36733,36734],{},"Will browsers that lack container queries apply rules inside @supports?","\nNo. A browser that does not recognise ",[18,36737,25409],{}," as valid evaluates the ",[18,36740,2086],{}," condition as false and skips the entire block, so the modern path is invisible to it and your fallback styles take over.",[14,36743,36744,36747,36748,36750,36751,36753],{},[62,36745,36746],{},"Should the viewport fallback come before or after the @supports block?","\nPut the mobile-first baseline and the ",[18,36749,874],{}," viewport fallback first, then the ",[18,36752,2086],{}," block last. Supporting browsers read both but the later container rules win on equal specificity; unsupported browsers only ever see the baseline and media query.",[14,36755,36756,36759,36760,36763,36764,36766],{},[62,36757,36758],{},"Can I detect container query support in JavaScript?","\nYes, with ",[18,36761,36762],{},"CSS.supports('container-type', 'inline-size')",", which returns a boolean. This is useful for conditionally loading a polyfill, but for styling alone the CSS ",[18,36765,2086],{}," rule is sufficient and avoids a flash of unstyled layout.",[50,36768,1391],{"id":1390},[1393,36770,36771,36776,36781,36786,36792],{},[1396,36772,36773,36775],{},[27,36774,35802],{"href":35801}," — the parent guide to graceful degradation strategies.",[1396,36777,36778,36780],{},[27,36779,26296],{"href":26188}," — broader degradation patterns beyond detection.",[1396,36782,36783,36785],{},[27,36784,26284],{"href":25340}," — the syntax the modern path relies on.",[1396,36787,36788,36791],{},[27,36789,36790],{"href":11248},"Container Query Units: cqi & cqb Explained"," — units that also need a static fallback value.",[1396,36793,36794,36796],{},[27,36795,1476],{"href":1475}," — gate any layout-swap transition behind reduced-motion.",[1430,36798,36799],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":286,"searchDepth":330,"depth":330,"links":36801},[36802,36803,36804,36805,36806,36807,36808,36809],{"id":35782,"depth":330,"text":35783},{"id":35808,"depth":330,"text":35809},{"id":4202,"depth":330,"text":4203},{"id":36533,"depth":330,"text":36534},{"id":36573,"depth":330,"text":36574},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Detect container query support with @supports (container-type: inline-size), layer a viewport fallback underneath, and ship one component that degrades cleanly.",{"seoTitle":36812,"datePublished":1447,"dateModified":1447,"faq":36813},"Feature Detection: @supports for Containers",[36814,36816,36818,36820],{"q":36723,"a":36815},"@supports cannot test an at-rule's existence reliably across engines, but it can test whether a property and value pair is valid. @supports (container-type: inline-size) is the canonical, widely supported probe for size container query support.",{"q":36734,"a":36817},"No. A browser that does not recognise container-type: inline-size as valid evaluates the @supports condition as false and skips the entire block, so the modern path is invisible to it and your fallback styles take over.",{"q":36746,"a":36819},"Put the mobile-first baseline and the @media viewport fallback first, then the @supports block last. Supporting browsers read both but the later container rules win on equal specificity; unsupported browsers only ever see the baseline and media query.",{"q":36758,"a":36821},"Yes, with CSS.supports('container-type', 'inline-size'), which returns a boolean. This is useful for conditionally loading a polyfill, but for styling alone the CSS @supports rule is sufficient and avoids a flash of unstyled layout.","\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Ffeature-detection-with-supports",{"title":35774,"description":36810},"mastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Ffeature-detection-with-supports\u002Findex","2fXt5ruu6ZZltT-acKcUKFQCz_UTLIpCbSaWLftaZUQ",{"id":36827,"title":36828,"body":36829,"description":37816,"extension":1444,"meta":37817,"navigation":333,"path":37830,"seo":37831,"stem":37832,"__hash__":37833},"content\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fhandling-container-query-fallbacks-for-older-browsers\u002Findex.md","Handling container query fallbacks for older browsers",{"type":7,"value":36830,"toc":37805},[36831,36834,36852,36855,36875,36880,36927,36931,36941,37088,37093,37116,37120,37126,37295,37300,37324,37328,37335,37448,37453,37489,37493,37496,37552,37554,37647,37651,37726,37728,37737,37749,37761,37776,37778,37803],[10,36832,36828],{"id":36833},"handling-container-query-fallbacks-for-older-browsers",[14,36835,36836,36837,36840,36841,36843,36844,36847,36848,36851],{},"Modern component architecture relies heavily on element-relative responsiveness, but ",[18,36838,36839],{},"legacy browser container support"," remains fragmented across enterprise and mobile environments. This page extends the ",[27,36842,35802],{"href":35801}," guide with ",[18,36845,36846],{},"CSS container query graceful degradation"," strategies that prevent cumulative layout shift (CLS) while maintaining modern layout performance. The runtime branching here pairs naturally with ",[27,36849,36850],{"href":19560},"feature detection using @supports","; by combining native detection, viewport-based mapping, and conditional script loading, you can ship resilient components without sacrificing progressive enhancement.",[14,36853,36854],{},"Key implementation priorities:",[1393,36856,36857,36863,36866,36869,36872],{},[1396,36858,36859,36860],{},"Detect support using ",[18,36861,36862],{},"@supports container detection",[1396,36864,36865],{},"Isolate modern rules to prevent cascade pollution",[1396,36867,36868],{},"Synchronize breakpoints via CSS custom properties",[1396,36870,36871],{},"Defer polyfill execution to protect LCP and TTI",[1396,36873,36874],{},"Validate fallback alignment across resize and orientation events",[14,36876,36877,36878,42],{},"For foundational architecture patterns, reference our core documentation on ",[27,36879,11296],{"href":5777},[133,36881,140,36883,140,36886,140,36889,140,36892,140,36894,140,36897,140,36899,140,36901,140,36904,140,36908,140,36910,140,36913,140,36915,140,36918,140,36920,140,36922,140,36924],{"viewBox":4133,"role":136,"ariaLabel":36882,"xmlns":138,"style":139},"Decision flow from feature detection to modern container query or viewport fallback",[142,36884,36885],{},"Container query fallback decision flow",[146,36887,36888],{},"Feature detection branches a request into the native container-query path or a viewport media-query fallback path.",[150,36890,36891],{"x":152,"y":2598,"style":1781},"Fallback decision path",[171,36893],{"x":2601,"y":5900,"width":2602,"height":175,"rx":176,"fill":177,"opacity":566,"stroke":167,"strokeWidth":468},[150,36895,36896],{"x":152,"y":9105,"style":19647},"@supports (container-type)",[163,36898],{"x1":2601,"y1":18870,"x2":1822,"y2":1787,"stroke":167,"strokeWidth":168},[163,36900],{"x1":8897,"y1":18870,"x2":1812,"y2":1787,"stroke":167,"strokeWidth":168},[150,36902,36903],{"x":2622,"y":25453,"style":5915},"supported",[150,36905,36907],{"x":36906,"y":25453,"style":5915},"512","not supported",[171,36909],{"x":4146,"y":1787,"width":12480,"height":5139,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":14604},[150,36911,36912],{"x":1822,"y":18866,"style":1794},"native @container query",[171,36914],{"x":15019,"y":1787,"width":12480,"height":5139,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":14604},[150,36916,36917],{"x":1812,"y":18866,"style":1794},"@media viewport fallback",[163,36919],{"x1":1822,"y1":32987,"x2":152,"y2":8947,"stroke":167,"strokeWidth":168},[163,36921],{"x1":1812,"y1":32987,"x2":152,"y2":8947,"stroke":167,"strokeWidth":168},[171,36923],{"x":2601,"y":8947,"width":2602,"height":8879,"rx":176,"fill":177,"opacity":178},[150,36925,36926],{"x":152,"y":229,"style":1794},"stable layout, no CLS",[50,36928,36930],{"id":36929},"feature-detection-progressive-enhancement-strategy","Feature Detection & Progressive Enhancement Strategy",[14,36932,36933,36934,36936,36937,36940],{},"Establish a reliable detection layer to branch CSS execution between modern and legacy rendering engines. Wrapping modern syntax in an ",[18,36935,2086],{}," block ensures older browsers ignore unsupported rules while applying baseline viewport fallbacks. This approach eliminates the need for heavy ",[18,36938,36939],{},"@container fallback CSS"," hacks and keeps the cascade predictable.",[281,36942,36944],{"className":438,"code":36943,"language":440,"meta":286,"style":286},"\u002F* Baseline fallback: applies to all browsers *\u002F\n.card {\n  width: 100%;\n  max-width: 600px;\n  display: block;\n}\n\n\u002F* Modern container query implementation *\u002F\n@supports (container-type: inline-size) {\n  .card-container {\n    container-type: inline-size;\n  }\n\n  @container (min-width: 400px) {\n    .card {\n      display: grid;\n      grid-template-columns: 1fr 2fr;\n    }\n  }\n}\n",[18,36945,36946,36951,36957,36969,36981,36991,36995,36999,37004,37014,37021,37028,37032,37036,37043,37049,37060,37076,37080,37084],{"__ignoreMap":286},[290,36947,36948],{"class":163,"line":292},[290,36949,36950],{"class":455},"\u002F* Baseline fallback: applies to all browsers *\u002F\n",[290,36952,36953,36955],{"class":163,"line":330},[290,36954,11528],{"class":303},[290,36956,450],{"class":295},[290,36958,36959,36961,36963,36965,36967],{"class":163,"line":337},[290,36960,17904],{"class":461},[290,36962,465],{"class":295},[290,36964,165],{"class":461},[290,36966,11018],{"class":541},[290,36968,471],{"class":295},[290,36970,36971,36973,36975,36977,36979],{"class":163,"line":364},[290,36972,17916],{"class":461},[290,36974,465],{"class":295},[290,36976,4176],{"class":461},[290,36978,674],{"class":541},[290,36980,471],{"class":295},[290,36982,36983,36985,36987,36989],{"class":163,"line":386},[290,36984,17742],{"class":461},[290,36986,465],{"class":295},[290,36988,68],{"class":461},[290,36990,471],{"class":295},[290,36992,36993],{"class":163,"line":408},[290,36994,620],{"class":295},[290,36996,36997],{"class":163,"line":428},[290,36998,334],{"emptyLinePlaceholder":333},[290,37000,37001],{"class":163,"line":517},[290,37002,37003],{"class":455},"\u002F* Modern container query implementation *\u002F\n",[290,37005,37006,37008,37010,37012],{"class":163,"line":523},[290,37007,2086],{"class":541},[290,37009,3595],{"class":295},[290,37011,24401],{"class":461},[290,37013,26104],{"class":295},[290,37015,37016,37019],{"class":163,"line":532},[290,37017,37018],{"class":303},"  .card-container",[290,37020,450],{"class":295},[290,37022,37023,37026],{"class":163,"line":551},[290,37024,37025],{"class":461},"    container-type",[290,37027,25565],{"class":295},[290,37029,37030],{"class":163,"line":586},[290,37031,771],{"class":295},[290,37033,37034],{"class":163,"line":602},[290,37035,334],{"emptyLinePlaceholder":333},[290,37037,37038,37041],{"class":163,"line":617},[290,37039,37040],{"class":541},"  @container",[290,37042,12004],{"class":295},[290,37044,37045,37047],{"class":163,"line":623},[290,37046,26128],{"class":303},[290,37048,450],{"class":295},[290,37050,37051,37054,37056,37058],{"class":163,"line":628},[290,37052,37053],{"class":461},"      display",[290,37055,465],{"class":295},[290,37057,9147],{"class":461},[290,37059,471],{"class":295},[290,37061,37062,37064,37066,37068,37070,37072,37074],{"class":163,"line":634},[290,37063,36130],{"class":461},[290,37065,465],{"class":295},[290,37067,468],{"class":461},[290,37069,11964],{"class":541},[290,37071,3290],{"class":461},[290,37073,11964],{"class":541},[290,37075,471],{"class":295},[290,37077,37078],{"class":163,"line":649},[290,37079,8200],{"class":295},[290,37081,37082],{"class":163,"line":660},[290,37083,771],{"class":295},[290,37085,37086],{"class":163,"line":688},[290,37087,620],{"class":295},[14,37089,37090],{},[62,37091,37092],{},"Implementation rules:",[3017,37094,37095,37100,37105,37111],{},[1396,37096,37097,37098,42],{},"Always declare baseline styles outside ",[18,37099,2086],{},[1396,37101,1499,37102,37104],{},[18,37103,25409],{}," as the detection target; it covers 95% of layout use cases.",[1396,37106,37107,37108,37110],{},"Avoid nesting ",[18,37109,12001],{}," inside other conditional blocks unless explicitly required for scoped theming.",[1396,37112,37113,37114,42],{},"For deeper context on progressive enhancement patterns, review our core documentation on ",[27,37115,35802],{"href":35801},[50,37117,37119],{"id":37118},"media-query-fallback-mapping-synchronization","Media Query Fallback Mapping & Synchronization",[14,37121,37122,37123,37125],{},"Translating container-relative breakpoints into viewport-relative media queries requires precise dimensional accounting. Parent container padding, margins, and grid gutters must be subtracted from the container's ",[18,37124,26044],{}," threshold to align viewport breakpoints accurately.",[281,37127,37129],{"className":438,"code":37128,"language":440,"meta":286,"style":286},":root {\n  --breakpoint-sm: 400px;\n}\n\n\u002F* Viewport fallback: accounts for typical 24px container padding *\u002F\n@media (min-width: calc(var(--breakpoint-sm) + 48px)) {\n  .card {\n    display: grid;\n    grid-template-columns: 1fr 2fr;\n  }\n}\n\n@supports (container-type: inline-size) {\n  .card-container {\n    container-type: inline-size;\n  }\n  @container (min-width: var(--breakpoint-sm)) {\n    .card {\n      display: grid;\n      grid-template-columns: 1fr 2fr;\n    }\n  }\n}\n",[18,37130,37131,37137,37150,37154,37158,37163,37174,37180,37190,37206,37210,37214,37218,37228,37234,37240,37244,37251,37257,37267,37283,37287,37291],{"__ignoreMap":286},[290,37132,37133,37135],{"class":163,"line":292},[290,37134,1554],{"class":303},[290,37136,450],{"class":295},[290,37138,37139,37142,37144,37146,37148],{"class":163,"line":330},[290,37140,37141],{"class":1561},"  --breakpoint-sm",[290,37143,465],{"class":295},[290,37145,2618],{"class":461},[290,37147,674],{"class":541},[290,37149,471],{"class":295},[290,37151,37152],{"class":163,"line":337},[290,37153,620],{"class":295},[290,37155,37156],{"class":163,"line":364},[290,37157,334],{"emptyLinePlaceholder":333},[290,37159,37160],{"class":163,"line":386},[290,37161,37162],{"class":455},"\u002F* Viewport fallback: accounts for typical 24px container padding *\u002F\n",[290,37164,37165,37167,37169,37171],{"class":163,"line":408},[290,37166,874],{"class":541},[290,37168,3595],{"class":295},[290,37170,26114],{"class":461},[290,37172,37173],{"class":295},": calc(var(--breakpoint-sm) + 48px)) {\n",[290,37175,37176,37178],{"class":163,"line":428},[290,37177,9083],{"class":303},[290,37179,450],{"class":295},[290,37181,37182,37184,37186,37188],{"class":163,"line":517},[290,37183,34590],{"class":461},[290,37185,465],{"class":295},[290,37187,9147],{"class":461},[290,37189,471],{"class":295},[290,37191,37192,37194,37196,37198,37200,37202,37204],{"class":163,"line":523},[290,37193,36011],{"class":461},[290,37195,465],{"class":295},[290,37197,468],{"class":461},[290,37199,11964],{"class":541},[290,37201,3290],{"class":461},[290,37203,11964],{"class":541},[290,37205,471],{"class":295},[290,37207,37208],{"class":163,"line":532},[290,37209,771],{"class":295},[290,37211,37212],{"class":163,"line":551},[290,37213,620],{"class":295},[290,37215,37216],{"class":163,"line":586},[290,37217,334],{"emptyLinePlaceholder":333},[290,37219,37220,37222,37224,37226],{"class":163,"line":602},[290,37221,2086],{"class":541},[290,37223,3595],{"class":295},[290,37225,24401],{"class":461},[290,37227,26104],{"class":295},[290,37229,37230,37232],{"class":163,"line":617},[290,37231,37018],{"class":303},[290,37233,450],{"class":295},[290,37235,37236,37238],{"class":163,"line":623},[290,37237,37025],{"class":461},[290,37239,25565],{"class":295},[290,37241,37242],{"class":163,"line":628},[290,37243,771],{"class":295},[290,37245,37246,37248],{"class":163,"line":634},[290,37247,37040],{"class":541},[290,37249,37250],{"class":295}," (min-width: var(--breakpoint-sm)) {\n",[290,37252,37253,37255],{"class":163,"line":649},[290,37254,26128],{"class":303},[290,37256,450],{"class":295},[290,37258,37259,37261,37263,37265],{"class":163,"line":660},[290,37260,37053],{"class":461},[290,37262,465],{"class":295},[290,37264,9147],{"class":461},[290,37266,471],{"class":295},[290,37268,37269,37271,37273,37275,37277,37279,37281],{"class":163,"line":688},[290,37270,36130],{"class":461},[290,37272,465],{"class":295},[290,37274,468],{"class":461},[290,37276,11964],{"class":541},[290,37278,3290],{"class":461},[290,37280,11964],{"class":541},[290,37282,471],{"class":295},[290,37284,37285],{"class":163,"line":693},[290,37286,8200],{"class":295},[290,37288,37289],{"class":163,"line":698},[290,37290,771],{"class":295},[290,37292,37293],{"class":163,"line":704},[290,37294,620],{"class":295},[14,37296,37297],{},[62,37298,37299],{},"Synchronization protocol:",[1393,37301,37302,37308,37316,37319],{},[1396,37303,37304,37305,37307],{},"Store all thresholds in ",[18,37306,1554],{}," or component-scoped custom properties.",[1396,37309,29271,37310,37312,37313,37315],{},[18,37311,3693],{}," adjustments only in the ",[18,37314,874],{}," block to keep container queries mathematically clean.",[1396,37317,37318],{},"Test alignment across standard breakpoints (320px, 768px, 1024px) and high-DPI viewports.",[1396,37320,3038,37321,37323],{},[18,37322,12276],{}," typography scales identically in both fallback and modern paths.",[50,37325,37327],{"id":37326},"javascript-polyfill-integration-performance-optimization","JavaScript Polyfill Integration & Performance Optimization",[14,37329,37330,37331,37334],{},"When exact container-relative behavior is non-negotiable (e.g., dynamic data grids or complex micro-interactions), a ",[18,37332,37333],{},"container queries polyfill"," becomes necessary. Load it conditionally to avoid blocking the main thread or degrading LCP.",[281,37336,37338],{"className":33563,"code":37337,"language":33565,"meta":286,"style":286},"if (!CSS.supports(\"container-type\", \"inline-size\")) {\n  import(\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fcontainer-query-polyfill@latest\u002Fdist\u002Fcontainer-query-polyfill.min.js\")\n    .then(() => {\n      \u002F\u002F Polyfill initialized; re-evaluate layout if needed\n      document.documentElement.classList.add(\"cq-polyfill-active\");\n    })\n    .catch((err) => console.error(\"Container query fallback failed:\", err));\n}\n",[18,37339,37340,37366,37377,37390,37395,37409,37414,37444],{"__ignoreMap":286},[290,37341,37342,37344,37346,37348,37350,37352,37354,37356,37359,37361,37364],{"class":163,"line":292},[290,37343,2913],{"class":541},[290,37345,3595],{"class":295},[290,37347,8006],{"class":541},[290,37349,6627],{"class":461},[290,37351,42],{"class":295},[290,37353,36673],{"class":303},[290,37355,484],{"class":295},[290,37357,37358],{"class":310},"\"container-type\"",[290,37360,569],{"class":295},[290,37362,37363],{"class":310},"\"inline-size\"",[290,37365,14796],{"class":295},[290,37367,37368,37370,37372,37375],{"class":163,"line":330},[290,37369,36690],{"class":541},[290,37371,484],{"class":295},[290,37373,37374],{"class":310},"\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fcontainer-query-polyfill@latest\u002Fdist\u002Fcontainer-query-polyfill.min.js\"",[290,37376,2965],{"class":295},[290,37378,37379,37382,37384,37386,37388],{"class":163,"line":337},[290,37380,37381],{"class":295},"    .",[290,37383,7661],{"class":303},[290,37385,7664],{"class":295},[290,37387,2988],{"class":541},[290,37389,450],{"class":295},[290,37391,37392],{"class":163,"line":364},[290,37393,37394],{"class":455},"      \u002F\u002F Polyfill initialized; re-evaluate layout if needed\n",[290,37396,37397,37400,37402,37404,37407],{"class":163,"line":386},[290,37398,37399],{"class":295},"      document.documentElement.classList.",[290,37401,2935],{"class":303},[290,37403,484],{"class":295},[290,37405,37406],{"class":310},"\"cq-polyfill-active\"",[290,37408,500],{"class":295},[290,37410,37411],{"class":163,"line":408},[290,37412,37413],{"class":295},"    })\n",[290,37415,37416,37418,37421,37423,37426,37428,37430,37433,37436,37438,37441],{"class":163,"line":428},[290,37417,37381],{"class":295},[290,37419,37420],{"class":303},"catch",[290,37422,9509],{"class":295},[290,37424,37425],{"class":1561},"err",[290,37427,490],{"class":295},[290,37429,2988],{"class":541},[290,37431,37432],{"class":295}," console.",[290,37434,37435],{"class":303},"error",[290,37437,484],{"class":295},[290,37439,37440],{"class":310},"\"Container query fallback failed:\"",[290,37442,37443],{"class":295},", err));\n",[290,37445,37446],{"class":163,"line":517},[290,37447,620],{"class":295},[14,37449,37450],{},[62,37451,37452],{},"Performance safeguards:",[1393,37454,37455,37468,37477,37486],{},[1396,37456,37457,37458,2351,37461,3942,37464,37467],{},"Use dynamic ",[18,37459,37460],{},"import()",[18,37462,37463],{},"\u003Cscript type=\"module\">",[18,37465,37466],{},"nomodule"," fallbacks.",[1396,37469,37470,37471,2351,37473,37476],{},"Defer execution until ",[18,37472,16080],{},[18,37474,37475],{},"requestIdleCallback"," to preserve TTI.",[1396,37478,37479,37480,21541,37482,37485],{},"Replace full polyfills with ",[18,37481,25390],{},[18,37483,37484],{},"IntersectionObserver"," listeners for lightweight state toggling.",[1396,37487,37488],{},"Audit main thread blocking time in DevTools Performance panel; polyfill mutation observers can spike if attached to deeply nested DOM trees.",[50,37490,37492],{"id":37491},"debugging-workflows-validation-protocols","Debugging Workflows & Validation Protocols",[14,37494,37495],{},"Systematically verify fallback behavior before shipping. Isolate rendering discrepancies and automate regression checks for container-dependent components.",[3017,37497,37498,37504,37519,37532,37538],{},[1396,37499,37500,37503],{},[62,37501,37502],{},"Emulate legacy engines:"," Use Chrome DevTools → More Tools → Rendering → Emulate CSS Container Queries (toggle off) or test in Safari 15 \u002F Firefox 109 via BrowserStack.",[1396,37505,37506,37509,37510,37512,37513,37515,37516,37518],{},[62,37507,37508],{},"Validate cascade order:"," Ensure ",[18,37511,874],{}," fallbacks load before ",[18,37514,2086],{}," blocks. Use ",[18,37517,3900],{}," only for emergency overrides; prefer specificity management.",[1396,37520,37521,37524,37525,3942,37528,37531],{},[62,37522,37523],{},"Monitor CLS:"," Track layout shifts during container resize and orientation changes. Use ",[18,37526,37527],{},"PerformanceObserver",[18,37529,37530],{},"layout-shift"," entry types to catch unexpected jumps.",[1396,37533,37534,37537],{},[62,37535,37536],{},"Automate visual regression:"," Run Playwright or Cypress snapshot tests across viewport ranges. Assert that component aspect ratios and grid column counts match expected states.",[1396,37539,37540,37543,37544,2351,37546,37548,37549,37551],{},[62,37541,37542],{},"Verify container declaration:"," Missing ",[18,37545,24401],{},[18,37547,26072],{}," causes ",[18,37550,12001],{}," to silently fail. Always inspect computed styles in DevTools.",[2757,37553,34625],{"id":34624},[2250,37555,37556,37572],{},[2253,37557,37558],{},[2256,37559,37560,37563,37566,37569],{},[2259,37561,37562],{},"Environment",[2259,37564,37565],{},"Native Support",[2259,37567,37568],{},"Fallback Target",[2259,37570,37571],{},"Polyfill Compatibility",[2269,37573,37574,37586,37598,37610,37620,37633],{},[2256,37575,37576,37578,37580,37583],{},[2274,37577,2276],{},[2274,37579,29200],{},[2274,37581,37582],{},"90–104",[2274,37584,37585],{},"Full (ResizeObserver)",[2256,37587,37588,37590,37593,37596],{},[2274,37589,2287],{},[2274,37591,37592],{},"110+",[2274,37594,37595],{},"90–109",[2274,37597,34666],{},[2256,37599,37600,37602,37604,37607],{},[2274,37601,2297],{},[2274,37603,8465],{},[2274,37605,37606],{},"14–15",[2274,37608,37609],{},"Full (with ResizeObserver shim)",[2256,37611,37612,37614,37616,37618],{},[2274,37613,2308],{},[2274,37615,29200],{},[2274,37617,37582],{},[2274,37619,34666],{},[2256,37621,37622,37625,37628,37631],{},[2274,37623,37624],{},"Opera",[2274,37626,37627],{},"91+",[2274,37629,37630],{},"80–90",[2274,37632,34666],{},[2256,37634,37635,37638,37641,37644],{},[2274,37636,37637],{},"Legacy Mobile WebView",[2274,37639,37640],{},"Varies",[2274,37642,37643],{},"Android 10–12, iOS 14–15",[2274,37645,37646],{},"Requires ResizeObserver polyfill",[2757,37648,37650],{"id":37649},"common-issues-direct-resolutions","Common Issues & Direct Resolutions",[1393,37652,37653,37669,37682,37698,37714],{},[1396,37654,37655,37658,37659,37661,37662,37665,37666,37668],{},[62,37656,37657],{},"Cascade conflicts:"," Fallback ",[18,37660,874],{}," rules override container styles in modern browsers. ",[86,37663,37664],{},"Fix:"," Wrap modern rules in ",[18,37667,2086],{}," and ensure fallbacks use lower specificity or load earlier.",[1396,37670,37671,37674,37675,37677,37678,37681],{},[62,37672,37673],{},"Premature layout shifts:"," Incorrect breakpoint mapping triggers early grid switches. ",[86,37676,37664],{}," Subtract parent padding\u002Fgutters from viewport thresholds and use ",[18,37679,37680],{},"min-height"," on media elements.",[1396,37683,37684,37687,37688,37690,37691,37694,37695,37697],{},[62,37685,37686],{},"Polyfill race conditions:"," Component renders before polyfill evaluates. ",[86,37689,37664],{}," Add a ",[18,37692,37693],{},".cq-loading"," class to hide or skeletonize components until ",[18,37696,16080],{}," + polyfill init resolves.",[1396,37699,37700,37543,37705,37707,37708,37710,37711,37713],{},[62,37701,37702,37703,723],{},"Silently ignored ",[18,37704,12001],{},[18,37706,24401],{}," declaration. ",[86,37709,37664],{}," Always declare ",[18,37712,25409],{}," on the direct parent.",[1396,37715,37716,37719,37720,37722,37723,37725],{},[62,37717,37718],{},"ResizeObserver performance degradation:"," Unoptimized listeners in legacy environments. ",[86,37721,37664],{}," Throttle resize callbacks, use ",[18,37724,13074],{},", and detach observers when components unmount.",[50,37727,1316],{"id":1315},[14,37729,37730,37733,37734,37736],{},[62,37731,37732],{},"Do I need a polyfill if I use @supports fallbacks?","\nNot necessarily. ",[18,37735,2086],{}," with media query fallbacks covers 95% of layout requirements. Use polyfills only when exact container-relative behavior is critical for complex component states or dynamic content injection.",[14,37738,37739,37742,37743,37745,37746,37748],{},[62,37740,37741],{},"How do I prevent layout shift when switching between fallback and container queries?","\nEnsure baseline styles match the smallest container state exactly. Use CSS variables for consistent spacing and typography, and avoid layout-triggering properties in ",[18,37744,12001],{}," blocks. Always define explicit aspect ratios or ",[18,37747,37680],{}," values for media elements.",[14,37750,37751,37757,37758,37760],{},[62,37752,37753,37754,37756],{},"Can I combine container queries with fluid typography (",[18,37755,12276],{},") in fallbacks?","\nYes. ",[18,37759,12276],{}," is widely supported and works independently of container queries. Apply it to baseline styles so typography scales gracefully in both modern and legacy contexts without requiring additional fallback logic.",[14,37762,37763,37769,37770,37772,37773,37775],{},[62,37764,37765,37766,37768],{},"Why does my ",[18,37767,12001],{}," rule get ignored in older browsers?","\nOlder browsers lack native ",[18,37771,12001],{}," support. Without an ",[18,37774,2086],{}," wrapper or polyfill, the rule is silently dropped. Always provide a viewport-based media query fallback for critical layouts, and verify cascade order to prevent overrides.",[50,37777,1391],{"id":1390},[1393,37779,37780,37786,37791,37798],{},[1396,37781,37782,37785],{},[27,37783,37784],{"href":19560},"Feature Detection with @supports"," — the detection primitive that gates every fallback branch here.",[1396,37787,37788,37790],{},[27,37789,35802],{"href":35801}," — the parent guide covering the full degradation strategy.",[1396,37792,37793,37797],{},[27,37794,37796],{"href":37795},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-vs-media-queries-comparison\u002F","Container vs Media Queries Comparison"," — when a viewport breakpoint is the better baseline.",[1396,37799,37800,37802],{},[27,37801,8757],{"href":8756}," — keep polyfill-driven reflows off the critical render path.",[1430,37804,34037],{},{"title":286,"searchDepth":330,"depth":330,"links":37806},[37807,37808,37809,37810,37814,37815],{"id":36929,"depth":330,"text":36930},{"id":37118,"depth":330,"text":37119},{"id":37326,"depth":330,"text":37327},{"id":37491,"depth":330,"text":37492,"children":37811},[37812,37813],{"id":34624,"depth":337,"text":34625},{"id":37649,"depth":337,"text":37650},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Handle container query fallbacks in older browsers: prevent CLS, use viewport-based mapping, and deploy conditionally with progressive enhancement.",{"seoTitle":37818,"datePublished":1447,"dateModified":1447,"faq":37819},"Container Query Fallbacks for Older Browsers",[37820,37822,37824,37827],{"q":37732,"a":37821},"Not necessarily. @supports with media query fallbacks covers about 95% of layout requirements. Use polyfills only when exact container-relative behavior is critical for complex component states or dynamic content injection.",{"q":37741,"a":37823},"Ensure baseline styles match the smallest container state exactly. Use CSS variables for consistent spacing and typography, avoid layout-triggering properties in @container blocks, and define explicit aspect ratios or min-height values for media elements.",{"q":37825,"a":37826},"Can I combine container queries with fluid typography (clamp()) in fallbacks?","Yes. clamp() is widely supported and works independently of container queries. Apply it to baseline styles so typography scales gracefully in both modern and legacy contexts without additional fallback logic.",{"q":37828,"a":37829},"Why does my @container rule get ignored in older browsers?","Older browsers lack native @container support, so without an @supports wrapper or polyfill the rule is silently dropped. Always provide a viewport-based media query fallback for critical layouts and verify cascade order to prevent overrides.","\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fhandling-container-query-fallbacks-for-older-browsers",{"title":36828,"description":37816},"mastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Fhandling-container-query-fallbacks-for-older-browsers\u002Findex","ktLLr_A14MsO9abYaaJQTC2U53GXdKL3GNiUPqh2-nA",{"id":37835,"title":37836,"body":37837,"description":38741,"extension":1444,"meta":38742,"navigation":333,"path":38753,"seo":38754,"stem":38755,"__hash__":38756},"content\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Findex.md","Container Query Fallbacks: Spec-Compliant CSS Strategies for Legacy Browsers",{"type":7,"value":37838,"toc":38728},[37839,37842,37847,37895,37901,37903,37907,37913,37916,37918,37921,37936,37944,38056,38060,38083,38085,38089,38100,38191,38196,38245,38247,38251,38263,38312,38317,38349,38351,38355,38358,38362,38428,38433,38456,38458,38462,38503,38508,38540,38542,38544,38637,38639,38641,38650,38669,38680,38686,38688,38725],[10,37840,37836],{"id":37841},"container-query-fallbacks-spec-compliant-css-strategies-for-legacy-browsers",[14,37843,21682,37844,37846],{},[62,37845,35802],{}," is no longer optional for enterprise-grade design systems. As component architectures shift from viewport-centric to context-aware layouts, ensuring resilience across varying browser environments becomes a core engineering responsibility. This guide bridges foundational layout concepts with practical, spec-compliant CSS techniques, prioritizing progressive enhancement over graceful degradation.",[133,37848,140,37850,140,37853,140,37856,140,37859,140,37861,140,37864,140,37866,140,37868,140,37871,140,37874,140,37876,140,37879,140,37883,140,37886,140,37889,140,37892],{"viewBox":4133,"role":136,"ariaLabel":37849,"xmlns":138,"style":139},"Decision flow choosing a container query path based on browser support",[142,37851,37852],{},"Fallback decision flow",[146,37854,37855],{},"An @supports test routes browsers to a native container query path or a viewport media query fallback.",[150,37857,37858],{"x":152,"y":2598,"style":154},"Support-gated fallback path",[171,37860],{"x":214,"y":5900,"width":2637,"height":5900,"rx":176,"fill":177,"opacity":178,"stroke":177,"strokeWidth":168},[150,37862,36896],{"x":152,"y":37863,"style":12455},"83",[163,37865],{"x1":5397,"y1":4180,"x2":1822,"y2":1830,"stroke":167,"strokeWidth":168,"opacity":232},[163,37867],{"x1":15019,"y1":4180,"x2":1812,"y2":1830,"stroke":167,"strokeWidth":168,"opacity":232},[150,37869,36903],{"x":33016,"y":5909,"style":37870},"text-anchor:middle;fill:#7aa2ff;font:12px sans-serif",[150,37872,36907],{"x":37873,"y":5909,"style":1808},"495",[171,37875],{"x":1788,"y":4166,"width":2633,"height":32042,"rx":176,"fill":72,"stroke":177,"strokeWidth":168},[150,37877,37878],{"x":1822,"y":27969,"style":2606},"native @container",[150,37880,37882],{"x":1822,"y":37881,"style":5915},"216","parent-driven layout",[171,37884],{"x":37885,"y":4166,"width":2633,"height":32042,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},"436",[150,37887,37888],{"x":1812,"y":27969,"style":2606},"@media fallback",[150,37890,37891],{"x":1812,"y":37881,"style":5915},"viewport baseline",[150,37893,37894],{"x":152,"y":35897,"style":22612},"both paths share the same design tokens",[14,37896,37897,37898,37900],{},"By leveraging native feature detection, strategic cascade management, and conditional JavaScript bridges, you can maintain UI consistency without sacrificing performance. The strategies outlined here align with modern ",[27,37899,11296],{"href":5777}," principles, ensuring your components scale predictably from legacy engines to cutting-edge rendering contexts.",[47,37902],{},[50,37904,37906],{"id":37905},"understanding-the-fallback-landscape","Understanding the Fallback Landscape",[14,37908,37909,37910,37912],{},"Browser adoption curves for ",[18,37911,12001],{}," have stabilized, but enterprise environments, embedded webviews, and older mobile WebKit instances still require defensive styling. The fallback landscape isn't about replicating exact container query behavior in unsupported browsers; it's about establishing a predictable baseline that degrades gracefully while preserving component isolation and design token integrity.",[14,37914,37915],{},"Progressive enhancement dictates that baseline layouts should function independently of advanced features. When container queries fail, the component must still render legibly, maintain accessible tap targets, and avoid cumulative layout shift (CLS). This requires decoupling layout logic from container context and relying on viewport-driven or intrinsic fallbacks until the rendering engine supports the Containment Module Level 3 specification.",[47,37917],{},[50,37919,37784],{"id":37920},"feature-detection-with-supports",[14,37922,37923,37924,37926,37927,37929,37930,37932,37933,42],{},"Native CSS feature detection is the cleanest way to gate container-specific styles. Building directly on ",[27,37925,26284],{"href":25340},", you can wrap ",[18,37928,12001],{}," rules inside ",[18,37931,2086],{}," to prevent parsing errors and cascade conflicts in legacy browsers. For a focused walkthrough of the test patterns themselves, see ",[27,37934,37935],{"href":19560},"feature detection with @supports",[14,37937,37938,37939,69,37941,37943],{},"The key is to declare ",[18,37940,24401],{},[18,37942,26072],{}," only when the engine supports them, then apply context-aware styles conditionally. This prevents unsupported browsers from attempting to parse invalid rules or triggering unexpected specificity wars.",[281,37945,37947],{"className":438,"code":37946,"language":440,"meta":286,"style":286},"\u002F* @supports Feature Detection Wrapper *\u002F\n@supports (container-type: inline-size) {\n  .card {\n    container-type: inline-size;\n    container-name: card;\n  }\n\n  @container card (min-width: 400px) {\n    .card__content {\n      display: flex;\n      flex-direction: row;\n      gap: var(--space-md);\n    }\n  }\n}\n",[18,37948,37949,37954,37964,37970,37976,37984,37988,37992,37999,38006,38016,38028,38044,38048,38052],{"__ignoreMap":286},[290,37950,37951],{"class":163,"line":292},[290,37952,37953],{"class":455},"\u002F* @supports Feature Detection Wrapper *\u002F\n",[290,37955,37956,37958,37960,37962],{"class":163,"line":330},[290,37957,2086],{"class":541},[290,37959,3595],{"class":295},[290,37961,24401],{"class":461},[290,37963,26104],{"class":295},[290,37965,37966,37968],{"class":163,"line":337},[290,37967,9083],{"class":303},[290,37969,450],{"class":295},[290,37971,37972,37974],{"class":163,"line":364},[290,37973,37025],{"class":461},[290,37975,25565],{"class":295},[290,37977,37978,37981],{"class":163,"line":386},[290,37979,37980],{"class":461},"    container-name",[290,37982,37983],{"class":295},": card;\n",[290,37985,37986],{"class":163,"line":408},[290,37987,771],{"class":295},[290,37989,37990],{"class":163,"line":428},[290,37991,334],{"emptyLinePlaceholder":333},[290,37993,37994,37996],{"class":163,"line":517},[290,37995,37040],{"class":541},[290,37997,37998],{"class":295}," card (min-width: 400px) {\n",[290,38000,38001,38004],{"class":163,"line":523},[290,38002,38003],{"class":303},"    .card__content",[290,38005,450],{"class":295},[290,38007,38008,38010,38012,38014],{"class":163,"line":532},[290,38009,37053],{"class":461},[290,38011,465],{"class":295},[290,38013,9055],{"class":461},[290,38015,471],{"class":295},[290,38017,38018,38021,38023,38026],{"class":163,"line":551},[290,38019,38020],{"class":461},"      flex-direction",[290,38022,465],{"class":295},[290,38024,38025],{"class":461},"row",[290,38027,471],{"class":295},[290,38029,38030,38033,38035,38037,38039,38042],{"class":163,"line":586},[290,38031,38032],{"class":461},"      gap",[290,38034,465],{"class":295},[290,38036,1622],{"class":461},[290,38038,484],{"class":295},[290,38040,38041],{"class":1561},"--space-md",[290,38043,500],{"class":295},[290,38045,38046],{"class":163,"line":602},[290,38047,8200],{"class":295},[290,38049,38050],{"class":163,"line":617},[290,38051,771],{"class":295},[290,38053,38054],{"class":163,"line":623},[290,38055,620],{"class":295},[14,38057,38058],{},[62,38059,1681],{},[1393,38061,38062,38070,38076],{},[1396,38063,38064,38065,3942,38067,38069],{},"Always pair ",[18,38066,24401],{},[18,38068,26072],{}," to avoid scoping collisions in nested components.",[1396,38071,38072,38073,38075],{},"Keep the ",[18,38074,2086],{}," block self-contained. Do not mix unsupported properties inside the block.",[1396,38077,38078,38079,38082],{},"Use CSS custom properties (",[18,38080,38081],{},"var(--space-md)",") to maintain design system consistency across both supported and unsupported contexts.",[47,38084],{},[50,38086,38088],{"id":38087},"fallback-layout-strategies","Fallback Layout Strategies",[14,38090,38091,38092,38094,38095,38099],{},"When native container queries are unavailable, viewport-based ",[18,38093,874],{}," queries and intrinsic sizing techniques serve as reliable baselines. Integrating seamlessly with established ",[27,38096,38098],{"href":38097},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002F","Responsive Component Patterns",", you can mirror container breakpoints using standard media queries, ensuring visual parity without JavaScript overhead.",[281,38101,38103],{"className":438,"code":38102,"language":440,"meta":286,"style":286},"\u002F* Media Query Fallback Baseline *\u002F\n@supports not (container-type: inline-size) {\n  @media (min-width: 768px) {\n    .card__content {\n      display: flex;\n      flex-direction: row;\n      gap: var(--space-md);\n    }\n  }\n}\n",[18,38104,38105,38110,38122,38139,38145,38155,38165,38179,38183,38187],{"__ignoreMap":286},[290,38106,38107],{"class":163,"line":292},[290,38108,38109],{"class":455},"\u002F* Media Query Fallback Baseline *\u002F\n",[290,38111,38112,38114,38116,38118,38120],{"class":163,"line":330},[290,38113,2086],{"class":541},[290,38115,2116],{"class":541},[290,38117,3595],{"class":295},[290,38119,24401],{"class":461},[290,38121,26104],{"class":295},[290,38123,38124,38126,38128,38130,38132,38135,38137],{"class":163,"line":337},[290,38125,26109],{"class":541},[290,38127,3595],{"class":295},[290,38129,26114],{"class":461},[290,38131,465],{"class":295},[290,38133,38134],{"class":461},"768",[290,38136,674],{"class":541},[290,38138,646],{"class":295},[290,38140,38141,38143],{"class":163,"line":364},[290,38142,38003],{"class":303},[290,38144,450],{"class":295},[290,38146,38147,38149,38151,38153],{"class":163,"line":386},[290,38148,37053],{"class":461},[290,38150,465],{"class":295},[290,38152,9055],{"class":461},[290,38154,471],{"class":295},[290,38156,38157,38159,38161,38163],{"class":163,"line":408},[290,38158,38020],{"class":461},[290,38160,465],{"class":295},[290,38162,38025],{"class":461},[290,38164,471],{"class":295},[290,38166,38167,38169,38171,38173,38175,38177],{"class":163,"line":428},[290,38168,38032],{"class":461},[290,38170,465],{"class":295},[290,38172,1622],{"class":461},[290,38174,484],{"class":295},[290,38176,38041],{"class":1561},[290,38178,500],{"class":295},[290,38180,38181],{"class":163,"line":517},[290,38182,8200],{"class":295},[290,38184,38185],{"class":163,"line":523},[290,38186,771],{"class":295},[290,38188,38189],{"class":163,"line":532},[290,38190,620],{"class":295},[14,38192,38193],{},[62,38194,38195],{},"Architectural Patterns for Fallbacks:",[3017,38197,38198,38212,38225],{},[1396,38199,38200,38203,38204,38207,38208,38211],{},[62,38201,38202],{},"Flexbox\u002FGrid Baselines:"," Define a default layout (e.g., ",[18,38205,38206],{},"flex-direction: column",") outside any feature query. Override it inside ",[18,38209,38210],{},"@supports not (...)"," with viewport breakpoints.",[1396,38213,38214,2318,38217,69,38219,38221,38222],{},[62,38215,38216],{},"CSS Custom Properties for Dynamic Scaling:",[18,38218,12276],{},[18,38220,3693],{}," to simulate fluid scaling without JS. Example: ",[18,38223,38224],{},"font-size: clamp(1rem, 2vw, 1.25rem);",[1396,38226,38227,38230,38231,38233,38234,38236,38237,38240,38241,38244],{},[62,38228,38229],{},"Breakpoint Alignment:"," Map ",[18,38232,12001],{}," breakpoints to equivalent ",[18,38235,874],{}," breakpoints. If ",[18,38238,38239],{},"@container card (min-width: 400px)"," triggers a layout shift, use ",[18,38242,38243],{},"@media (min-width: 768px)"," in the fallback to approximate the same visual threshold.",[47,38246],{},[50,38248,38250],{"id":38249},"polyfills-and-javascript-bridges","Polyfills and JavaScript Bridges",[14,38252,38253,38254,38257,38258,69,38261,42],{},"For environments requiring exact component isolation (e.g., legacy enterprise dashboards, embedded admin panels), CSS-only fallbacks may not suffice. The Google Chrome Labs ",[18,38255,38256],{},"container-query-polyfill"," package can bridge the gap, but it introduces measurable performance overhead via ",[18,38259,38260],{},"MutationObserver",[18,38262,25390],{},[281,38264,38266],{"className":33563,"code":38265,"language":33565,"meta":286,"style":286},"\u002F\u002F Conditional polyfill: only loads in unsupported browsers\nif (!CSS.supports(\"container-type\", \"inline-size\")) {\n  import(\"https:\u002F\u002Funpkg.com\u002Fcontainer-query-polyfill@1\u002Fdist\u002Fcontainer-query-polyfill.modern.js\");\n}\n",[18,38267,38268,38273,38297,38308],{"__ignoreMap":286},[290,38269,38270],{"class":163,"line":292},[290,38271,38272],{"class":455},"\u002F\u002F Conditional polyfill: only loads in unsupported browsers\n",[290,38274,38275,38277,38279,38281,38283,38285,38287,38289,38291,38293,38295],{"class":163,"line":330},[290,38276,2913],{"class":541},[290,38278,3595],{"class":295},[290,38280,8006],{"class":541},[290,38282,6627],{"class":461},[290,38284,42],{"class":295},[290,38286,36673],{"class":303},[290,38288,484],{"class":295},[290,38290,37358],{"class":310},[290,38292,569],{"class":295},[290,38294,37363],{"class":310},[290,38296,14796],{"class":295},[290,38298,38299,38301,38303,38306],{"class":163,"line":337},[290,38300,36690],{"class":541},[290,38302,484],{"class":295},[290,38304,38305],{"class":310},"\"https:\u002F\u002Funpkg.com\u002Fcontainer-query-polyfill@1\u002Fdist\u002Fcontainer-query-polyfill.modern.js\"",[290,38307,500],{"class":295},[290,38309,38310],{"class":163,"line":364},[290,38311,620],{"class":295},[14,38313,38314],{},[62,38315,38316],{},"Trade-offs & Best Practices:",[1393,38318,38319,38325,38334,38343],{},[1396,38320,38321,38324],{},[62,38322,38323],{},"Performance Overhead:"," JS polyfills recalculate layout on DOM mutations, increasing main thread work and potentially impacting FID\u002FLCP.",[1396,38326,38327,38330,38331,38333],{},[62,38328,38329],{},"Conditional Loading:"," Always wrap polyfill imports in ",[18,38332,36580],{}," checks. Never ship polyfill code to modern browsers.",[1396,38335,38336,38339,38340,38342],{},[62,38337,38338],{},"When to Use JS:"," Reserve polyfills for complex nested grids, micro-interaction-heavy components, or strict compliance requirements. For 90% of use cases, CSS-only fallbacks via ",[18,38341,2086],{}," are faster and more maintainable.",[1396,38344,38345,38348],{},[62,38346,38347],{},"CI\u002FCD Integration:"," Automate fallback validation by running Lighthouse CI against legacy browser profiles and tracking CLS deltas.",[47,38350],{},[50,38352,38354],{"id":38353},"testing-and-validation-workflows","Testing and Validation Workflows",[14,38356,38357],{},"Robust fallback implementation requires automated and manual validation. Visual regression testing ensures that layout shifts don't occur when switching between supported and unsupported engines.",[2757,38359,38361],{"id":38360},"devtools-debugging-steps","DevTools Debugging Steps",[3017,38363,38364,38386,38401,38414],{},[1396,38365,38366,38369,38370,16198,38373,38375,38376,38378,38379,38382,38383,42],{},[62,38367,38368],{},"Emulate Unsupported Engines:"," Open Chrome DevTools → ",[18,38371,38372],{},"More tools",[18,38374,3024],{}," → disable ",[18,38377,12001],{}," support via experimental flags, or use Firefox's ",[18,38380,38381],{},"about:config"," to toggle ",[18,38384,38385],{},"layout.css.container-queries.enabled",[1396,38387,38388,38391,38392,38394,38395,38397,38398,38400],{},[62,38389,38390],{},"Inspect Cascade Conflicts:"," Use the ",[18,38393,13452],{}," pane to verify that ",[18,38396,38210],{}," rules are correctly overriding baseline styles. Check for specificity leaks using the ",[18,38399,13407],{}," tab.",[1396,38402,38403,38406,38407,38409,38410,38413],{},[62,38404,38405],{},"Monitor Layout Shifts:"," Open ",[18,38408,16173],{}," panel, record a page load, and inspect ",[18,38411,38412],{},"Layout Shift"," events. Ensure fallback activation doesn't trigger reflow after paint.",[1396,38415,38416,38391,38419,38422,38423,69,38425,38427],{},[62,38417,38418],{},"Validate Container Scoping:",[18,38420,38421],{},"Elements"," panel to verify ",[18,38424,26072],{},[18,38426,24401],{}," are applied only when supported. Check for unexpected inheritance in nested components.",[14,38429,38430],{},[62,38431,38432],{},"Automation Stack:",[1393,38434,38435,38441,38450],{},[1396,38436,38437,38440],{},[62,38438,38439],{},"BrowserStack \u002F Sauce Labs:"," Run cross-browser matrix tests against IE11, Edge 18, and iOS 14 WebKit.",[1396,38442,38443,38446,38447,38449],{},[62,38444,38445],{},"Playwright \u002F Cypress:"," Configure visual regression snapshots with ",[18,38448,2086],{}," toggled off. Compare against baseline renders.",[1396,38451,38452,38455],{},[62,38453,38454],{},"Accessibility Audits:"," Verify that fallback layouts maintain logical DOM order, focus management, and screen reader announcements.",[47,38457],{},[50,38459,38461],{"id":38460},"browser-support-spec-compliance","Browser Support & Spec Compliance",[2250,38463,38464,38474],{},[2253,38465,38466],{},[2256,38467,38468,38470,38472],{},[2259,38469,34634],{},[2259,38471,37565],{},[2259,38473,37568],{},[2269,38475,38476,38485,38494],{},[2256,38477,38478,38480,38482],{},[2274,38479,34649],{},[2274,38481,29200],{},[2274,38483,38484],{},"Legacy Edge (18-), Android WebKit",[2256,38486,38487,38489,38491],{},[2274,38488,2297],{},[2274,38490,8465],{},[2274,38492,38493],{},"iOS 14- WebKit",[2256,38495,38496,38498,38500],{},[2274,38497,2287],{},[2274,38499,37592],{},[2274,38501,38502],{},"ESR 102, older Gecko",[14,38504,38505],{},[62,38506,38507],{},"Spec References:",[1393,38509,38510,38523,38532],{},[1396,38511,38512,3595,38517,569,38519,569,38521,3914],{},[27,38513,38516],{"href":38514,"rel":38515},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-contain-3\u002F",[13489],"CSS Containment Module Level 3",[18,38518,12001],{},[18,38520,24401],{},[18,38522,26072],{},[1396,38524,38525,3595,38530,3914],{},[27,38526,38529],{"href":38527,"rel":38528},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-conditional-3\u002F",[13489],"CSS Conditional Rules Module Level 3",[18,38531,2086],{},[1396,38533,38534,38539],{},[27,38535,38538],{"href":38536,"rel":38537},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fmediaqueries-4\u002F",[13489],"CSS Media Queries Level 4"," (Viewport fallback alignment)",[47,38541],{},[50,38543,3865],{"id":3864},[2250,38545,38546,38557],{},[2253,38547,38548],{},[2256,38549,38550,38552,38554],{},[2259,38551,2338],{},[2259,38553,3876],{},[2259,38555,38556],{},"Mitigation Strategy",[2269,38558,38559,38578,38600,38616],{},[2256,38560,38561,38564,38567],{},[2274,38562,38563],{},"Layout shift on polyfill load",[2274,38565,38566],{},"Delayed style recalculation after DOM mutation",[2274,38568,38569,38570,2351,38573,38575,38576,42],{},"Pre-allocate dimensions using ",[18,38571,38572],{},"aspect-ratio",[18,38574,37680],{},". Defer polyfill to ",[18,38577,37475],{},[2256,38579,38580,38588,38591],{},[2274,38581,38582,38583,69,38585,38587],{},"Conflicting ",[18,38584,874],{},[18,38586,12001],{}," rules",[2274,38589,38590],{},"Specificity wars in overlapping breakpoints",[2274,38592,38593,38594,38596,38597,38599],{},"Isolate fallbacks with ",[18,38595,38210],{},". Use CSS cascade layers (",[18,38598,12681],{},") to enforce precedence.",[2256,38601,38602,38607,38610],{},[2274,38603,38604,38605],{},"Performance degradation with ",[18,38606,38260],{},[2274,38608,38609],{},"Heavy DOM polling in polyfills",[2274,38611,38612,38613,38615],{},"Throttle observer callbacks. Use ",[18,38614,25390],{}," natively where possible. Fallback to viewport queries instead.",[2256,38617,38618,38624,38627],{},[2274,38619,38620,38621,38623],{},"Inconsistent ",[18,38622,26072],{}," scoping",[2274,38625,38626],{},"Global namespace collisions in legacy engines",[2274,38628,38629,38630,569,38633,38636],{},"Prefix container names (e.g., ",[18,38631,38632],{},"card-primary",[18,38634,38635],{},"card-secondary","). Avoid reusing names across component boundaries.",[47,38638],{},[50,38640,1316],{"id":1315},[14,38642,38643,38646,38647,38649],{},[62,38644,38645],{},"Should I use a polyfill or CSS-only fallbacks for container queries?","\nCSS-only fallbacks via ",[18,38648,2086],{}," are preferred for performance and maintainability. Polyfills should only be used when exact component isolation is critical for legacy enterprise browsers or when strict design system parity is mandated.",[14,38651,38652,38655,38656,569,38659,569,38661,38663,38664,3041,38666,38668],{},[62,38653,38654],{},"How do I prevent layout shifts when fallbacks activate?","\nDefine baseline dimensions using intrinsic sizing (",[18,38657,38658],{},"min-content",[18,38660,17909],{},[18,38662,38572],{},") or explicit ",[18,38665,37680],{},[18,38667,26114],{}," constraints. Ensure fallback media queries mirror the container query breakpoints to maintain visual consistency and avoid post-paint reflows.",[14,38670,38671,4968,38674,38676,38677,38679],{},[62,38672,38673],{},"Can I combine container queries with CSS Grid fallbacks?",[18,38675,2086],{}," to gate the container query block, and provide a standard Grid or Flexbox layout outside the block. The cascade ensures the most specific, supported rule applies. Leverage ",[18,38678,12681],{}," to explicitly control fallback precedence.",[14,38681,38682,38685],{},[62,38683,38684],{},"Do container query fallbacks impact Core Web Vitals?","\nPoorly implemented JS polyfills can increase CLS and FID due to delayed layout recalculation. Native CSS fallbacks and conditional loading strategies preserve LCP and CLS scores while maintaining responsiveness. Always audit fallback activation in the Performance tab before shipping.",[50,38687,1391],{"id":1390},[1393,38689,38690,38695,38703,38710,38715],{},[1396,38691,38692,38694],{},[27,38693,36828],{"href":26188}," — viewport and intrinsic baselines for unsupported engines.",[1396,38696,38697,19562,38700,38702],{},[27,38698,38699],{"href":19560},"Feature detection with @supports",[18,38701,2086],{}," test patterns that gate container styles.",[1396,38704,38705,19562,38707,38709],{},[27,38706,26284],{"href":25340},[18,38708,12001],{}," at-rule and units these fallbacks wrap.",[1396,38711,38712,38714],{},[27,38713,11296],{"href":5777}," — the parent guide to component-driven responsive design.",[1396,38716,38717,38719,38720,3041,38722,38724],{},[27,38718,1413],{"href":1412}," — the same ",[18,38721,2086],{},[18,38723,874],{}," enhancement mindset applied to accessible motion.",[1430,38726,38727],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":286,"searchDepth":330,"depth":330,"links":38729},[38730,38731,38732,38733,38734,38737,38738,38739,38740],{"id":37905,"depth":330,"text":37906},{"id":37920,"depth":330,"text":37784},{"id":38087,"depth":330,"text":38088},{"id":38249,"depth":330,"text":38250},{"id":38353,"depth":330,"text":38354,"children":38735},[38736],{"id":38360,"depth":337,"text":38361},{"id":38460,"depth":330,"text":38461},{"id":3864,"depth":330,"text":3865},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"CSS container query fallbacks for legacy browsers: progressive enhancement, feature detection, and spec-compliant graceful degradation strategies.",{"seoTitle":38743,"datePublished":1447,"dateModified":1447,"faq":38744},"CSS Container Query Fallbacks Guide",[38745,38747,38749,38751],{"q":38645,"a":38746},"CSS-only fallbacks via @supports are preferred for performance and maintainability. Polyfills should only be used when exact component isolation is critical for legacy enterprise browsers or when strict design system parity is mandated.",{"q":38654,"a":38748},"Define baseline dimensions using intrinsic sizing (min-content, max-content, aspect-ratio) or explicit min-height\u002Fmin-width constraints. Ensure fallback media queries mirror the container query breakpoints to maintain visual consistency and avoid post-paint reflows.",{"q":38673,"a":38750},"Yes. Use @supports to gate the container query block, and provide a standard Grid or Flexbox layout outside the block. The cascade ensures the most specific, supported rule applies. Leverage @layer to explicitly control fallback precedence.",{"q":38684,"a":38752},"Poorly implemented JS polyfills can increase CLS and FID due to delayed layout recalculation. Native CSS fallbacks and conditional loading strategies preserve LCP and CLS scores while maintaining responsiveness. Always audit fallback activation in the Performance tab before shipping.","\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks",{"title":37836,"description":38741},"mastering-container-queries-responsive-layouts\u002Fcontainer-query-fallbacks\u002Findex","oJM18Vk9kuPy2MiiwAm9hMPqzqsnD0WEE0prWVhp23c",{"id":38758,"title":38759,"body":38760,"description":39936,"extension":1444,"meta":39937,"navigation":333,"path":39948,"seo":39949,"stem":39950,"__hash__":39951},"content\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-query-units-cqi-cqb-explained\u002Findex.md","Container Query Units Explained: cqi, cqb, cqw, cqh, cqmin and cqmax",{"type":7,"value":38761,"toc":39926},[38762,38765,38767,38785,38787,38833,38845,38847,38887,38890,39642,39644,39680,39682,39694,39799,39811,39813,39831,39833,39850,39862,39874,39886,39888,39924],[10,38763,38759],{"id":38764},"container-query-units-explained-cqi-cqb-cqw-cqh-cqmin-and-cqmax",[50,38766,35783],{"id":35782},[14,38768,38769,38770,569,38772,38775,38776,38779,38780,35803,38782,38784],{},"You have a card that lives in a sidebar on one screen and spans the full content column on another, and you want its padding, heading size, and gap to scale with the space the card actually occupies rather than with the viewport. Viewport units (",[18,38771,12094],{},[18,38773,38774],{},"vh",") only know about the screen, so a card in a 280px sidebar gets the same ",[18,38777,38778],{},"4vw"," padding as a card in a 1200px hero. Container query length units solve exactly this: they resolve against the measured size of the nearest query container, letting one component scale correctly in every slot. This guide sits under ",[27,38781,26284],{"href":25340},[27,38783,11296],{"href":5777},", and focuses narrowly on the six length units and how each resolves.",[50,38786,35809],{"id":35808},[14,38788,38789,38790,569,38793,569,38796,569,38798,569,38800,569,38803,38806,38807,69,38809,38811,38812,69,38814,38816,38817,38819,38820,3041,38822,8393,38824,38826,38827,38829,38830,38832],{},"The six units — ",[18,38791,38792],{},"cqw",[18,38794,38795],{},"cqh",[18,38797,11404],{},[18,38799,12282],{},[18,38801,38802],{},"cqmin",[18,38804,38805],{},"cqmax"," — each represent 1% of a dimension of the query container. ",[18,38808,38792],{},[18,38810,38795],{}," are the physical width and height. ",[18,38813,11404],{},[18,38815,12282],{}," are the logical inline and block sizes, which track the writing mode rather than the screen axis. ",[18,38818,38802],{}," resolves to the smaller of ",[18,38821,11404],{},[18,38823,12282],{},[18,38825,38805],{}," to the larger. Because these units read the container box directly, you get continuous fluid scaling with no breakpoints and no JavaScript ",[18,38828,25390],{}," callback. That matters for accessibility too: a ",[18,38831,25390],{},"-driven resize fires layout work on the main thread and can lag behind the paint, producing visible jumps for users on low-powered devices, whereas container units are resolved by the engine during layout in a single pass.",[14,38834,38835,38836,38839,38840,38842,38843,42],{},"The one caveat is unbounded scaling. A raw ",[18,38837,38838],{},"font-size: 5cqi"," will keep growing as the container widens, eventually overflowing the line. The fix is to compose the unit inside ",[18,38841,12276],{}," so it has a floor and a ceiling — which is also how you combine it cleanly with the techniques covered in the guide to ",[27,38844,11313],{"href":11312},[50,38846,4203],{"id":4202},[133,38848,140,38850,140,38853,140,38856,140,38859,140,38861,140,38865,140,38867,140,38869,140,38873,140,38875,140,38879,140,38884],{"viewBox":2588,"role":136,"ariaLabel":38849,"xmlns":138,"style":139},"A query container box with its inline-size labelled cqi along the horizontal axis and block-size labelled cqb along the vertical axis",[142,38851,38852],{},"Container query length axes",[146,38854,38855],{},"A container box showing the inline axis resolving cqi and the block axis resolving cqb, with cqmin and cqmax derived from them.",[150,38857,38858],{"x":152,"y":2598,"style":154},"How cqi and cqb map to a query container",[171,38860],{"x":1830,"y":5144,"width":2618,"height":2602,"rx":5894,"fill":177,"stroke":167,"strokeWidth":168,"opacity":178},[150,38862,38864],{"x":152,"y":4154,"style":38863},"text-anchor:middle;fill:currentColor;font:600 14px sans-serif;opacity:0.85","query container",[150,38866,25409],{"x":152,"y":4184,"style":10248},[163,38868],{"x1":1830,"y1":11114,"x2":2619,"y2":11114,"stroke":177,"strokeWidth":194},[150,38870,38872],{"x":152,"y":38871,"style":183},"302","inline = 100cqi",[163,38874],{"x1":4147,"y1":5144,"x2":4147,"y2":3112,"stroke":177,"strokeWidth":194},[150,38876,38878],{"x":4196,"y":4166,"style":183,"transform":38877},"rotate(-90 92 164)","block = 100cqb",[150,38880,38883],{"x":38881,"y":1787,"style":38882},"612","text-anchor:start;fill:currentColor;font:11px ui-monospace,monospace;opacity:0.8","cqmin = min",[150,38885,38886],{"x":38881,"y":6673,"style":38882},"cqmax = max",[14,38888,38889],{},"The component below is self-contained: drop it into an empty HTML file and resize the browser. Both cards use identical CSS, yet each scales to the column it lands in.",[281,38891,38893],{"className":283,"code":38892,"language":285,"meta":286,"style":286},"\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\u003Cstyle>\n  .layout {\n    display: grid;\n    grid-template-columns: 1fr 3fr; \u002F* sidebar, then main *\u002F\n    gap: 2rem;\n    padding: 2rem;\n  }\n\n  \u002F* Each slot becomes a query container so cq* units read ITS size, not the viewport *\u002F\n  .slot {\n    container-type: inline-size;\n  }\n\n  .card {\n    \u002F* Padding scales with the container's inline size, clamped to sane bounds *\u002F\n    padding: clamp(0.75rem, 4cqi, 2rem);\n    \u002F* Gap also tracks inline size, but never collapses below 0.5rem *\u002F\n    display: grid;\n    gap: clamp(0.5rem, 2cqi, 1.25rem);\n    border-radius: clamp(8px, 2cqi, 18px);\n    background: #11141c;\n    color: #e8ecf4;\n  }\n\n  .card h2 {\n    \u002F* Fluid heading: floor 1.1rem, scales at 6% of inline size, ceiling 2rem *\u002F\n    font-size: clamp(1.1rem, 6cqi, 2rem);\n    line-height: 1.15;\n    margin: 0;\n  }\n\n  .card p {\n    \u002F* cqmin keeps body text sensible even in a tall, narrow container *\u002F\n    font-size: clamp(0.9rem, 1.6cqmin + 0.6rem, 1.05rem);\n    margin: 0;\n    opacity: 0.85;\n  }\n\n  \u002F* cqb (block axis) used for a top accent bar that grows with container height *\u002F\n  .card::before {\n    content: \"\";\n    display: block;\n    height: clamp(3px, 1cqb, 10px);\n    background: #7aa2ff;\n    border-radius: 999px;\n  }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv class=\"layout\">\n    \u003Cdiv class=\"slot\">\n      \u003Carticle class=\"card\">\n        \u003Ch2>Sidebar card\u003C\u002Fh2>\n        \u003Cp>Narrow container: cqi is small, so padding and heading stay compact.\u003C\u002Fp>\n      \u003C\u002Farticle>\n    \u003C\u002Fdiv>\n    \u003Cdiv class=\"slot\">\n      \u003Carticle class=\"card\">\n        \u003Ch2>Main column card\u003C\u002Fh2>\n        \u003Cp>Wide container: the same cqi rules produce larger type, gaps and padding.\u003C\u002Fp>\n      \u003C\u002Farticle>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,38894,38895,38905,38919,38927,38941,38961,38969,38976,38986,39005,39017,39029,39033,39037,39042,39048,39054,39058,39062,39068,39073,39101,39106,39116,39144,39172,39182,39192,39196,39200,39209,39214,39244,39256,39267,39271,39275,39284,39289,39324,39334,39344,39348,39352,39357,39364,39374,39384,39413,39423,39435,39439,39447,39455,39463,39478,39492,39506,39519,39532,39540,39548,39562,39576,39589,39602,39610,39618,39626,39634],{"__ignoreMap":286},[290,38896,38897,38899,38901,38903],{"class":163,"line":292},[290,38898,8982],{"class":295},[290,38900,35913],{"class":299},[290,38902,8988],{"class":303},[290,38904,327],{"class":295},[290,38906,38907,38909,38911,38913,38915,38917],{"class":163,"line":330},[290,38908,296],{"class":295},[290,38910,285],{"class":299},[290,38912,8999],{"class":303},[290,38914,307],{"class":295},[290,38916,9004],{"class":310},[290,38918,327],{"class":295},[290,38920,38921,38923,38925],{"class":163,"line":337},[290,38922,296],{"class":295},[290,38924,9013],{"class":299},[290,38926,327],{"class":295},[290,38928,38929,38931,38933,38935,38937,38939],{"class":163,"line":364},[290,38930,296],{"class":295},[290,38932,9022],{"class":299},[290,38934,9025],{"class":303},[290,38936,307],{"class":295},[290,38938,9030],{"class":310},[290,38940,327],{"class":295},[290,38942,38943,38945,38947,38949,38951,38953,38955,38957,38959],{"class":163,"line":386},[290,38944,296],{"class":295},[290,38946,9022],{"class":299},[290,38948,35962],{"class":303},[290,38950,307],{"class":295},[290,38952,35967],{"class":310},[290,38954,35970],{"class":303},[290,38956,307],{"class":295},[290,38958,35975],{"class":310},[290,38960,327],{"class":295},[290,38962,38963,38965,38967],{"class":163,"line":408},[290,38964,296],{"class":295},[290,38966,1430],{"class":299},[290,38968,327],{"class":295},[290,38970,38971,38974],{"class":163,"line":428},[290,38972,38973],{"class":303},"  .layout",[290,38975,450],{"class":295},[290,38977,38978,38980,38982,38984],{"class":163,"line":517},[290,38979,34590],{"class":461},[290,38981,465],{"class":295},[290,38983,9147],{"class":461},[290,38985,471],{"class":295},[290,38987,38988,38990,38992,38994,38996,38998,39000,39002],{"class":163,"line":523},[290,38989,36011],{"class":461},[290,38991,465],{"class":295},[290,38993,468],{"class":461},[290,38995,11964],{"class":541},[290,38997,19770],{"class":461},[290,38999,11964],{"class":541},[290,39001,828],{"class":295},[290,39003,39004],{"class":455},"\u002F* sidebar, then main *\u002F\n",[290,39006,39007,39009,39011,39013,39015],{"class":163,"line":532},[290,39008,36027],{"class":461},[290,39010,465],{"class":295},[290,39012,194],{"class":461},[290,39014,801],{"class":541},[290,39016,471],{"class":295},[290,39018,39019,39021,39023,39025,39027],{"class":163,"line":551},[290,39020,36040],{"class":461},[290,39022,465],{"class":295},[290,39024,194],{"class":461},[290,39026,801],{"class":541},[290,39028,471],{"class":295},[290,39030,39031],{"class":163,"line":586},[290,39032,771],{"class":295},[290,39034,39035],{"class":163,"line":602},[290,39036,334],{"emptyLinePlaceholder":333},[290,39038,39039],{"class":163,"line":617},[290,39040,39041],{"class":455},"  \u002F* Each slot becomes a query container so cq* units read ITS size, not the viewport *\u002F\n",[290,39043,39044,39046],{"class":163,"line":623},[290,39045,26167],{"class":303},[290,39047,450],{"class":295},[290,39049,39050,39052],{"class":163,"line":628},[290,39051,37025],{"class":461},[290,39053,25565],{"class":295},[290,39055,39056],{"class":163,"line":634},[290,39057,771],{"class":295},[290,39059,39060],{"class":163,"line":649},[290,39061,334],{"emptyLinePlaceholder":333},[290,39063,39064,39066],{"class":163,"line":660},[290,39065,9083],{"class":303},[290,39067,450],{"class":295},[290,39069,39070],{"class":163,"line":688},[290,39071,39072],{"class":455},"    \u002F* Padding scales with the container's inline size, clamped to sane bounds *\u002F\n",[290,39074,39075,39077,39079,39081,39083,39085,39087,39089,39091,39093,39095,39097,39099],{"class":163,"line":693},[290,39076,36040],{"class":461},[290,39078,465],{"class":295},[290,39080,11555],{"class":461},[290,39082,484],{"class":295},[290,39084,823],{"class":461},[290,39086,801],{"class":541},[290,39088,569],{"class":295},[290,39090,249],{"class":461},[290,39092,11404],{"class":541},[290,39094,569],{"class":295},[290,39096,194],{"class":461},[290,39098,801],{"class":541},[290,39100,500],{"class":295},[290,39102,39103],{"class":163,"line":698},[290,39104,39105],{"class":455},"    \u002F* Gap also tracks inline size, but never collapses below 0.5rem *\u002F\n",[290,39107,39108,39110,39112,39114],{"class":163,"line":704},[290,39109,34590],{"class":461},[290,39111,465],{"class":295},[290,39113,9147],{"class":461},[290,39115,471],{"class":295},[290,39117,39118,39120,39122,39124,39126,39128,39130,39132,39134,39136,39138,39140,39142],{"class":163,"line":710},[290,39119,36027],{"class":461},[290,39121,465],{"class":295},[290,39123,11555],{"class":461},[290,39125,484],{"class":295},[290,39127,798],{"class":461},[290,39129,801],{"class":541},[290,39131,569],{"class":295},[290,39133,194],{"class":461},[290,39135,11404],{"class":541},[290,39137,569],{"class":295},[290,39139,35120],{"class":461},[290,39141,801],{"class":541},[290,39143,500],{"class":295},[290,39145,39146,39148,39150,39152,39154,39156,39158,39160,39162,39164,39166,39168,39170],{"class":163,"line":717},[290,39147,12759],{"class":461},[290,39149,465],{"class":295},[290,39151,11555],{"class":461},[290,39153,484],{"class":295},[290,39155,176],{"class":461},[290,39157,674],{"class":541},[290,39159,569],{"class":295},[290,39161,194],{"class":461},[290,39163,11404],{"class":541},[290,39165,569],{"class":295},[290,39167,17921],{"class":461},[290,39169,674],{"class":541},[290,39171,500],{"class":295},[290,39173,39174,39176,39178,39180],{"class":163,"line":730},[290,39175,9124],{"class":461},[290,39177,465],{"class":295},[290,39179,36057],{"class":461},[290,39181,471],{"class":295},[290,39183,39184,39186,39188,39190],{"class":163,"line":742},[290,39185,36064],{"class":461},[290,39187,465],{"class":295},[290,39189,36069],{"class":461},[290,39191,471],{"class":295},[290,39193,39194],{"class":163,"line":768},[290,39195,771],{"class":295},[290,39197,39198],{"class":163,"line":774},[290,39199,334],{"emptyLinePlaceholder":333},[290,39201,39202,39204,39207],{"class":163,"line":779},[290,39203,9083],{"class":303},[290,39205,39206],{"class":299}," h2",[290,39208,450],{"class":295},[290,39210,39211],{"class":163,"line":784},[290,39212,39213],{"class":455},"    \u002F* Fluid heading: floor 1.1rem, scales at 6% of inline size, ceiling 2rem *\u002F\n",[290,39215,39216,39219,39221,39223,39225,39228,39230,39232,39234,39236,39238,39240,39242],{"class":163,"line":812},[290,39217,39218],{"class":461},"    font-size",[290,39220,465],{"class":295},[290,39222,11555],{"class":461},[290,39224,484],{"class":295},[290,39226,39227],{"class":461},"1.1",[290,39229,801],{"class":541},[290,39231,569],{"class":295},[290,39233,5901],{"class":461},[290,39235,11404],{"class":541},[290,39237,569],{"class":295},[290,39239,194],{"class":461},[290,39241,801],{"class":541},[290,39243,500],{"class":295},[290,39245,39246,39249,39251,39254],{"class":163,"line":860},[290,39247,39248],{"class":461},"    line-height",[290,39250,465],{"class":295},[290,39252,39253],{"class":461},"1.15",[290,39255,471],{"class":295},[290,39257,39258,39261,39263,39265],{"class":163,"line":865},[290,39259,39260],{"class":461},"    margin",[290,39262,465],{"class":295},[290,39264,487],{"class":461},[290,39266,471],{"class":295},[290,39268,39269],{"class":163,"line":871},[290,39270,771],{"class":295},[290,39272,39273],{"class":163,"line":880},[290,39274,334],{"emptyLinePlaceholder":333},[290,39276,39277,39279,39282],{"class":163,"line":896},[290,39278,9083],{"class":303},[290,39280,39281],{"class":299}," p",[290,39283,450],{"class":295},[290,39285,39286],{"class":163,"line":4734},[290,39287,39288],{"class":455},"    \u002F* cqmin keeps body text sensible even in a tall, narrow container *\u002F\n",[290,39290,39291,39293,39295,39297,39299,39301,39303,39305,39307,39309,39311,39314,39316,39318,39320,39322],{"class":163,"line":4742},[290,39292,39218],{"class":461},[290,39294,465],{"class":295},[290,39296,11555],{"class":461},[290,39298,484],{"class":295},[290,39300,3589],{"class":461},[290,39302,801],{"class":541},[290,39304,569],{"class":295},[290,39306,6296],{"class":461},[290,39308,38802],{"class":541},[290,39310,3592],{"class":1561},[290,39312,39313],{"class":461}," 0.6",[290,39315,801],{"class":541},[290,39317,569],{"class":295},[290,39319,21090],{"class":461},[290,39321,801],{"class":541},[290,39323,500],{"class":295},[290,39325,39326,39328,39330,39332],{"class":163,"line":4761},[290,39327,39260],{"class":461},[290,39329,465],{"class":295},[290,39331,487],{"class":461},[290,39333,471],{"class":295},[290,39335,39336,39338,39340,39342],{"class":163,"line":4766},[290,39337,733],{"class":461},[290,39339,465],{"class":295},[290,39341,17985],{"class":461},[290,39343,471],{"class":295},[290,39345,39346],{"class":163,"line":4786},[290,39347,771],{"class":295},[290,39349,39350],{"class":163,"line":9635},[290,39351,334],{"emptyLinePlaceholder":333},[290,39353,39354],{"class":163,"line":9641},[290,39355,39356],{"class":455},"  \u002F* cqb (block axis) used for a top accent bar that grows with container height *\u002F\n",[290,39358,39359,39362],{"class":163,"line":9647},[290,39360,39361],{"class":303},"  .card::before",[290,39363,450],{"class":295},[290,39365,39366,39368,39370,39372],{"class":163,"line":9665},[290,39367,21250],{"class":461},[290,39369,465],{"class":295},[290,39371,1916],{"class":310},[290,39373,471],{"class":295},[290,39375,39376,39378,39380,39382],{"class":163,"line":9683},[290,39377,34590],{"class":461},[290,39379,465],{"class":295},[290,39381,68],{"class":461},[290,39383,471],{"class":295},[290,39385,39386,39389,39391,39393,39395,39397,39399,39401,39403,39405,39407,39409,39411],{"class":163,"line":9688},[290,39387,39388],{"class":461},"    height",[290,39390,465],{"class":295},[290,39392,11555],{"class":461},[290,39394,484],{"class":295},[290,39396,1579],{"class":461},[290,39398,674],{"class":541},[290,39400,569],{"class":295},[290,39402,468],{"class":461},[290,39404,12282],{"class":541},[290,39406,569],{"class":295},[290,39408,836],{"class":461},[290,39410,674],{"class":541},[290,39412,500],{"class":295},[290,39414,39415,39417,39419,39421],{"class":163,"line":9694},[290,39416,9124],{"class":461},[290,39418,465],{"class":295},[290,39420,177],{"class":461},[290,39422,471],{"class":295},[290,39424,39425,39427,39429,39431,39433],{"class":163,"line":9700},[290,39426,12759],{"class":461},[290,39428,465],{"class":295},[290,39430,10465],{"class":461},[290,39432,674],{"class":541},[290,39434,471],{"class":295},[290,39436,39437],{"class":163,"line":9710},[290,39438,771],{"class":295},[290,39440,39441,39443,39445],{"class":163,"line":9716},[290,39442,431],{"class":295},[290,39444,1430],{"class":299},[290,39446,327],{"class":295},[290,39448,39449,39451,39453],{"class":163,"line":9738},[290,39450,431],{"class":295},[290,39452,9013],{"class":299},[290,39454,327],{"class":295},[290,39456,39457,39459,39461],{"class":163,"line":9748},[290,39458,296],{"class":295},[290,39460,9239],{"class":299},[290,39462,327],{"class":295},[290,39464,39465,39467,39469,39471,39473,39476],{"class":163,"line":9754},[290,39466,367],{"class":295},[290,39468,342],{"class":299},[290,39470,314],{"class":303},[290,39472,307],{"class":295},[290,39474,39475],{"class":310},"\"layout\"",[290,39477,327],{"class":295},[290,39479,39480,39482,39484,39486,39488,39490],{"class":163,"line":9760},[290,39481,4290],{"class":295},[290,39483,342],{"class":299},[290,39485,314],{"class":303},[290,39487,307],{"class":295},[290,39489,25483],{"class":310},[290,39491,327],{"class":295},[290,39493,39494,39496,39498,39500,39502,39504],{"class":163,"line":9777},[290,39495,36430],{"class":295},[290,39497,11445],{"class":299},[290,39499,314],{"class":303},[290,39501,307],{"class":295},[290,39503,9295],{"class":310},[290,39505,327],{"class":295},[290,39507,39508,39510,39512,39515,39517],{"class":163,"line":9787},[290,39509,36461],{"class":295},[290,39511,50],{"class":299},[290,39513,39514],{"class":295},">Sidebar card\u003C\u002F",[290,39516,50],{"class":299},[290,39518,327],{"class":295},[290,39520,39521,39523,39525,39528,39530],{"class":163,"line":9793},[290,39522,36461],{"class":295},[290,39524,14],{"class":299},[290,39526,39527],{"class":295},">Narrow container: cqi is small, so padding and heading stay compact.\u003C\u002F",[290,39529,14],{"class":299},[290,39531,327],{"class":295},[290,39533,39534,39536,39538],{"class":163,"line":9799},[290,39535,36493],{"class":295},[290,39537,11445],{"class":299},[290,39539,327],{"class":295},[290,39541,39542,39544,39546],{"class":163,"line":9805},[290,39543,36502],{"class":295},[290,39545,342],{"class":299},[290,39547,327],{"class":295},[290,39549,39550,39552,39554,39556,39558,39560],{"class":163,"line":9811},[290,39551,4290],{"class":295},[290,39553,342],{"class":299},[290,39555,314],{"class":303},[290,39557,307],{"class":295},[290,39559,25483],{"class":310},[290,39561,327],{"class":295},[290,39563,39564,39566,39568,39570,39572,39574],{"class":163,"line":9820},[290,39565,36430],{"class":295},[290,39567,11445],{"class":299},[290,39569,314],{"class":303},[290,39571,307],{"class":295},[290,39573,9295],{"class":310},[290,39575,327],{"class":295},[290,39577,39578,39580,39582,39585,39587],{"class":163,"line":9829},[290,39579,36461],{"class":295},[290,39581,50],{"class":299},[290,39583,39584],{"class":295},">Main column card\u003C\u002F",[290,39586,50],{"class":299},[290,39588,327],{"class":295},[290,39590,39591,39593,39595,39598,39600],{"class":163,"line":27197},[290,39592,36461],{"class":295},[290,39594,14],{"class":299},[290,39596,39597],{"class":295},">Wide container: the same cqi rules produce larger type, gaps and padding.\u003C\u002F",[290,39599,14],{"class":299},[290,39601,327],{"class":295},[290,39603,39604,39606,39608],{"class":163,"line":27202},[290,39605,36493],{"class":295},[290,39607,11445],{"class":299},[290,39609,327],{"class":295},[290,39611,39612,39614,39616],{"class":163,"line":27209},[290,39613,36502],{"class":295},[290,39615,342],{"class":299},[290,39617,327],{"class":295},[290,39619,39620,39622,39624],{"class":163,"line":27220},[290,39621,4315],{"class":295},[290,39623,342],{"class":299},[290,39625,327],{"class":295},[290,39627,39628,39630,39632],{"class":163,"line":27235},[290,39629,431],{"class":295},[290,39631,9239],{"class":299},[290,39633,327],{"class":295},[290,39635,39636,39638,39640],{"class":163,"line":27240},[290,39637,431],{"class":295},[290,39639,285],{"class":299},[290,39641,327],{"class":295},[50,39643,36534],{"id":36533},[14,39645,39646,39647,39649,39650,39653,39654,39657,39658,39660,39661,39663,39664,39666,39667,39670,39671,39673,39674,39676,39677,39679],{},"The unit that does the real work here is ",[18,39648,11404],{},". It is defined as 1% of the query container's ",[86,39651,39652],{},"inline"," size — the dimension along which text flows. In the default left-to-right, horizontal writing mode that is the container's width, so ",[18,39655,39656],{},"6cqi"," is 6% of the card's rendered width. Because both cards share the same CSS but live in differently sized ",[18,39659,25556],{}," containers, the engine resolves ",[18,39662,39656],{}," to a different pixel value for each, with no media query and no script. Wrapping every ",[18,39665,11404],{}," value in ",[18,39668,39669],{},"clamp(min, preferred, max)"," is what makes this production-safe: the middle argument supplies the fluid behavior while the floor and ceiling guarantee the component never produces unreadably small or comically large text. Note that ",[18,39672,25409],{}," only establishes the ",[86,39675,39652],{}," axis as a query dimension; ",[18,39678,12282],{}," still resolves against the element's natural block size, which is why the accent bar above scales gently rather than dramatically.",[50,39681,36574],{"id":36573},[14,39683,39684,39685,39687,39688,39690,39691,39693],{},"Under a vertical writing mode the physical and logical units diverge, and this is precisely why ",[18,39686,11404],{}," is the safer default. Add the rule below and the inline axis becomes vertical, so ",[18,39689,11404],{}," now tracks height while ",[18,39692,38792],{}," would still track width.",[281,39695,39697],{"className":438,"code":39696,"language":440,"meta":286,"style":286},"\u002F* RTL \u002F vertical writing: cqi follows the inline (now vertical) axis automatically *\u002F\n.slot {\n  container-type: inline-size;\n}\n\n.vertical .slot {\n  writing-mode: vertical-rl;\n}\n\n\u002F* This heading scales correctly in BOTH writing modes because it uses cqi, not cqw *\u002F\n.card h2 {\n  font-size: clamp(1.1rem, 6cqi, 2rem);\n}\n",[18,39698,39699,39704,39710,39716,39720,39724,39734,39746,39750,39754,39759,39767,39795],{"__ignoreMap":286},[290,39700,39701],{"class":163,"line":292},[290,39702,39703],{"class":455},"\u002F* RTL \u002F vertical writing: cqi follows the inline (now vertical) axis automatically *\u002F\n",[290,39705,39706,39708],{"class":163,"line":330},[290,39707,25556],{"class":303},[290,39709,450],{"class":295},[290,39711,39712,39714],{"class":163,"line":337},[290,39713,11509],{"class":461},[290,39715,25565],{"class":295},[290,39717,39718],{"class":163,"line":364},[290,39719,620],{"class":295},[290,39721,39722],{"class":163,"line":386},[290,39723,334],{"emptyLinePlaceholder":333},[290,39725,39726,39729,39732],{"class":163,"line":408},[290,39727,39728],{"class":303},".vertical",[290,39730,39731],{"class":303}," .slot",[290,39733,450],{"class":295},[290,39735,39736,39739,39741,39744],{"class":163,"line":428},[290,39737,39738],{"class":461},"  writing-mode",[290,39740,465],{"class":295},[290,39742,39743],{"class":461},"vertical-rl",[290,39745,471],{"class":295},[290,39747,39748],{"class":163,"line":517},[290,39749,620],{"class":295},[290,39751,39752],{"class":163,"line":523},[290,39753,334],{"emptyLinePlaceholder":333},[290,39755,39756],{"class":163,"line":532},[290,39757,39758],{"class":455},"\u002F* This heading scales correctly in BOTH writing modes because it uses cqi, not cqw *\u002F\n",[290,39760,39761,39763,39765],{"class":163,"line":551},[290,39762,11528],{"class":303},[290,39764,39206],{"class":299},[290,39766,450],{"class":295},[290,39768,39769,39771,39773,39775,39777,39779,39781,39783,39785,39787,39789,39791,39793],{"class":163,"line":586},[290,39770,17980],{"class":461},[290,39772,465],{"class":295},[290,39774,11555],{"class":461},[290,39776,484],{"class":295},[290,39778,39227],{"class":461},[290,39780,801],{"class":541},[290,39782,569],{"class":295},[290,39784,5901],{"class":461},[290,39786,11404],{"class":541},[290,39788,569],{"class":295},[290,39790,194],{"class":461},[290,39792,801],{"class":541},[290,39794,500],{"class":295},[290,39796,39797],{"class":163,"line":602},[290,39798,620],{"class":295},[14,39800,39801,39802,39805,39806,3041,39808,39810],{},"If you had written ",[18,39803,39804],{},"6cqw",", the heading would scale with physical width even after the text rotated to flow vertically, breaking the relationship between type size and the line it sits on. Using the logical ",[18,39807,11404],{},[18,39809,12282],{}," pair keeps fluid sizing correct for internationalized layouts without writing a second rule.",[50,39812,1299],{"id":1298},[14,39814,39815,39816,569,39818,569,39820,569,39822,569,39824,8393,39826,39828,39829,42],{},"Container query length units shipped alongside size container queries and are part of the same baseline: Chrome and Edge 105+, Safari 16.0+, and Firefox 110+ all resolve ",[18,39817,38792],{},[18,39819,38795],{},[18,39821,11404],{},[18,39823,12282],{},[18,39825,38802],{},[18,39827,38805],{},". There is no separate flag for the units once size queries are supported. For older engines the units degrade to their viewport equivalents only when no ancestor is a query container; in practice you should still ship a static fallback value first so unsupported browsers render a fixed size, as covered in the guide to ",[27,39830,37935],{"href":19560},[50,39832,1316],{"id":1315},[14,39834,39835,39838,39840,39841,39843,39844,39846,39847,39849],{},[62,39836,39837],{},"What is the difference between cqw and cqi?",[18,39839,38792],{}," is always 1% of the container's physical width, while ",[18,39842,11404],{}," is 1% of its inline size. In a standard horizontal writing mode they resolve to the same value, but ",[18,39845,11404],{}," follows the writing direction, so it maps to height under vertical writing modes. Prefer ",[18,39848,11404],{}," for logical, direction-agnostic layouts.",[14,39851,39852,39855,39856,39858,39859,39861],{},[62,39853,39854],{},"Why does my cqi value resolve to zero?","\nContainer query units only resolve against an ancestor that declares ",[18,39857,24401],{},". If no ancestor is a query container, the unit falls back to the small-viewport equivalent, and if the queried axis has no definite size the unit computes to zero. Set ",[18,39860,25409],{}," on the parent.",[14,39863,39864,39867,39868,39870,39871,39873],{},[62,39865,39866],{},"Can I use cqi for font sizing directly?","\nYes, but wrap it in ",[18,39869,12276],{}," so the text stays within a readable minimum and maximum. A bare ",[18,39872,11404],{}," font size scales without limit and becomes unreadable in very wide or very narrow containers.",[14,39875,39876,39879,39880,39882,39883,39885],{},[62,39877,39878],{},"Do container query units work without an @container rule?","\nYes. The length units resolve from the container's measured size independently of any ",[18,39881,12001],{}," block, so you can use ",[18,39884,11404],{}," for fluid sizing even when you write no conditional queries at all.",[50,39887,1391],{"id":1390},[1393,39889,39890,39899,39906,39911,39919],{},[1396,39891,39892,39894,39895,69,39897,42],{},[27,39893,26284],{"href":25340}," — the parent guide defining ",[18,39896,24401],{},[18,39898,12001],{},[1396,39900,39901,39905],{},[27,39902,39904],{"href":39903},"\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fnesting-and-naming-container-queries\u002F","Nesting and Naming Container Queries"," — control which ancestor a query and its units resolve against.",[1396,39907,39908,39910],{},[27,39909,37784],{"href":19560}," — ship static fallbacks for engines without container units.",[1396,39912,39913,39915,39916,39918],{},[27,39914,12361],{"href":11312}," — the bounding function that makes ",[18,39917,11404],{}," text production-safe.",[1396,39920,39921,39923],{},[27,39922,4901],{"href":4900}," — reuse container-relative tokens to scale animation timing.",[1430,39925,10109],{},{"title":286,"searchDepth":330,"depth":330,"links":39927},[39928,39929,39930,39931,39932,39933,39934,39935],{"id":35782,"depth":330,"text":35783},{"id":35808,"depth":330,"text":35809},{"id":4202,"depth":330,"text":4203},{"id":36533,"depth":330,"text":36534},{"id":36573,"depth":330,"text":36574},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"How cqw, cqh, cqi, cqb, cqmin and cqmax resolve against a query container, plus when to reach for cqi to drive fluid type and spacing.",{"seoTitle":39938,"datePublished":1447,"dateModified":1447,"faq":39939},"Container Query Units: cqi & cqb Guide",[39940,39942,39944,39946],{"q":39837,"a":39941},"cqw is always 1% of the container's width, while cqi is 1% of its inline size. In a standard horizontal writing mode they resolve identically, but cqi follows the writing direction, so it maps to height under vertical writing modes. Prefer cqi for logical, direction-agnostic layouts.",{"q":39854,"a":39943},"Container query units only resolve against an ancestor that declares container-type. If no ancestor is a query container, the unit falls back to the small viewport equivalent, and if the queried axis has no definite size the unit computes to zero. Set container-type: inline-size on the parent.",{"q":39866,"a":39945},"Yes, but wrap it in clamp() so the text stays within a readable minimum and maximum. A bare cqi font size scales without limit and becomes unreadable in very wide or very narrow containers.",{"q":39878,"a":39947},"Yes. The length units resolve from the container's measured size independently of any @container block, so you can use cqi for fluid sizing even when you write no conditional queries at all.","\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-query-units-cqi-cqb-explained",{"title":38759,"description":39936},"mastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-query-units-cqi-cqb-explained\u002Findex","rdCJ1vEEZ13B3Tp6wod-XWLJsjeJSVh5JE96OPrbacc",{"id":39953,"title":39954,"body":39955,"description":40873,"extension":1444,"meta":40874,"navigation":333,"path":40887,"seo":40888,"stem":40889,"__hash__":40890},"content\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-vs-media-queries-comparison\u002Findex.md","Container vs Media Queries Comparison: Architectural Decision Guide",{"type":7,"value":39956,"toc":40863},[39957,39960,39975,39980,39994,40038,40042,40054,40071,40188,40192,40200,40210,40232,40264,40268,40277,40280,40453,40457,40460,40517,40523,40638,40640,40691,40703,40707,40783,40785,40802,40808,40817,40832,40834,40860],[10,39958,39954],{"id":39959},"container-vs-media-queries-comparison-architectural-decision-guide",[14,39961,39962,39963,39965,39966,39968,39969,39971,39972,39974],{},"This technical reference isolates the architectural differences between viewport-dependent ",[18,39964,874],{}," rules and context-aware ",[18,39967,12001],{}," queries. It sits within the ",[27,39970,26284],{"href":25340}," guide, and for engineers building component-driven systems, selecting the correct query type directly impacts layout stability, render performance, and maintainability. When evaluating ",[27,39973,11296],{"href":5777}," for your stack, understanding the intrinsic sizing boundaries and cascade implications is essential for avoiding layout thrashing.",[14,39976,39977],{},[62,39978,39979],{},"Key Architectural Differences:",[1393,39981,39982,39985,39988,39991],{},[1396,39983,39984],{},"Viewport-centric vs. parent-context evaluation boundaries",[1396,39986,39987],{},"Performance trade-offs in layout recalculation",[1396,39989,39990],{},"Fallback strategies for unsupported environments",[1396,39992,39993],{},"Component isolation and design system scalability",[133,39995,140,39997,140,40000,140,40003,140,40006,140,40008,140,40011,140,40014,140,40017,140,40020,140,40023,140,40026,140,40028,140,40031,140,40034],{"viewBox":2588,"role":136,"ariaLabel":39996,"xmlns":138,"style":139},"Comparison of media query evaluating against the viewport and container query evaluating against a parent element",[142,39998,39999],{},"@media versus @container evaluation context",[146,40001,40002],{},"The left side shows a media query measuring the full viewport; the right shows a container query measuring a single parent element's inline-size.",[150,40004,40005],{"x":152,"y":153,"style":1781},"Evaluation context",[150,40007,874],{"x":4184,"y":4172,"style":5883},[150,40009,12001],{"x":40010,"y":4172,"style":5883},"534",[171,40012],{"x":4146,"y":5892,"width":40013,"height":2622,"rx":836,"fill":72,"stroke":167,"strokeWidth":194,"opacity":232},"292",[150,40015,40016],{"x":4184,"y":6642,"style":1808},"viewport",[171,40018],{"x":9105,"y":4158,"width":4199,"height":4195,"rx":176,"fill":177,"opacity":40019},"0.14",[150,40021,40022],{"x":4184,"y":17549,"style":183},"measures 100vw",[171,40024],{"x":40025,"y":5892,"width":40013,"height":2622,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":169},"388",[171,40027],{"x":26459,"y":4158,"width":29490,"height":4195,"rx":176,"fill":177,"opacity":11349,"stroke":167,"strokeWidth":168},[150,40029,40030],{"x":40010,"y":1787,"style":4181},"parent container",[150,40032,40033],{"x":40010,"y":174,"style":183},"measures inline-size",[150,40035,40037],{"x":152,"y":40036,"style":1808},"306","same cascade rules, different reference box",[50,40039,40041],{"id":40040},"core-evaluation-boundaries-cascade-behavior","Core Evaluation Boundaries & Cascade Behavior",[14,40043,40044,40045,40047,40048,40050,40051,40053],{},"The fundamental distinction lies in the evaluation context. ",[18,40046,874],{}," resolves breakpoints against the root viewport dimensions, while ",[18,40049,12001],{}," evaluates against the nearest ancestor explicitly declared with ",[18,40052,24401],{},". The CSS cascade order and specificity rules remain identical, but the evaluation context shifts from global to local.",[14,40055,40056,40057,569,40059,40061,40062,40064,40065,40067,40068,40070],{},"Intrinsic sizing (",[18,40058,38658],{},[18,40060,17909],{},") interacts differently with each. ",[18,40063,874],{}," ignores intrinsic parent constraints, whereas ",[18,40066,12001],{}," respects the computed inline-size of the query container, enabling true fluid adaptation. That same inline-size becomes the basis for the ",[27,40069,26013],{"href":11248},", which scale content relative to the container rather than the viewport.",[281,40072,40074],{"className":438,"code":40073,"language":440,"meta":286,"style":286},"\u002F* Baseline: Viewport-dependent *\u002F\n@media (min-width: 768px) {\n  .card {\n    flex-direction: row;\n  }\n}\n\n\u002F* Context-aware: Parent-dependent *\u002F\n.card-wrapper {\n  container-type: inline-size;\n  container-name: card;\n}\n\n@container card (min-width: 400px) {\n  .card {\n    flex-direction: row;\n  }\n}\n",[18,40075,40076,40081,40097,40103,40114,40118,40122,40126,40131,40138,40144,40150,40154,40158,40164,40170,40180,40184],{"__ignoreMap":286},[290,40077,40078],{"class":163,"line":292},[290,40079,40080],{"class":455},"\u002F* Baseline: Viewport-dependent *\u002F\n",[290,40082,40083,40085,40087,40089,40091,40093,40095],{"class":163,"line":330},[290,40084,874],{"class":541},[290,40086,3595],{"class":295},[290,40088,26114],{"class":461},[290,40090,465],{"class":295},[290,40092,38134],{"class":461},[290,40094,674],{"class":541},[290,40096,646],{"class":295},[290,40098,40099,40101],{"class":163,"line":337},[290,40100,9083],{"class":303},[290,40102,450],{"class":295},[290,40104,40105,40108,40110,40112],{"class":163,"line":364},[290,40106,40107],{"class":461},"    flex-direction",[290,40109,465],{"class":295},[290,40111,38025],{"class":461},[290,40113,471],{"class":295},[290,40115,40116],{"class":163,"line":386},[290,40117,771],{"class":295},[290,40119,40120],{"class":163,"line":408},[290,40121,620],{"class":295},[290,40123,40124],{"class":163,"line":428},[290,40125,334],{"emptyLinePlaceholder":333},[290,40127,40128],{"class":163,"line":517},[290,40129,40130],{"class":455},"\u002F* Context-aware: Parent-dependent *\u002F\n",[290,40132,40133,40136],{"class":163,"line":523},[290,40134,40135],{"class":303},".card-wrapper",[290,40137,450],{"class":295},[290,40139,40140,40142],{"class":163,"line":532},[290,40141,11509],{"class":461},[290,40143,25565],{"class":295},[290,40145,40146,40148],{"class":163,"line":551},[290,40147,25575],{"class":461},[290,40149,37983],{"class":295},[290,40151,40152],{"class":163,"line":586},[290,40153,620],{"class":295},[290,40155,40156],{"class":163,"line":602},[290,40157,334],{"emptyLinePlaceholder":333},[290,40159,40160,40162],{"class":163,"line":617},[290,40161,12001],{"class":541},[290,40163,37998],{"class":295},[290,40165,40166,40168],{"class":163,"line":623},[290,40167,9083],{"class":303},[290,40169,450],{"class":295},[290,40171,40172,40174,40176,40178],{"class":163,"line":628},[290,40173,40107],{"class":461},[290,40175,465],{"class":295},[290,40177,38025],{"class":461},[290,40179,471],{"class":295},[290,40181,40182],{"class":163,"line":634},[290,40183,771],{"class":295},[290,40185,40186],{"class":163,"line":649},[290,40187,620],{"class":295},[50,40189,40191],{"id":40190},"performance-implications-layout-thrashing","Performance Implications & Layout Thrashing",[14,40193,40194,40196,40197,40199],{},[18,40195,874],{}," triggers style recalculation only during viewport resize events. ",[18,40198,12001],{}," triggers recalculation whenever the parent container's inline-size changes, which can occur during JS-driven animations, drag operations, or dynamic DOM mutations.",[14,40201,40202,40203,40205,40206,40209],{},"Excessive nested ",[18,40204,12001],{}," rules can cascade layout recalculations, increasing main-thread work and triggering Cumulative Layout Shift (CLS). Disciplined ",[27,40207,40208],{"href":39903},"nesting and naming of container queries"," keeps evaluation scopes shallow and predictable. To mitigate this:",[3017,40211,40212,40221,40224],{},[1396,40213,29271,40214,2351,40217,40220],{},[18,40215,40216],{},"contain: layout",[18,40218,40219],{},"contain: strict"," to isolate repaint\u002Freflow boundaries.",[1396,40222,40223],{},"Flatten container hierarchies where possible.",[1396,40225,40226,40227,2351,40229,40231],{},"Use DevTools Performance panel to record layout events and filter by ",[18,40228,13462],{},[18,40230,32984],{}," to identify thrashing triggers.",[281,40233,40235],{"className":438,"code":40234,"language":440,"meta":286,"style":286},".card-wrapper {\n  container-type: inline-size;\n  contain: layout; \u002F* Isolates reflow scope *\u002F\n}\n",[18,40236,40237,40243,40249,40260],{"__ignoreMap":286},[290,40238,40239,40241],{"class":163,"line":292},[290,40240,40135],{"class":303},[290,40242,450],{"class":295},[290,40244,40245,40247],{"class":163,"line":330},[290,40246,11509],{"class":461},[290,40248,25565],{"class":295},[290,40250,40251,40254,40257],{"class":163,"line":337},[290,40252,40253],{"class":461},"  contain",[290,40255,40256],{"class":295},": layout; ",[290,40258,40259],{"class":455},"\u002F* Isolates reflow scope *\u002F\n",[290,40261,40262],{"class":163,"line":364},[290,40263,620],{"class":295},[50,40265,40267],{"id":40266},"fallback-architecture-progressive-enhancement","Fallback Architecture & Progressive Enhancement",[14,40269,40270,40271,40273,40274,40276],{},"Legacy environments require graceful degradation without duplicating style blocks. Use ",[18,40272,2086],{}," to conditionally apply container queries while preserving ",[18,40275,874],{}," as a mobile-first baseline.",[14,40278,40279],{},"Avoid style duplication by leveraging CSS custom properties for shared values. Note that polyfills introduce significant JS overhead and should only be considered for enterprise environments with strict legacy browser mandates.",[281,40281,40283],{"className":438,"code":40282,"language":440,"meta":286,"style":286},".card {\n  \u002F* Mobile-first baseline *\u002F\n  display: flex;\n  flex-direction: column;\n  gap: 1rem;\n}\n\n\u002F* Container query for modern browsers *\u002F\n@supports (container-type: inline-size) {\n  .card-wrapper {\n    container-type: inline-size;\n  }\n  @container (min-width: 400px) {\n    .card {\n      flex-direction: row;\n    }\n  }\n}\n\n\u002F* Viewport fallback for unsupported browsers *\u002F\n@media (min-width: 768px) {\n  .card {\n    flex-direction: row;\n  }\n}\n",[18,40284,40285,40291,40296,40306,40318,40330,40334,40338,40343,40353,40360,40366,40370,40376,40382,40392,40396,40400,40404,40408,40413,40429,40435,40445,40449],{"__ignoreMap":286},[290,40286,40287,40289],{"class":163,"line":292},[290,40288,11528],{"class":303},[290,40290,450],{"class":295},[290,40292,40293],{"class":163,"line":330},[290,40294,40295],{"class":455},"  \u002F* Mobile-first baseline *\u002F\n",[290,40297,40298,40300,40302,40304],{"class":163,"line":337},[290,40299,17742],{"class":461},[290,40301,465],{"class":295},[290,40303,9055],{"class":461},[290,40305,471],{"class":295},[290,40307,40308,40311,40313,40316],{"class":163,"line":364},[290,40309,40310],{"class":461},"  flex-direction",[290,40312,465],{"class":295},[290,40314,40315],{"class":461},"column",[290,40317,471],{"class":295},[290,40319,40320,40322,40324,40326,40328],{"class":163,"line":386},[290,40321,26819],{"class":461},[290,40323,465],{"class":295},[290,40325,468],{"class":461},[290,40327,801],{"class":541},[290,40329,471],{"class":295},[290,40331,40332],{"class":163,"line":408},[290,40333,620],{"class":295},[290,40335,40336],{"class":163,"line":428},[290,40337,334],{"emptyLinePlaceholder":333},[290,40339,40340],{"class":163,"line":517},[290,40341,40342],{"class":455},"\u002F* Container query for modern browsers *\u002F\n",[290,40344,40345,40347,40349,40351],{"class":163,"line":523},[290,40346,2086],{"class":541},[290,40348,3595],{"class":295},[290,40350,24401],{"class":461},[290,40352,26104],{"class":295},[290,40354,40355,40358],{"class":163,"line":532},[290,40356,40357],{"class":303},"  .card-wrapper",[290,40359,450],{"class":295},[290,40361,40362,40364],{"class":163,"line":551},[290,40363,37025],{"class":461},[290,40365,25565],{"class":295},[290,40367,40368],{"class":163,"line":586},[290,40369,771],{"class":295},[290,40371,40372,40374],{"class":163,"line":602},[290,40373,37040],{"class":541},[290,40375,12004],{"class":295},[290,40377,40378,40380],{"class":163,"line":617},[290,40379,26128],{"class":303},[290,40381,450],{"class":295},[290,40383,40384,40386,40388,40390],{"class":163,"line":623},[290,40385,38020],{"class":461},[290,40387,465],{"class":295},[290,40389,38025],{"class":461},[290,40391,471],{"class":295},[290,40393,40394],{"class":163,"line":628},[290,40395,8200],{"class":295},[290,40397,40398],{"class":163,"line":634},[290,40399,771],{"class":295},[290,40401,40402],{"class":163,"line":649},[290,40403,620],{"class":295},[290,40405,40406],{"class":163,"line":660},[290,40407,334],{"emptyLinePlaceholder":333},[290,40409,40410],{"class":163,"line":688},[290,40411,40412],{"class":455},"\u002F* Viewport fallback for unsupported browsers *\u002F\n",[290,40414,40415,40417,40419,40421,40423,40425,40427],{"class":163,"line":693},[290,40416,874],{"class":541},[290,40418,3595],{"class":295},[290,40420,26114],{"class":461},[290,40422,465],{"class":295},[290,40424,38134],{"class":461},[290,40426,674],{"class":541},[290,40428,646],{"class":295},[290,40430,40431,40433],{"class":163,"line":698},[290,40432,9083],{"class":303},[290,40434,450],{"class":295},[290,40436,40437,40439,40441,40443],{"class":163,"line":704},[290,40438,40107],{"class":461},[290,40440,465],{"class":295},[290,40442,38025],{"class":461},[290,40444,471],{"class":295},[290,40446,40447],{"class":163,"line":710},[290,40448,771],{"class":295},[290,40450,40451],{"class":163,"line":717},[290,40452,620],{"class":295},[50,40454,40456],{"id":40455},"implementation-decision-matrix","Implementation Decision Matrix",[14,40458,40459],{},"Select the query type based on architectural scope, not convenience.",[2250,40461,40462,40475],{},[2253,40463,40464],{},[2256,40465,40466,40469,40472],{},[2259,40467,40468],{},"Component Scope",[2259,40470,40471],{},"Recommended Query",[2259,40473,40474],{},"Rationale",[2269,40476,40477,40489,40501],{},[2256,40478,40479,40482,40486],{},[2274,40480,40481],{},"Global Layout (Header, Footer, Page Grid)",[2274,40483,40484],{},[18,40485,874],{},[2274,40487,40488],{},"Optimized for viewport breakpoints; minimal layout work",[2256,40490,40491,40494,40498],{},[2274,40492,40493],{},"Reusable Components (Cards, Nav, Tables)",[2274,40495,40496],{},[18,40497,12001],{},[2274,40499,40500],{},"Decouples from viewport; adapts to available space",[2256,40502,40503,40506,40509],{},[2274,40504,40505],{},"Hybrid Systems",[2274,40507,40508],{},"Both",[2274,40510,40511,40513,40514,40516],{},[18,40512,874],{}," for macro layout, ",[18,40515,12001],{}," for micro components",[14,40518,40519,40520,40522],{},"For exact property mapping and advanced syntax, reference the ",[27,40521,26284],{"href":25340}," documentation.",[281,40524,40526],{"className":438,"code":40525,"language":440,"meta":286,"style":286},"\u002F* Hybrid Implementation Pattern *\u002F\n.page-grid {\n  container-type: inline-size;\n}\n\n@media (min-width: 1024px) {\n  .page-grid {\n    grid-template-columns: 1fr 300px;\n  }\n}\n\n@container (min-width: 600px) {\n  .sidebar-widget {\n    display: block;\n  }\n}\n",[18,40527,40528,40533,40540,40546,40550,40554,40571,40578,40594,40598,40602,40606,40613,40620,40630,40634],{"__ignoreMap":286},[290,40529,40530],{"class":163,"line":292},[290,40531,40532],{"class":455},"\u002F* Hybrid Implementation Pattern *\u002F\n",[290,40534,40535,40538],{"class":163,"line":330},[290,40536,40537],{"class":303},".page-grid",[290,40539,450],{"class":295},[290,40541,40542,40544],{"class":163,"line":337},[290,40543,11509],{"class":461},[290,40545,25565],{"class":295},[290,40547,40548],{"class":163,"line":364},[290,40549,620],{"class":295},[290,40551,40552],{"class":163,"line":386},[290,40553,334],{"emptyLinePlaceholder":333},[290,40555,40556,40558,40560,40562,40564,40567,40569],{"class":163,"line":408},[290,40557,874],{"class":541},[290,40559,3595],{"class":295},[290,40561,26114],{"class":461},[290,40563,465],{"class":295},[290,40565,40566],{"class":461},"1024",[290,40568,674],{"class":541},[290,40570,646],{"class":295},[290,40572,40573,40576],{"class":163,"line":428},[290,40574,40575],{"class":303},"  .page-grid",[290,40577,450],{"class":295},[290,40579,40580,40582,40584,40586,40588,40590,40592],{"class":163,"line":517},[290,40581,36011],{"class":461},[290,40583,465],{"class":295},[290,40585,468],{"class":461},[290,40587,11964],{"class":541},[290,40589,33719],{"class":461},[290,40591,674],{"class":541},[290,40593,471],{"class":295},[290,40595,40596],{"class":163,"line":523},[290,40597,771],{"class":295},[290,40599,40600],{"class":163,"line":532},[290,40601,620],{"class":295},[290,40603,40604],{"class":163,"line":551},[290,40605,334],{"emptyLinePlaceholder":333},[290,40607,40608,40610],{"class":163,"line":586},[290,40609,12001],{"class":541},[290,40611,40612],{"class":295}," (min-width: 600px) {\n",[290,40614,40615,40618],{"class":163,"line":602},[290,40616,40617],{"class":303},"  .sidebar-widget",[290,40619,450],{"class":295},[290,40621,40622,40624,40626,40628],{"class":163,"line":617},[290,40623,34590],{"class":461},[290,40625,465],{"class":295},[290,40627,68],{"class":461},[290,40629,471],{"class":295},[290,40631,40632],{"class":163,"line":623},[290,40633,771],{"class":295},[290,40635,40636],{"class":163,"line":628},[290,40637,620],{"class":295},[50,40639,34625],{"id":34624},[2250,40641,40642,40652],{},[2253,40643,40644],{},[2256,40645,40646,40648,40650],{},[2259,40647,34634],{},[2259,40649,2264],{},[2259,40651,2267],{},[2269,40653,40654,40663,40671,40683],{},[2256,40655,40656,40658,40660],{},[2274,40657,2276],{},[2274,40659,29200],{},[2274,40661,40662],{},"Full support",[2256,40664,40665,40667,40669],{},[2274,40666,2287],{},[2274,40668,37592],{},[2274,40670,40662],{},[2256,40672,40673,40675,40678],{},[2274,40674,2297],{},[2274,40676,40677],{},"16.0+",[2274,40679,40680,40681],{},"Requires explicit ",[18,40682,24401],{},[2256,40684,40685,40687,40689],{},[2274,40686,2308],{},[2274,40688,29200],{},[2274,40690,40662],{},[14,40692,40693,40695,40696,40698,40699,40702],{},[62,40694,20503],{}," Requires explicit ",[18,40697,24401],{}," declaration on the parent. Partial support in older Safari versions may require ",[18,40700,40701],{},"-webkit"," prefixes for experimental flags. Polyfills exist but introduce significant JS overhead.",[50,40704,40706],{"id":40705},"common-issues-debugging-steps","Common Issues & Debugging Steps",[2250,40708,40709,40719],{},[2253,40710,40711],{},[2256,40712,40713,40715,40717],{},[2259,40714,2338],{},[2259,40716,3876],{},[2259,40718,8563],{},[2269,40720,40721,40744,40761],{},[2256,40722,40723,40726,40735],{},[2274,40724,40725],{},"Query not triggering despite correct syntax",[2274,40727,3930,40728,40730,40731,40734],{},[18,40729,24401],{}," on ancestor, or parent has ",[18,40732,40733],{},"0px"," inline-size due to flex\u002Fgrid constraints",[2274,40736,40737,40738,40740,40741,40743],{},"Verify ",[18,40739,25409],{}," is applied. Check computed styles for parent width. Apply ",[18,40742,40216],{}," to prevent size collapse.",[2256,40745,40746,40749,40755],{},[2274,40747,40748],{},"Excessive layout thrashing on resize",[2274,40750,40751,40752,40754],{},"Multiple nested ",[18,40753,12001],{}," rules evaluating simultaneously during drag\u002Fresize operations",[2274,40756,40757,40758,40760],{},"Flatten container hierarchy. Apply ",[18,40759,40219],{}," where possible. Debounce JS-driven dimension changes.",[2256,40762,40763,40766,40774],{},[2274,40764,40765],{},"Fallback styles overriding container queries",[2274,40767,40768,40769,34299,40771,40773],{},"CSS cascade places ",[18,40770,874],{},[18,40772,12001],{},", or specificity mismatch",[2274,40775,40776,40777,40779,40780,40782],{},"Wrap fallbacks in ",[18,40778,2086],{}," or use lower specificity selectors. Ensure ",[18,40781,12001],{}," rules appear after baseline styles.",[50,40784,1316],{"id":1315},[14,40786,40787,40795,40796,40798,40799,40801],{},[62,40788,40789,40790,69,40792,40794],{},"Can I use ",[18,40791,12001],{},[18,40793,874],{}," on the same element?","\nYes. They evaluate independently. Use ",[18,40797,874],{}," for viewport-level layout shifts and ",[18,40800,12001],{}," for component-level context. Ensure cascade order prevents unintended overrides.",[14,40803,40804,40807],{},[62,40805,40806],{},"Do container queries replace media queries entirely?","\nNo. Media queries remain optimal for global layout, typography scaling, and viewport-specific breakpoints. Container queries excel at component portability and nested layout adaptation.",[14,40809,40810,40813,40814,40816],{},[62,40811,40812],{},"How do I debug a container query that isn't applying?","\nInspect the parent element for ",[18,40815,24401],{},". Verify inline-size is non-zero. Use DevTools' Container Query panel to visualize evaluation boundaries and check for specificity conflicts.",[14,40818,40819,40825,40826,40828,40829,40831],{},[62,40820,40821,40822,40824],{},"What is the performance cost of using many ",[18,40823,12001],{}," rules?","\nEach ",[18,40827,12001],{}," evaluation requires layout recalculation when the parent resizes. Limit to 3-5 per component, use ",[18,40830,40216],{},", and avoid deeply nested container hierarchies to maintain 60fps rendering.",[50,40833,1391],{"id":1390},[1393,40835,40836,40842,40847,40855],{},[1396,40837,40838,40841],{},[27,40839,40840],{"href":11248},"Container Query Units cqi and cqb"," — size content against the container box you chose here.",[1396,40843,40844,40846],{},[27,40845,39904],{"href":39903}," — keep multi-level query scopes shallow and collision-free.",[1396,40848,40849,40851,40852,40854],{},[27,40850,26284],{"href":25340}," — the parent guide for the full ",[18,40853,12001],{}," reference.",[1396,40856,40857,40859],{},[27,40858,8757],{"href":8756}," — apply the same layout-recalculation discipline to motion.",[1430,40861,40862],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":286,"searchDepth":330,"depth":330,"links":40864},[40865,40866,40867,40868,40869,40870,40871,40872],{"id":40040,"depth":330,"text":40041},{"id":40190,"depth":330,"text":40191},{"id":40266,"depth":330,"text":40267},{"id":40455,"depth":330,"text":40456},{"id":34624,"depth":330,"text":34625},{"id":40705,"depth":330,"text":40706},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Container vs media queries: architectural comparison of intrinsic sizing boundaries, cascade implications, and when to use each for component-driven layouts.",{"seoTitle":40875,"datePublished":1447,"dateModified":1447,"faq":40876},"Container vs Media Queries: Decision Guide",[40877,40880,40882,40884],{"q":40878,"a":40879},"Can I use @container and @media on the same element?","Yes. They evaluate independently. Use @media for viewport-level layout shifts and @container for component-level context, and ensure cascade order prevents unintended overrides.",{"q":40806,"a":40881},"No. Media queries remain optimal for global layout, typography scaling, and viewport-specific breakpoints. Container queries excel at component portability and nested layout adaptation.",{"q":40812,"a":40883},"Inspect the parent element for container-type, verify its inline-size is non-zero, and use the browser DevTools container query panel to visualize evaluation boundaries and check for specificity conflicts.",{"q":40885,"a":40886},"What is the performance cost of using many @container rules?","Each @container evaluation requires layout recalculation when the parent resizes. Limit to roughly 3 to 5 per component, use contain: layout, and avoid deeply nested container hierarchies to maintain 60fps rendering.","\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-vs-media-queries-comparison",{"title":39954,"description":40873},"mastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fcontainer-vs-media-queries-comparison\u002Findex","EkQ5OEkG5_VYwBfBbUs4HadIk2IcWhzwGeJIa2hm8xQ",{"id":40892,"title":40893,"body":40894,"description":41933,"extension":1444,"meta":41934,"navigation":333,"path":41946,"seo":41947,"stem":41948,"__hash__":41949},"content\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fhow-to-use-container-queries-in-production\u002Findex.md","How to Use Container Queries in Production: A Technical Reference",{"type":7,"value":40895,"toc":41923},[40896,40899,40908,40913,40934,40991,40993,40997,41005,41010,41035,41131,41133,41137,41152,41156,41193,41305,41307,41311,41314,41318,41340,41508,41526,41528,41532,41535,41540,41568,41573,41664,41666,41670,41720,41736,41738,41740,41852,41854,41856,41862,41873,41887,41896,41898,41920],[10,40897,40893],{"id":40898},"how-to-use-container-queries-in-production-a-technical-reference",[14,40900,40901,40902,40904,40905,40907],{},"A production-grade reference for implementing CSS container queries in component-driven architectures. This guide bridges foundational ",[27,40903,26284],{"href":25340}," with scalable deployment, focusing on containment boundaries, performance auditing, and graceful degradation. As engineering teams integrate ",[27,40906,11296],{"href":5777}," into modern design systems, precise scoping and fallback strategies become critical for maintaining layout stability and minimizing main-thread blocking.",[14,40909,40910],{},[62,40911,40912],{},"Key Implementation Priorities:",[1393,40914,40915,40922,40925,40931],{},[1396,40916,40917,40918,69,40920],{},"Define explicit containment boundaries using ",[18,40919,24401],{},[18,40921,26072],{},[1396,40923,40924],{},"Optimize rendering performance with CSS containment properties",[1396,40926,40927,40928,40930],{},"Implement progressive enhancement via ",[18,40929,2086],{}," feature detection",[1396,40932,40933],{},"Debug query evaluation and layout shifts with browser DevTools",[133,40935,140,40937,140,40940,140,40943,140,40946,140,40948,140,40952,140,40955,140,40958,140,40962,140,40964,140,40966,140,40970,140,40972,140,40975,140,40979,140,40982,140,40984,140,40986,140,40988],{"viewBox":32969,"role":136,"ariaLabel":40936,"xmlns":138,"style":139},"Production pipeline from declaring a container to scoped queries, fallback, and audit",[142,40938,40939],{},"Production container query pipeline",[146,40941,40942],{},"Four ordered stages: declare containment, write scoped queries, gate with a fallback, then audit performance.",[150,40944,40945],{"x":152,"y":2598,"style":1781},"Production pipeline",[171,40947],{"x":158,"y":2630,"width":8904,"height":4147,"rx":836,"fill":177,"opacity":22004,"stroke":167,"strokeWidth":468},[150,40949,40951],{"x":40950,"y":4158,"style":2606},"102","1. Declare",[150,40953,24401],{"x":40950,"y":40954,"style":183},"142",[171,40956],{"x":40957,"y":2630,"width":8904,"height":4147,"rx":836,"fill":177,"opacity":22004,"stroke":167,"strokeWidth":468},"204",[150,40959,40961],{"x":40960,"y":4158,"style":2606},"282","2. Query",[150,40963,12001],{"x":40960,"y":40954,"style":183},[171,40965],{"x":22584,"y":2630,"width":8904,"height":4147,"rx":836,"fill":177,"opacity":22004,"stroke":167,"strokeWidth":468},[150,40967,40969],{"x":40968,"y":4158,"style":2606},"462","3. Gate",[150,40971,2086],{"x":40968,"y":40954,"style":183},[171,40973],{"x":40974,"y":2630,"width":5123,"height":4147,"rx":836,"fill":177,"opacity":22004,"stroke":167,"strokeWidth":468},"564",[150,40976,40978],{"x":40977,"y":4158,"style":2606},"630","4. Audit",[150,40980,40981],{"x":40977,"y":40954,"style":4181},"CLS \u002F TBT",[163,40983],{"x1":2602,"y1":181,"x2":40957,"y2":181,"stroke":167,"strokeWidth":168},[163,40985],{"x1":152,"y1":181,"x2":22584,"y2":181,"stroke":167,"strokeWidth":168},[163,40987],{"x1":5887,"y1":181,"x2":40974,"y2":181,"stroke":167,"strokeWidth":168},[150,40989,40990],{"x":152,"y":209,"style":1808},"contain the scope, then enhance progressively",[47,40992],{},[50,40994,40996],{"id":40995},"establishing-containment-boundaries","Establishing Containment Boundaries",[14,40998,40999,41000,69,41002,41004],{},"Correctly defining ",[18,41001,24401],{},[18,41003,26072],{}," prevents layout thrashing, eliminates scope leakage, and establishes predictable query contexts for isolated UI components. In a responsive component architecture, containment must be explicit and minimal.",[14,41006,41007],{},[62,41008,41009],{},"Implementation Rules:",[1393,41011,41012,41021,41027,41032],{},[1396,41013,1499,41014,41016,41017,41020],{},[18,41015,25409],{}," for horizontal component scaling. Avoid ",[18,41018,41019],{},"size"," unless vertical containment is strictly required.",[1396,41022,41023,41024,41026],{},"Explicitly name containers (",[18,41025,26072],{},") to prevent global query collisions in complex component trees.",[1396,41028,29271,41029,41031],{},[18,41030,34746],{}," to isolate repaint\u002Freflow triggers from the rest of the DOM.",[1396,41033,41034],{},"Avoid applying containment to frequently mutating DOM nodes or elements with heavy JS-driven state.",[281,41036,41038],{"className":438,"code":41037,"language":440,"meta":286,"style":286},"\u002F* Container declaration block *\u002F\n.card-component {\n  container-type: inline-size;\n  container-name: card;\n  contain: layout style;\n}\n\n\u002F* Named container scoping pattern *\u002F\n@container card (min-width: 400px) {\n  .card-component__meta {\n    display: flex;\n    gap: 1rem;\n  }\n}\n",[18,41039,41040,41045,41052,41058,41064,41075,41079,41083,41088,41094,41101,41111,41123,41127],{"__ignoreMap":286},[290,41041,41042],{"class":163,"line":292},[290,41043,41044],{"class":455},"\u002F* Container declaration block *\u002F\n",[290,41046,41047,41050],{"class":163,"line":330},[290,41048,41049],{"class":303},".card-component",[290,41051,450],{"class":295},[290,41053,41054,41056],{"class":163,"line":337},[290,41055,11509],{"class":461},[290,41057,25565],{"class":295},[290,41059,41060,41062],{"class":163,"line":364},[290,41061,25575],{"class":461},[290,41063,37983],{"class":295},[290,41065,41066,41068,41071,41073],{"class":163,"line":386},[290,41067,40253],{"class":461},[290,41069,41070],{"class":295},": layout ",[290,41072,1430],{"class":461},[290,41074,471],{"class":295},[290,41076,41077],{"class":163,"line":408},[290,41078,620],{"class":295},[290,41080,41081],{"class":163,"line":428},[290,41082,334],{"emptyLinePlaceholder":333},[290,41084,41085],{"class":163,"line":517},[290,41086,41087],{"class":455},"\u002F* Named container scoping pattern *\u002F\n",[290,41089,41090,41092],{"class":163,"line":523},[290,41091,12001],{"class":541},[290,41093,37998],{"class":295},[290,41095,41096,41099],{"class":163,"line":532},[290,41097,41098],{"class":303},"  .card-component__meta",[290,41100,450],{"class":295},[290,41102,41103,41105,41107,41109],{"class":163,"line":551},[290,41104,34590],{"class":461},[290,41106,465],{"class":295},[290,41108,9055],{"class":461},[290,41110,471],{"class":295},[290,41112,41113,41115,41117,41119,41121],{"class":163,"line":586},[290,41114,36027],{"class":461},[290,41116,465],{"class":295},[290,41118,468],{"class":461},[290,41120,801],{"class":541},[290,41122,471],{"class":295},[290,41124,41125],{"class":163,"line":602},[290,41126,771],{"class":295},[290,41128,41129],{"class":163,"line":617},[290,41130,620],{"class":295},[47,41132],{},[50,41134,41136],{"id":41135},"query-evaluation-unit-mapping","Query Evaluation & Unit Mapping",[14,41138,41139,41140,41151],{},"Mapping ",[27,41141,41142,41143,569,41145,569,41147,569,41149,3914],{"href":11248},"container query units (",[18,41144,11404],{},[18,41146,12282],{},[18,41148,38792],{},[18,41150,38795],{}," to fluid typography and micro-interactions requires strict adherence to logical axis consistency. This ensures components scale predictably across internationalized layouts and varying writing modes.",[14,41153,41154],{},[62,41155,41009],{},[1393,41157,41158,41172,41178,41184],{},[1396,41159,41160,41161,41163,41164,41166,41167,3041,41169,41171],{},"Prefer ",[18,41162,11404],{}," (container query inline) and ",[18,41165,12282],{}," (container query block) over ",[18,41168,38792],{},[18,41170,38795],{}," for writing-mode readiness.",[1396,41173,41174,41175,41177],{},"Combine container units with ",[18,41176,12276],{}," to enforce hard upper\u002Flower bounds and prevent extreme scaling.",[1396,41179,41180,41181,41183],{},"Avoid deeply nested ",[18,41182,12001],{}," rules to reduce evaluation overhead and cascade complexity.",[1396,41185,41186,41187,569,41189,41192],{},"Leverage range syntax (",[18,41188,26114],{},[18,41190,41191],{},"max-width",") for cleaner, media-agnostic logic.",[281,41194,41196],{"className":438,"code":41195,"language":440,"meta":286,"style":286},"\u002F* Fluid typography scaling & micro-interaction threshold mapping *\u002F\n.card-component__title {\n  font-size: clamp(1rem, 2cqi + 0.5rem, 1.5rem);\n  transition: transform 0.2s ease;\n}\n\n@container card (min-width: 300px) {\n  .card-component__title:hover {\n    transform: translateX(4cqi);\n  }\n}\n",[18,41197,41198,41203,41210,41245,41259,41263,41267,41274,41281,41297,41301],{"__ignoreMap":286},[290,41199,41200],{"class":163,"line":292},[290,41201,41202],{"class":455},"\u002F* Fluid typography scaling & micro-interaction threshold mapping *\u002F\n",[290,41204,41205,41208],{"class":163,"line":330},[290,41206,41207],{"class":303},".card-component__title",[290,41209,450],{"class":295},[290,41211,41212,41214,41216,41218,41220,41222,41224,41226,41228,41230,41232,41235,41237,41239,41241,41243],{"class":163,"line":337},[290,41213,17980],{"class":461},[290,41215,465],{"class":295},[290,41217,11555],{"class":461},[290,41219,484],{"class":295},[290,41221,468],{"class":461},[290,41223,801],{"class":541},[290,41225,569],{"class":295},[290,41227,194],{"class":461},[290,41229,11404],{"class":541},[290,41231,3592],{"class":1561},[290,41233,41234],{"class":461}," 0.5",[290,41236,801],{"class":541},[290,41238,569],{"class":295},[290,41240,168],{"class":461},[290,41242,801],{"class":541},[290,41244,500],{"class":295},[290,41246,41247,41249,41251,41253,41255,41257],{"class":163,"line":364},[290,41248,526],{"class":461},[290,41250,1880],{"class":295},[290,41252,566],{"class":461},[290,41254,1886],{"class":541},[290,41256,545],{"class":461},[290,41258,471],{"class":295},[290,41260,41261],{"class":163,"line":386},[290,41262,620],{"class":295},[290,41264,41265],{"class":163,"line":408},[290,41266,334],{"emptyLinePlaceholder":333},[290,41268,41269,41271],{"class":163,"line":428},[290,41270,12001],{"class":541},[290,41272,41273],{"class":295}," card (min-width: 300px) {\n",[290,41275,41276,41279],{"class":163,"line":517},[290,41277,41278],{"class":303},"  .card-component__title:hover",[290,41280,450],{"class":295},[290,41282,41283,41285,41287,41289,41291,41293,41295],{"class":163,"line":523},[290,41284,745],{"class":461},[290,41286,465],{"class":295},[290,41288,27966],{"class":461},[290,41290,484],{"class":295},[290,41292,249],{"class":461},[290,41294,11404],{"class":541},[290,41296,500],{"class":295},[290,41298,41299],{"class":163,"line":532},[290,41300,771],{"class":295},[290,41302,41303],{"class":163,"line":551},[290,41304,620],{"class":295},[47,41306],{},[50,41308,41310],{"id":41309},"production-fallbacks-progressive-enhancement","Production Fallbacks & Progressive Enhancement",[14,41312,41313],{},"CSS container query fallbacks are mandatory for enterprise-grade deployments. Graceful degradation relies on feature detection, viewport-based baselines, and CSS custom property swapping.",[14,41315,41316],{},[62,41317,41009],{},[1393,41319,41320,41328,41334,41337],{},[1396,41321,41322,41323,37929,41325,41327],{},"Wrap ",[18,41324,12001],{},[18,41326,26207],{}," to gate unsupported browsers.",[1396,41329,41330,41331,41333],{},"Provide static viewport ",[18,41332,874],{}," queries as a baseline fallback for legacy environments.",[1396,41335,41336],{},"Use CSS variables to toggle between container and viewport logic without duplicating declarations.",[1396,41338,41339],{},"Restrict JavaScript polyfills to critical user journeys only; avoid global polyfill injection.",[281,41341,41343],{"className":438,"code":41342,"language":440,"meta":286,"style":286},"\u002F* Feature detection wrapper & viewport fallback mapping *\u002F\n.card-component {\n  container-type: inline-size;\n  container-name: card;\n  contain: layout style;\n}\n\n@supports (container-type: inline-size) {\n  @container card (min-width: 400px) {\n    .card-component__meta {\n      display: flex;\n      gap: 1rem;\n    }\n  }\n}\n\n\u002F* Viewport fallback for unsupported environments *\u002F\n@media (min-width: 400px) {\n  .no-cq-support .card-component__meta {\n    display: flex;\n    gap: 1rem;\n  }\n}\n",[18,41344,41345,41350,41356,41362,41368,41378,41382,41386,41396,41402,41409,41419,41431,41435,41439,41443,41447,41452,41468,41478,41488,41500,41504],{"__ignoreMap":286},[290,41346,41347],{"class":163,"line":292},[290,41348,41349],{"class":455},"\u002F* Feature detection wrapper & viewport fallback mapping *\u002F\n",[290,41351,41352,41354],{"class":163,"line":330},[290,41353,41049],{"class":303},[290,41355,450],{"class":295},[290,41357,41358,41360],{"class":163,"line":337},[290,41359,11509],{"class":461},[290,41361,25565],{"class":295},[290,41363,41364,41366],{"class":163,"line":364},[290,41365,25575],{"class":461},[290,41367,37983],{"class":295},[290,41369,41370,41372,41374,41376],{"class":163,"line":386},[290,41371,40253],{"class":461},[290,41373,41070],{"class":295},[290,41375,1430],{"class":461},[290,41377,471],{"class":295},[290,41379,41380],{"class":163,"line":408},[290,41381,620],{"class":295},[290,41383,41384],{"class":163,"line":428},[290,41385,334],{"emptyLinePlaceholder":333},[290,41387,41388,41390,41392,41394],{"class":163,"line":517},[290,41389,2086],{"class":541},[290,41391,3595],{"class":295},[290,41393,24401],{"class":461},[290,41395,26104],{"class":295},[290,41397,41398,41400],{"class":163,"line":523},[290,41399,37040],{"class":541},[290,41401,37998],{"class":295},[290,41403,41404,41407],{"class":163,"line":532},[290,41405,41406],{"class":303},"    .card-component__meta",[290,41408,450],{"class":295},[290,41410,41411,41413,41415,41417],{"class":163,"line":551},[290,41412,37053],{"class":461},[290,41414,465],{"class":295},[290,41416,9055],{"class":461},[290,41418,471],{"class":295},[290,41420,41421,41423,41425,41427,41429],{"class":163,"line":586},[290,41422,38032],{"class":461},[290,41424,465],{"class":295},[290,41426,468],{"class":461},[290,41428,801],{"class":541},[290,41430,471],{"class":295},[290,41432,41433],{"class":163,"line":602},[290,41434,8200],{"class":295},[290,41436,41437],{"class":163,"line":617},[290,41438,771],{"class":295},[290,41440,41441],{"class":163,"line":623},[290,41442,620],{"class":295},[290,41444,41445],{"class":163,"line":628},[290,41446,334],{"emptyLinePlaceholder":333},[290,41448,41449],{"class":163,"line":634},[290,41450,41451],{"class":455},"\u002F* Viewport fallback for unsupported environments *\u002F\n",[290,41453,41454,41456,41458,41460,41462,41464,41466],{"class":163,"line":649},[290,41455,874],{"class":541},[290,41457,3595],{"class":295},[290,41459,26114],{"class":461},[290,41461,465],{"class":295},[290,41463,2618],{"class":461},[290,41465,674],{"class":541},[290,41467,646],{"class":295},[290,41469,41470,41473,41476],{"class":163,"line":660},[290,41471,41472],{"class":303},"  .no-cq-support",[290,41474,41475],{"class":303}," .card-component__meta",[290,41477,450],{"class":295},[290,41479,41480,41482,41484,41486],{"class":163,"line":688},[290,41481,34590],{"class":461},[290,41483,465],{"class":295},[290,41485,9055],{"class":461},[290,41487,471],{"class":295},[290,41489,41490,41492,41494,41496,41498],{"class":163,"line":693},[290,41491,36027],{"class":461},[290,41493,465],{"class":295},[290,41495,468],{"class":461},[290,41497,801],{"class":541},[290,41499,471],{"class":295},[290,41501,41502],{"class":163,"line":698},[290,41503,771],{"class":295},[290,41505,41506],{"class":163,"line":704},[290,41507,620],{"class":295},[14,41509,41510],{},[86,41511,41512,41513,41516,41517,41520,41521,2351,41523,41525],{},"Note: Toggle the ",[18,41514,41515],{},".no-cq-support"," class via a lightweight feature detection script (",[18,41518,41519],{},"!CSS.supports('container-type', 'inline-size')",") on the ",[18,41522,2901],{},[18,41524,12854],{}," element.",[47,41527],{},[50,41529,41531],{"id":41530},"debugging-performance-auditing","Debugging & Performance Auditing",[14,41533,41534],{},"Identifying containment leaks, query evaluation bottlenecks, and cumulative layout shift (CLS) triggers requires systematic auditing. Production-ready container queries demand strict scope isolation and render budget management.",[14,41536,41537],{},[62,41538,41539],{},"Debugging Checklist:",[3017,41541,41542,41551,41557,41560,41565],{},[1396,41543,41544,41545,41547,41548,42],{},"Open Chromium DevTools → ",[62,41546,13462],{}," panel → Enable ",[62,41549,41550],{},"Container Queries",[1396,41552,41553,41554,41556],{},"Inspect active containers for correct ",[18,41555,26044],{}," resolution and query evaluation state.",[1396,41558,41559],{},"Run Lighthouse Performance audit; monitor CLS and Total Blocking Time (TBT).",[1396,41561,40737,41562,41564],{},[18,41563,34746],{}," is applied to prevent style recalculation bleed.",[1396,41566,41567],{},"Isolate query scopes to component boundaries to prevent cascade bloat.",[14,41569,41570],{},[62,41571,41572],{},"Performance Audit CSS:",[281,41574,41576],{"className":438,"code":41575,"language":440,"meta":286,"style":286},"\u002F* Audit: Force containment boundaries for layout shift tracking *\u002F\n.container-audit {\n  container-type: inline-size;\n  contain: strict; \u002F* Combines layout, style, paint, size *\u002F\n}\n\n\u002F* Audit: Disable query evaluation during heavy scroll\u002Fanimation *\u002F\n@media (prefers-reduced-motion: reduce) {\n  @container card (min-width: 300px) {\n    .card-component__title {\n      transition: none;\n    }\n  }\n}\n",[18,41577,41578,41583,41590,41596,41610,41614,41618,41623,41629,41635,41642,41652,41656,41660],{"__ignoreMap":286},[290,41579,41580],{"class":163,"line":292},[290,41581,41582],{"class":455},"\u002F* Audit: Force containment boundaries for layout shift tracking *\u002F\n",[290,41584,41585,41588],{"class":163,"line":330},[290,41586,41587],{"class":303},".container-audit",[290,41589,450],{"class":295},[290,41591,41592,41594],{"class":163,"line":337},[290,41593,11509],{"class":461},[290,41595,25565],{"class":295},[290,41597,41598,41600,41602,41605,41607],{"class":163,"line":364},[290,41599,40253],{"class":461},[290,41601,465],{"class":295},[290,41603,41604],{"class":461},"strict",[290,41606,828],{"class":295},[290,41608,41609],{"class":455},"\u002F* Combines layout, style, paint, size *\u002F\n",[290,41611,41612],{"class":163,"line":386},[290,41613,620],{"class":295},[290,41615,41616],{"class":163,"line":408},[290,41617,334],{"emptyLinePlaceholder":333},[290,41619,41620],{"class":163,"line":428},[290,41621,41622],{"class":455},"\u002F* Audit: Disable query evaluation during heavy scroll\u002Fanimation *\u002F\n",[290,41624,41625,41627],{"class":163,"line":517},[290,41626,874],{"class":541},[290,41628,877],{"class":295},[290,41630,41631,41633],{"class":163,"line":523},[290,41632,37040],{"class":541},[290,41634,41273],{"class":295},[290,41636,41637,41640],{"class":163,"line":532},[290,41638,41639],{"class":303},"    .card-component__title",[290,41641,450],{"class":295},[290,41643,41644,41646,41648,41650],{"class":163,"line":551},[290,41645,36625],{"class":461},[290,41647,465],{"class":295},[290,41649,72],{"class":461},[290,41651,471],{"class":295},[290,41653,41654],{"class":163,"line":586},[290,41655,8200],{"class":295},[290,41657,41658],{"class":163,"line":602},[290,41659,771],{"class":295},[290,41661,41662],{"class":163,"line":617},[290,41663,620],{"class":295},[47,41665],{},[50,41667,41669],{"id":41668},"browser-support-fallback-strategy","Browser Support & Fallback Strategy",[2250,41671,41672,41681],{},[2253,41673,41674],{},[2256,41675,41676,41678],{},[2259,41677,2261],{},[2259,41679,41680],{},"Support Status",[2269,41682,41683,41691,41698,41705,41712],{},[2256,41684,41685,41688],{},[2274,41686,41687],{},"Chrome 105+",[2274,41689,41690],{},"✅ Full",[2256,41692,41693,41696],{},[2274,41694,41695],{},"Edge 105+",[2274,41697,41690],{},[2256,41699,41700,41703],{},[2274,41701,41702],{},"Safari 16+",[2274,41704,41690],{},[2256,41706,41707,41710],{},[2274,41708,41709],{},"Firefox 110+",[2274,41711,41690],{},[2256,41713,41714,41717],{},[2274,41715,41716],{},"iOS Safari 15.4",[2274,41718,41719],{},"️ Partial (requires flags)",[14,41721,41722,41724,41725,2376,41727,41729,41730,41732,41733,41735],{},[62,41723,13313],{}," Gate all ",[18,41726,12001],{},[18,41728,26207],{},". Provide viewport-based ",[18,41731,874],{}," queries as the baseline. Apply a ",[18,41734,41515],{}," class via feature detection script for legacy routing. Avoid polyfills unless enterprise compliance mandates IE\u002Flegacy Safari support.",[47,41737],{},[50,41739,13329],{"id":13328},[2250,41741,41742,41752],{},[2253,41743,41744],{},[2256,41745,41746,41748,41750],{},[2259,41747,2338],{},[2259,41749,8560],{},[2259,41751,3879],{},[2269,41753,41754,41776,41798,41821],{},[2256,41755,41756,41761,41766],{},[2274,41757,41758],{},[62,41759,41760],{},"Container query not triggering",[2274,41762,3930,41763,41765],{},[18,41764,24401],{},", or parent has zero inline-size due to flex\u002Fgrid constraints.",[2274,41767,40737,41768,41770,41771,3041,41773,41775],{},[18,41769,25409],{}," is applied. Ensure explicit ",[18,41772,1748],{},[18,41774,26114],{},". Check DevTools Container panel for evaluation status.",[2256,41777,41778,41783,41786],{},[2274,41779,41780],{},[62,41781,41782],{},"Layout thrashing & performance degradation",[2274,41784,41785],{},"Over-containment on deeply nested or frequently updated DOM trees.",[2274,41787,41788,41789,41791,41792,41794,41795,41797],{},"Limit ",[18,41790,24401],{}," to isolated wrappers. Use ",[18,41793,34746],{},". Avoid containers on elements with ",[18,41796,13074],{}," or scroll listeners.",[2256,41799,41800,41805,41812],{},[2274,41801,41802],{},[62,41803,41804],{},"Fallback styles overriding container styles",[2274,41806,41807,41808,69,41810,42],{},"CSS specificity conflicts or incorrect cascade ordering between ",[18,41809,874],{},[18,41811,12001],{},[2274,41813,41814,41815,41817,41818,41820],{},"Place ",[18,41816,2086],{}," wrapper after base styles. Use explicit class toggling or ",[18,41819,12681],{}," to control cascade priority.",[2256,41822,41823,41835,41841],{},[2274,41824,41825],{},[62,41826,41827,41828,3041,41830,41832,41833],{},"Container units (",[18,41829,11404],{},[18,41831,12282],{},") resolving to ",[18,41834,487],{},[2274,41836,41837,41838,42],{},"Query evaluated before layout is established, or container uses ",[18,41839,41840],{},"display: contents",[2274,41842,41843,41844,41846,41847,3041,41849,41851],{},"Avoid ",[18,41845,41840],{}," on containers. Use ",[18,41848,37680],{},[18,41850,26114],{}," for intrinsic sizing. Defer heavy JS until layout stabilizes.",[47,41853],{},[50,41855,1316],{"id":1315},[14,41857,41858,41861],{},[62,41859,41860],{},"Should I use container queries instead of media queries in production?","\nNo. Container queries complement media queries. Use media queries for page-level layout shifts and container queries for component-level responsiveness. Combine both for resilient, scalable architectures.",[14,41863,41864,41867,41868,3041,41870,41872],{},[62,41865,41866],{},"How do I prevent container queries from causing layout shifts?","\nDefine explicit ",[18,41869,26114],{},[18,41871,37680],{}," on containers, apply CSS containment properties, and avoid dynamic content injection that alters container dimensions post-render. Validate with Lighthouse CLS audits.",[14,41874,41875,41883,41884,41886],{},[62,41876,41877,41878,569,41880,41882],{},"Are container query units (",[18,41879,11404],{},[18,41881,12282],{},") safe for production typography?","\nYes, when paired with ",[18,41885,12276],{}," for upper\u002Flower bounds. They provide predictable scaling relative to component context, but always test across writing modes and legacy fallbacks.",[14,41888,41889,41892,41893,41895],{},[62,41890,41891],{},"How do I debug container queries in production environments?","\nUse Chromium DevTools' Container Query panel to inspect active queries, container dimensions, and evaluation states. For production monitoring, log container dimensions via ",[18,41894,25390],{}," and track CLS metrics via your analytics pipeline.",[50,41897,1391],{"id":1390},[1393,41899,41900,41905,41910,41915],{},[1396,41901,41902,41904],{},[27,41903,37796],{"href":37795}," — choose the right query type before shipping.",[1396,41906,41907,41909],{},[27,41908,39904],{"href":39903}," — scope named containers cleanly in large component trees.",[1396,41911,41912,41914],{},[27,41913,26284],{"href":25340}," — the parent guide for the full syntax reference.",[1396,41916,41917,41919],{},[27,41918,8757],{"href":8756}," — pair containment auditing with compositor-friendly motion.",[1430,41921,41922],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":286,"searchDepth":330,"depth":330,"links":41924},[41925,41926,41927,41928,41929,41930,41931,41932],{"id":40995,"depth":330,"text":40996},{"id":41135,"depth":330,"text":41136},{"id":41309,"depth":330,"text":41310},{"id":41530,"depth":330,"text":41531},{"id":41668,"depth":330,"text":41669},{"id":13328,"depth":330,"text":13329},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Use CSS container queries in production: containment boundaries, performance auditing, graceful degradation, and design system integration patterns.",{"seoTitle":41935,"datePublished":1447,"dateModified":1447,"faq":41936},"Container Queries in Production: A Reference",[41937,41939,41941,41944],{"q":41860,"a":41938},"No. Container queries complement media queries. Use media queries for page-level layout shifts and container queries for component-level responsiveness, combining both for resilient, scalable architectures.",{"q":41866,"a":41940},"Define explicit min-width or min-height on containers, apply CSS containment properties, and avoid dynamic content injection that alters container dimensions post-render. Validate with Lighthouse CLS audits.",{"q":41942,"a":41943},"Are container query units (cqi, cqb) safe for production typography?","Yes, when paired with clamp() for upper and lower bounds. They provide predictable scaling relative to component context, but always test across writing modes and legacy fallbacks.",{"q":41891,"a":41945},"Use the Chromium DevTools container query panel to inspect active queries, container dimensions, and evaluation states. For production monitoring, log container dimensions via ResizeObserver and track CLS metrics through your analytics pipeline.","\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fhow-to-use-container-queries-in-production",{"title":40893,"description":41933},"mastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fhow-to-use-container-queries-in-production\u002Findex","f7X90DouhgnGHIES-pwT6VvDt4188SvizS5BswIUCLg",{"id":41951,"title":26284,"body":41952,"description":43163,"extension":1444,"meta":43164,"navigation":333,"path":43175,"seo":43176,"stem":43177,"__hash__":43178},"content\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Findex.md",{"type":7,"value":41953,"toc":43150},[41954,41957,41974,42015,42019,42033,42035,42039,42045,42073,42080,42184,42186,42192,42198,42255,42261,42379,42381,42385,42396,42423,42429,42540,42542,42546,42557,42740,42742,42744,42831,42840,42842,42846,42955,42957,42959,42973,42979,42991,42997,42999,43001,43059,43061,43063,43116,43118,43120,43148],[10,41955,26284],{"id":41956},"container-query-syntax-basics",[14,41958,41959,41960,41962,41963,41965,41966,41968,41969,69,41971,41973],{},"Modern component-driven architecture demands a shift from viewport-centric breakpoints to element-aware contexts. This guide breaks down the foundational syntax of the ",[18,41961,12001],{}," at-rule and sits within the broader ",[27,41964,11296],{"href":5777}," guide. By establishing containment directly on UI elements, you can decouple component styling from global layout shifts and ship self-contained, reusable modules. We’ll cover the core ",[18,41967,12001],{}," at-rule, ",[18,41970,24401],{},[18,41972,26072],{}," properties, and the new relative units that power responsive micro-interactions.",[133,41975,140,41977,140,41980,140,41983,140,41986,140,41989,140,41992,140,41995,140,41997,140,42000,140,42003,140,42006,140,42010],{"viewBox":2588,"role":136,"ariaLabel":41976,"xmlns":138,"style":139},"How a container query resolves from a queried element up to its containment context",[142,41978,41979],{},"Resolving a container query up the DOM tree",[146,41981,41982],{},"A queried element resolves its @container rule against the nearest ancestor that declares container-type.",[150,41984,41985],{"x":152,"y":2598,"style":154},"Query resolution path",[171,41987],{"x":1786,"y":4172,"width":4176,"height":41988,"rx":836,"fill":177,"opacity":22004,"stroke":177,"strokeWidth":168},"78",[150,41990,41991],{"x":152,"y":159,"style":5883},"ancestor: container-type: inline-size",[150,41993,41994],{"x":152,"y":5159,"style":12455},"container-name: card",[171,41996],{"x":1830,"y":8904,"width":2618,"height":5905,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},[150,41998,41999],{"x":152,"y":17549,"style":5915},"intermediate wrapper (no containment)",[171,42001],{"x":8947,"y":42002,"width":8947,"height":1786,"rx":176,"fill":72,"stroke":167,"strokeWidth":168},"238",[150,42004,42005],{"x":152,"y":220,"style":10262},"queried element",[150,42007,42009],{"x":152,"y":42008,"style":1799},"286","@container card (...)",[150,42011,42014],{"x":42012,"y":174,"style":42013},"610","text-anchor:end;fill:#7aa2ff;font:11px sans-serif","resolves upward",[14,42016,42017],{},[62,42018,14961],{},[1393,42020,42021,42024,42027,42030],{},[1396,42022,42023],{},"Understanding the paradigm shift from viewport to component-level responsiveness",[1396,42025,42026],{},"Establishing containment contexts without breaking layout flow",[1396,42028,42029],{},"Writing spec-compliant size and style queries",[1396,42031,42032],{},"Leveraging container-relative units for scalable UI components",[47,42034],{},[50,42036,42038],{"id":42037},"declaring-the-containment-context","Declaring the Containment Context",[14,42040,42041,42042,42044],{},"To query an element, you must first establish a containment context on its direct ancestor. The ",[18,42043,24401],{}," property dictates which dimensions the browser tracks, directly impacting rendering performance and query accuracy.",[1393,42046,42047,42052,42058,42067],{},[1396,42048,42049,42051],{},[18,42050,25409],{},": Tracks only the inline axis (width in horizontal writing modes). Highly performant, as it avoids block-size layout recalculations and is the recommended default.",[1396,42053,42054,42057],{},[18,42055,42056],{},"container-type: size",": Tracks both inline and block dimensions. Triggers more frequent layout recalculations; reserve for components where height directly dictates internal layout.",[1396,42059,42060,42062,42063,42066],{},[18,42061,26072],{},": Assigns an explicit identifier. Crucial for targeting specific containers in deeply nested DOM trees where multiple containment contexts exist; see ",[27,42064,42065],{"href":39903},"nesting and naming container queries"," for disambiguation strategies.",[1396,42068,42069,42072],{},[62,42070,42071],{},"Ancestor Rule:"," Containment must be declared on a direct or indirect parent of the queried element. The browser resolves queries up the DOM tree until it finds a matching named or typed container.",[14,42074,42075,42076,42079],{},"When deciding between viewport and element-level responsiveness, understanding the ",[27,42077,42078],{"href":37795},"Container vs media queries comparison"," clarifies when containment prevents unnecessary layout thrashing.",[281,42081,42083],{"className":438,"code":42082,"language":440,"meta":286,"style":286},"\u002F* Minimal DOM structure for context *\u002F\n\u002F* \u003Cdiv class=\"card-wrapper\">\u003Cdiv class=\"card-content\">...\u003C\u002Fdiv>\u003C\u002Fdiv> *\u002F\n\n.card-wrapper {\n  container-type: inline-size;\n  container-name: card;\n}\n\n@container card (min-width: 400px) {\n  .card-content {\n    display: grid;\n    grid-template-columns: 1fr 2fr;\n    gap: 1.5rem;\n  }\n}\n",[18,42084,42085,42090,42095,42099,42105,42111,42117,42121,42125,42131,42138,42148,42164,42176,42180],{"__ignoreMap":286},[290,42086,42087],{"class":163,"line":292},[290,42088,42089],{"class":455},"\u002F* Minimal DOM structure for context *\u002F\n",[290,42091,42092],{"class":163,"line":330},[290,42093,42094],{"class":455},"\u002F* \u003Cdiv class=\"card-wrapper\">\u003Cdiv class=\"card-content\">...\u003C\u002Fdiv>\u003C\u002Fdiv> *\u002F\n",[290,42096,42097],{"class":163,"line":337},[290,42098,334],{"emptyLinePlaceholder":333},[290,42100,42101,42103],{"class":163,"line":364},[290,42102,40135],{"class":303},[290,42104,450],{"class":295},[290,42106,42107,42109],{"class":163,"line":386},[290,42108,11509],{"class":461},[290,42110,25565],{"class":295},[290,42112,42113,42115],{"class":163,"line":408},[290,42114,25575],{"class":461},[290,42116,37983],{"class":295},[290,42118,42119],{"class":163,"line":428},[290,42120,620],{"class":295},[290,42122,42123],{"class":163,"line":517},[290,42124,334],{"emptyLinePlaceholder":333},[290,42126,42127,42129],{"class":163,"line":523},[290,42128,12001],{"class":541},[290,42130,37998],{"class":295},[290,42132,42133,42136],{"class":163,"line":532},[290,42134,42135],{"class":303},"  .card-content",[290,42137,450],{"class":295},[290,42139,42140,42142,42144,42146],{"class":163,"line":551},[290,42141,34590],{"class":461},[290,42143,465],{"class":295},[290,42145,9147],{"class":461},[290,42147,471],{"class":295},[290,42149,42150,42152,42154,42156,42158,42160,42162],{"class":163,"line":586},[290,42151,36011],{"class":461},[290,42153,465],{"class":295},[290,42155,468],{"class":461},[290,42157,11964],{"class":541},[290,42159,3290],{"class":461},[290,42161,11964],{"class":541},[290,42163,471],{"class":295},[290,42165,42166,42168,42170,42172,42174],{"class":163,"line":602},[290,42167,36027],{"class":461},[290,42169,465],{"class":295},[290,42171,168],{"class":461},[290,42173,801],{"class":541},[290,42175,471],{"class":295},[290,42177,42178],{"class":163,"line":617},[290,42179,771],{"class":295},[290,42181,42182],{"class":163,"line":623},[290,42183,620],{"class":295},[47,42185],{},[50,42187,1517,42189,42191],{"id":42188},"the-container-at-rule-structure",[18,42190,12001],{}," At-Rule Structure",[14,42193,42194,42195,42197],{},"Once a context is established, conditional styling follows a strict, spec-compliant syntax. The ",[18,42196,12001],{}," at-rule evaluates either physical dimensions or computed CSS values, enabling highly granular component logic.",[1393,42199,42200,42208,42230,42243],{},[1396,42201,42202,2694,42205],{},[62,42203,42204],{},"Standard Syntax:",[18,42206,42207],{},"@container \u003Cname> (\u003Ccondition>) { ... }",[1396,42209,42210,42213,42214,569,42216,569,42218,22253,42220,569,42222,569,42225,8393,42227,42],{},[62,42211,42212],{},"Size Conditions:"," Use standard length units (",[18,42215,674],{},[18,42217,801],{},[18,42219,11018],{},[18,42221,26114],{},[18,42223,42224],{},"max-height",[18,42226,38572],{},[18,42228,42229],{},"orientation",[1396,42231,42232,42235,42236,569,42239,42242],{},[62,42233,42234],{},"Style Queries:"," Evaluate custom properties (",[18,42237,42238],{},"--theme",[18,42240,42241],{},"--spacing",") and computed values. Ideal for theme switching, accessibility states, and component variants without JavaScript.",[1396,42244,42245,2694,42248,569,42250,569,42252,42254],{},[62,42246,42247],{},"Logical Operators:",[18,42249,11985],{},[18,42251,18318],{},[18,42253,10198],{}," enable complex condition grouping. Always group conditions logically to improve readability and maintainability.",[14,42256,42257,42258,42260],{},"Structuring queries this way aligns directly with scalable ",[27,42259,38098],{"href":38097}," in production workflows.",[281,42262,42264],{"className":438,"code":42263,"language":440,"meta":286,"style":286},"\u002F* Style Query: Evaluates computed custom property *\u002F\n@container style(--theme: dark) {\n  .component {\n    background: #1a1a1a;\n    color: #f0f0f0;\n    border-color: #333;\n  }\n}\n\n\u002F* Complex Size Query with Logical Operators *\u002F\n@container card (min-width: 300px) and (max-width: 600px) {\n  .component-header {\n    flex-direction: column;\n    align-items: flex-start;\n  }\n}\n",[18,42265,42266,42271,42278,42285,42296,42307,42318,42322,42326,42330,42335,42342,42349,42359,42371,42375],{"__ignoreMap":286},[290,42267,42268],{"class":163,"line":292},[290,42269,42270],{"class":455},"\u002F* Style Query: Evaluates computed custom property *\u002F\n",[290,42272,42273,42275],{"class":163,"line":330},[290,42274,12001],{"class":541},[290,42276,42277],{"class":295}," style(--theme: dark) {\n",[290,42279,42280,42283],{"class":163,"line":337},[290,42281,42282],{"class":303},"  .component",[290,42284,450],{"class":295},[290,42286,42287,42289,42291,42294],{"class":163,"line":364},[290,42288,9124],{"class":461},[290,42290,465],{"class":295},[290,42292,42293],{"class":461},"#1a1a1a",[290,42295,471],{"class":295},[290,42297,42298,42300,42302,42305],{"class":163,"line":386},[290,42299,36064],{"class":461},[290,42301,465],{"class":295},[290,42303,42304],{"class":461},"#f0f0f0",[290,42306,471],{"class":295},[290,42308,42309,42312,42314,42316],{"class":163,"line":408},[290,42310,42311],{"class":461},"    border-color",[290,42313,465],{"class":295},[290,42315,15974],{"class":461},[290,42317,471],{"class":295},[290,42319,42320],{"class":163,"line":428},[290,42321,771],{"class":295},[290,42323,42324],{"class":163,"line":517},[290,42325,620],{"class":295},[290,42327,42328],{"class":163,"line":523},[290,42329,334],{"emptyLinePlaceholder":333},[290,42331,42332],{"class":163,"line":532},[290,42333,42334],{"class":455},"\u002F* Complex Size Query with Logical Operators *\u002F\n",[290,42336,42337,42339],{"class":163,"line":551},[290,42338,12001],{"class":541},[290,42340,42341],{"class":295}," card (min-width: 300px) and (max-width: 600px) {\n",[290,42343,42344,42347],{"class":163,"line":586},[290,42345,42346],{"class":303},"  .component-header",[290,42348,450],{"class":295},[290,42350,42351,42353,42355,42357],{"class":163,"line":602},[290,42352,40107],{"class":461},[290,42354,465],{"class":295},[290,42356,40315],{"class":461},[290,42358,471],{"class":295},[290,42360,42361,42364,42366,42369],{"class":163,"line":617},[290,42362,42363],{"class":461},"    align-items",[290,42365,465],{"class":295},[290,42367,42368],{"class":461},"flex-start",[290,42370,471],{"class":295},[290,42372,42373],{"class":163,"line":623},[290,42374,771],{"class":295},[290,42376,42377],{"class":163,"line":628},[290,42378,620],{"class":295},[47,42380],{},[50,42382,42384],{"id":42383},"container-query-units-relative-sizing","Container Query Units & Relative Sizing",[14,42386,42387,42388,2351,42390,42392,42393,42395],{},"Container-relative units replace static ",[18,42389,674],{},[18,42391,801],{}," values with dynamic scaling factors tied directly to the containment context. These units enable proportional spacing, typography, and micro-interactions that adapt fluidly to available space; the dedicated breakdown of ",[27,42394,26013],{"href":11248}," covers their writing-mode behavior in depth.",[1393,42397,42398,42405,42416],{},[1396,42399,42400,1203,42402,42404],{},[18,42401,38792],{},[18,42403,38795],{},": 1% of container inline\u002Fblock size.",[1396,42406,42407,1203,42409,42411,42412,42415],{},[18,42408,11404],{},[18,42410,12282],{},": Logical equivalents that adapt to ",[18,42413,42414],{},"writing-mode"," (critical for RTL\u002Fvertical layouts).",[1396,42417,42418,1203,42420,42422],{},[18,42419,38802],{},[18,42421,38805],{},": Selects the smaller or larger dimension, perfect for responsive padding, border radii, and icon scaling.",[14,42424,42425,42426,42428],{},"Pairing these units with fluid scaling techniques like ",[27,42427,12361],{"href":11312}," creates self-adjusting components that scale proportionally without hard breakpoints.",[281,42430,42432],{"className":438,"code":42431,"language":440,"meta":286,"style":286},".component-title {\n  \u002F* Fluid typography scaling between 1rem and 2.5rem based on container width *\u002F\n  font-size: clamp(1rem, 2cqw + 0.5rem, 2.5rem);\n\n  \u002F* Logical spacing that adapts to container dimensions *\u002F\n  padding: 1cqi;\n  margin-block: 0.5cqb;\n  border-radius: min(1rem, 2cqmin);\n}\n",[18,42433,42434,42441,42446,42480,42484,42489,42501,42514,42536],{"__ignoreMap":286},[290,42435,42436,42439],{"class":163,"line":292},[290,42437,42438],{"class":303},".component-title",[290,42440,450],{"class":295},[290,42442,42443],{"class":163,"line":330},[290,42444,42445],{"class":455},"  \u002F* Fluid typography scaling between 1rem and 2.5rem based on container width *\u002F\n",[290,42447,42448,42450,42452,42454,42456,42458,42460,42462,42464,42466,42468,42470,42472,42474,42476,42478],{"class":163,"line":337},[290,42449,17980],{"class":461},[290,42451,465],{"class":295},[290,42453,11555],{"class":461},[290,42455,484],{"class":295},[290,42457,468],{"class":461},[290,42459,801],{"class":541},[290,42461,569],{"class":295},[290,42463,194],{"class":461},[290,42465,38792],{"class":541},[290,42467,3592],{"class":1561},[290,42469,41234],{"class":461},[290,42471,801],{"class":541},[290,42473,569],{"class":295},[290,42475,10258],{"class":461},[290,42477,801],{"class":541},[290,42479,500],{"class":295},[290,42481,42482],{"class":163,"line":364},[290,42483,334],{"emptyLinePlaceholder":333},[290,42485,42486],{"class":163,"line":386},[290,42487,42488],{"class":455},"  \u002F* Logical spacing that adapts to container dimensions *\u002F\n",[290,42490,42491,42493,42495,42497,42499],{"class":163,"line":408},[290,42492,10433],{"class":461},[290,42494,465],{"class":295},[290,42496,468],{"class":461},[290,42498,11404],{"class":541},[290,42500,471],{"class":295},[290,42502,42503,42506,42508,42510,42512],{"class":163,"line":428},[290,42504,42505],{"class":461},"  margin-block",[290,42507,465],{"class":295},[290,42509,798],{"class":461},[290,42511,12282],{"class":541},[290,42513,471],{"class":295},[290,42515,42516,42518,42520,42522,42524,42526,42528,42530,42532,42534],{"class":163,"line":517},[290,42517,1663],{"class":461},[290,42519,465],{"class":295},[290,42521,32765],{"class":461},[290,42523,484],{"class":295},[290,42525,468],{"class":461},[290,42527,801],{"class":541},[290,42529,569],{"class":295},[290,42531,194],{"class":461},[290,42533,38802],{"class":541},[290,42535,500],{"class":295},[290,42537,42538],{"class":163,"line":523},[290,42539,620],{"class":295},[47,42541],{},[50,42543,42545],{"id":42544},"production-ready-syntax-progressive-enhancement","Production-Ready Syntax & Progressive Enhancement",[14,42547,42548,42549,42551,42552,42554,42555,42],{},"Deploying container queries requires robust progressive enhancement. Wrap your syntax in ",[18,42550,26207],{}," to ensure graceful degradation. Provide baseline styles outside the block, and optionally chain ",[18,42553,874],{}," queries for legacy browsers. Avoid containment conflicts by ensuring container declarations don't interfere with CSS Grid\u002FFlexbox intrinsic sizing. Modern linters (Stylelint, PostCSS) support container query validation out-of-the-box. For deployment guidelines, refer to ",[27,42556,4037],{"href":4036},[281,42558,42560],{"className":438,"code":42559,"language":440,"meta":286,"style":286},"\u002F* Progressive Enhancement Pattern *\u002F\n@supports (container-type: inline-size) {\n  .card {\n    container-type: inline-size;\n    padding: 1rem;\n  }\n\n  @container (min-width: 300px) {\n    .card {\n      padding: 2rem;\n    }\n  }\n}\n\n@supports not (container-type: inline-size) {\n  \u002F* Baseline fallback for unsupported browsers *\u002F\n  .card {\n    padding: 1rem;\n  }\n\n  @media (min-width: 600px) {\n    .card {\n      padding: 2rem;\n    }\n  }\n}\n",[18,42561,42562,42567,42577,42583,42589,42601,42605,42609,42616,42622,42635,42639,42643,42647,42651,42663,42668,42674,42686,42690,42694,42710,42716,42728,42732,42736],{"__ignoreMap":286},[290,42563,42564],{"class":163,"line":292},[290,42565,42566],{"class":455},"\u002F* Progressive Enhancement Pattern *\u002F\n",[290,42568,42569,42571,42573,42575],{"class":163,"line":330},[290,42570,2086],{"class":541},[290,42572,3595],{"class":295},[290,42574,24401],{"class":461},[290,42576,26104],{"class":295},[290,42578,42579,42581],{"class":163,"line":337},[290,42580,9083],{"class":303},[290,42582,450],{"class":295},[290,42584,42585,42587],{"class":163,"line":364},[290,42586,37025],{"class":461},[290,42588,25565],{"class":295},[290,42590,42591,42593,42595,42597,42599],{"class":163,"line":386},[290,42592,36040],{"class":461},[290,42594,465],{"class":295},[290,42596,468],{"class":461},[290,42598,801],{"class":541},[290,42600,471],{"class":295},[290,42602,42603],{"class":163,"line":408},[290,42604,771],{"class":295},[290,42606,42607],{"class":163,"line":428},[290,42608,334],{"emptyLinePlaceholder":333},[290,42610,42611,42613],{"class":163,"line":517},[290,42612,37040],{"class":541},[290,42614,42615],{"class":295}," (min-width: 300px) {\n",[290,42617,42618,42620],{"class":163,"line":523},[290,42619,26128],{"class":303},[290,42621,450],{"class":295},[290,42623,42624,42627,42629,42631,42633],{"class":163,"line":532},[290,42625,42626],{"class":461},"      padding",[290,42628,465],{"class":295},[290,42630,194],{"class":461},[290,42632,801],{"class":541},[290,42634,471],{"class":295},[290,42636,42637],{"class":163,"line":551},[290,42638,8200],{"class":295},[290,42640,42641],{"class":163,"line":586},[290,42642,771],{"class":295},[290,42644,42645],{"class":163,"line":602},[290,42646,620],{"class":295},[290,42648,42649],{"class":163,"line":617},[290,42650,334],{"emptyLinePlaceholder":333},[290,42652,42653,42655,42657,42659,42661],{"class":163,"line":623},[290,42654,2086],{"class":541},[290,42656,2116],{"class":541},[290,42658,3595],{"class":295},[290,42660,24401],{"class":461},[290,42662,26104],{"class":295},[290,42664,42665],{"class":163,"line":628},[290,42666,42667],{"class":455},"  \u002F* Baseline fallback for unsupported browsers *\u002F\n",[290,42669,42670,42672],{"class":163,"line":634},[290,42671,9083],{"class":303},[290,42673,450],{"class":295},[290,42675,42676,42678,42680,42682,42684],{"class":163,"line":649},[290,42677,36040],{"class":461},[290,42679,465],{"class":295},[290,42681,468],{"class":461},[290,42683,801],{"class":541},[290,42685,471],{"class":295},[290,42687,42688],{"class":163,"line":660},[290,42689,771],{"class":295},[290,42691,42692],{"class":163,"line":688},[290,42693,334],{"emptyLinePlaceholder":333},[290,42695,42696,42698,42700,42702,42704,42706,42708],{"class":163,"line":693},[290,42697,26109],{"class":541},[290,42699,3595],{"class":295},[290,42701,26114],{"class":461},[290,42703,465],{"class":295},[290,42705,4176],{"class":461},[290,42707,674],{"class":541},[290,42709,646],{"class":295},[290,42711,42712,42714],{"class":163,"line":698},[290,42713,26128],{"class":303},[290,42715,450],{"class":295},[290,42717,42718,42720,42722,42724,42726],{"class":163,"line":704},[290,42719,42626],{"class":461},[290,42721,465],{"class":295},[290,42723,194],{"class":461},[290,42725,801],{"class":541},[290,42727,471],{"class":295},[290,42729,42730],{"class":163,"line":710},[290,42731,8200],{"class":295},[290,42733,42734],{"class":163,"line":717},[290,42735,771],{"class":295},[290,42737,42738],{"class":163,"line":730},[290,42739,620],{"class":295},[47,42741],{},[50,42743,13239],{"id":13238},[2250,42745,42746,42760],{},[2253,42747,42748],{},[2256,42749,42750,42752,42754,42756,42758],{},[2259,42751,3737],{},[2259,42753,24818],{},[2259,42755,2287],{},[2259,42757,2297],{},[2259,42759,2267],{},[2269,42761,42762,42777,42792,42811],{},[2256,42763,42764,42768,42770,42772,42774],{},[2274,42765,42766],{},[18,42767,25409],{},[2274,42769,29200],{},[2274,42771,37592],{},[2274,42773,8465],{},[2274,42775,42776],{},"Widely supported, safe for production",[2256,42778,42779,42783,42785,42787,42789],{},[2274,42780,42781],{},[18,42782,42056],{},[2274,42784,29200],{},[2274,42786,37592],{},[2274,42788,8465],{},[2274,42790,42791],{},"Supported, but monitor layout performance",[2256,42793,42794,42802,42804,42806,42808],{},[2274,42795,42796,42797,569,42799,42801],{},"Container Query Units (",[18,42798,38792],{},[18,42800,11404],{},", etc.)",[2274,42803,29200],{},[2274,42805,37592],{},[2274,42807,8465],{},[2274,42809,42810],{},"Fully supported across modern engines",[2256,42812,42813,42819,42821,42824,42826],{},[2274,42814,42815,42816,3914],{},"Style Queries (",[18,42817,42818],{},"@container style(...)",[2274,42820,24936],{},[2274,42822,42823],{},"Pending",[2274,42825,24939],{},[2274,42827,42828,42829,40930],{},"Partial support; use ",[18,42830,2086],{},[14,42832,42833,42836,42837,42839],{},[62,42834,42835],{},"Cross-Browser Note:"," Inline-size containment is stable across all major engines. Full size containment and style queries require progressive enhancement strategies. Always test with ",[18,42838,2086],{}," before shipping style-dependent logic.",[47,42841],{},[50,42843,42845],{"id":42844},"common-implementation-pitfalls-fixes","Common Implementation Pitfalls & Fixes",[2250,42847,42848,42858],{},[2253,42849,42850],{},[2256,42851,42852,42854,42856],{},[2259,42853,2338],{},[2259,42855,3876],{},[2259,42857,8563],{},[2269,42859,42860,42879,42898,42928,42939],{},[2256,42861,42862,42865,42871],{},[2274,42863,42864],{},"Layout thrashing on resize",[2274,42866,42867,42868,42870],{},"Applying ",[18,42869,41019],{}," containment to deeply nested or frequently repainted elements",[2274,42872,42873,42874,2401,42876,42878],{},"Default to ",[18,42875,26044],{},[18,42877,41019],{}," only when block dimension directly impacts internal layout.",[2256,42880,42881,42884,42889],{},[2274,42882,42883],{},"Fallback chains override container styles",[2274,42885,3930,42886,42888],{},[18,42887,2086],{}," nesting or incorrect cascade order",[2274,42890,42891,42892,42894,42895,42897],{},"Wrap CQs in ",[18,42893,2086],{},", place baseline styles outside, and use higher specificity or ",[18,42896,3900],{}," sparingly if needed.",[2256,42899,42900,42903,42916],{},[2274,42901,42902],{},"Inconsistent behavior in vertical writing modes",[2274,42904,42905,42906,3041,42908,42910,42911,3041,42913,42915],{},"Confusing ",[18,42907,1748],{},[18,42909,2722],{}," with logical ",[18,42912,39652],{},[18,42914,68],{}," axes",[2274,42917,1499,42918,3041,42920,69,42922,3041,42924,42927],{},[18,42919,11404],{},[18,42921,12282],{},[18,42923,26044],{},[18,42925,42926],{},"block-size"," for writing-mode agnostic layouts.",[2256,42929,42930,42933,42936],{},[2274,42931,42932],{},"Global layout shifts instead of component adaptations",[2274,42934,42935],{},"Overusing CQs for page-level routing or major structural changes",[2274,42937,42938],{},"Reserve CQs for component-level micro-adaptations. Use media queries for global layout shifts.",[2256,42940,42941,42944,42949],{},[2274,42942,42943],{},"Ambiguous matches in nested trees",[2274,42945,3930,42946,42948],{},[18,42947,26072],{}," when multiple containers share the same type",[2274,42950,42951,42952,42954],{},"Always assign explicit ",[18,42953,26072],{}," values in complex component hierarchies.",[47,42956],{},[50,42958,16218],{"id":16217},[14,42960,42961,42967,42969,42970,42972],{},[62,42962,19486,42963,69,42965,5734],{},[18,42964,25409],{},[18,42966,42056],{},[18,42968,26044],{}," establishes a query context based only on the inline dimension (width in horizontal writing modes), which is highly performant. ",[18,42971,41019],{}," establishes a context for both inline and block dimensions but triggers more frequent layout recalculations, making it less suitable for frequently resized elements.",[14,42974,42975,42978],{},[62,42976,42977],{},"Can I use container queries with CSS Grid and Flexbox?","\nYes. Container queries work seamlessly with Grid and Flexbox. Declare the containment context on a parent element, and queried children can adjust their internal grid\u002Fflex layouts based on the container's dimensions rather than the viewport.",[14,42980,42981,42984,42985,42987,42988,42990],{},[62,42982,42983],{},"How do I handle browsers that don't support container queries?","\nWrap your container query syntax in an ",[18,42986,26207],{}," block. Provide a baseline layout outside the block, and optionally use ",[18,42989,874],{}," queries as a fallback for viewport-based responsiveness in older browsers.",[14,42992,42993,42996],{},[62,42994,42995],{},"When should I use style queries instead of size queries?","\nUse style queries when component appearance depends on computed CSS properties, custom properties, or inherited states rather than physical dimensions. They are ideal for theme switching, accessibility preferences, and state-driven UI variations.",[47,42998],{},[50,43000,13396],{"id":8357},[3017,43002,43003,43014,43028,43037,43046],{},[1396,43004,43005,43008,43009,69,43011,43013],{},[62,43006,43007],{},"Inspect Containment Context:"," Open Chrome\u002FFirefox DevTools → Elements panel. Select the container element. In the Computed tab, verify ",[18,43010,24401],{},[18,43012,26072],{}," are applied.",[1396,43015,43016,13418,43019,43022,43023,3041,43025,43027],{},[62,43017,43018],{},"Visualize Query Boundaries:",[18,43020,43021],{},"Show container query boundaries"," in the Layout pane (under Elements > Styles > Container Queries). This overlays a visual guide showing where ",[18,43024,38792],{},[18,43026,11404],{}," calculations originate.",[1396,43029,43030,43033,43034,43036],{},[62,43031,43032],{},"Simulate Container Resize:"," Use the Device Toolbar or manually drag the container's parent in the Elements panel. Observe ",[18,43035,12001],{}," breakpoints triggering in the Styles pane.",[1396,43038,43039,43042,43043,43045],{},[62,43040,43041],{},"Debug Style Queries:"," Toggle custom properties in the Styles panel. DevTools will highlight which ",[18,43044,42818],{}," blocks activate based on computed values.",[1396,43047,43048,43050,43051,43053,43054,80,43056,43058],{},[62,43049,13458],{}," Open Performance tab → Record → Resize container repeatedly. Check for ",[18,43052,13462],{}," spikes. If present, switch from ",[18,43055,41019],{},[18,43057,26044],{}," containment or debounce JS-driven resizes.",[47,43060],{},[50,43062,13480],{"id":13479},[1393,43064,43065,43080,43093],{},[1396,43066,43067,43070,43071,569,43073,43075,43076],{},[62,43068,43069],{},"CSS Containment Module Level 3:"," Defines ",[18,43072,24401],{},[18,43074,26072],{},", and containment semantics. ",[27,43077,43079],{"href":38514,"rel":43078},[13489],"W3C Spec",[1396,43081,43082,43085,43086,43088,43089],{},[62,43083,43084],{},"CSS Conditional Rules Module Level 5:"," Standardizes ",[18,43087,12001],{}," syntax, logical operators, and style query evaluation. ",[27,43090,43079],{"href":43091,"rel":43092},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-conditional-5\u002F",[13489],[1396,43094,43095,43098,43099,569,43101,569,43103,569,43105,569,43107,569,43109,43111,43112],{},[62,43096,43097],{},"CSS Values & Units Module Level 4:"," Documents container-relative units (",[18,43100,38792],{},[18,43102,38795],{},[18,43104,11404],{},[18,43106,12282],{},[18,43108,38802],{},[18,43110,38805],{},"). ",[27,43113,43079],{"href":43114,"rel":43115},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-values-4\u002F#container-lengths",[13489],[47,43117],{},[50,43119,1391],{"id":1390},[1393,43121,43122,43128,43134,43139,43143],{},[1396,43123,43124,43127],{},[27,43125,43126],{"href":11248},"Container query units cqi and cqb explained"," — how logical container units track writing mode.",[1396,43129,43130,43133],{},[27,43131,43132],{"href":39903},"Nesting and naming container queries"," — disambiguating multiple containment contexts.",[1396,43135,43136,43138],{},[27,43137,42078],{"href":37795}," — when element context beats the viewport.",[1396,43140,43141,8769],{},[27,43142,11296],{"href":5777},[1396,43144,43145,43147],{},[27,43146,33331],{"href":8756}," — keeping container-driven transitions on the compositor.",[1430,43149,41922],{},{"title":286,"searchDepth":330,"depth":330,"links":43151},[43152,43153,43155,43156,43157,43158,43159,43160,43161,43162],{"id":42037,"depth":330,"text":42038},{"id":42188,"depth":330,"text":43154},"The @container At-Rule Structure",{"id":42383,"depth":330,"text":42384},{"id":42544,"depth":330,"text":42545},{"id":13238,"depth":330,"text":13239},{"id":42844,"depth":330,"text":42845},{"id":16217,"depth":330,"text":16218},{"id":8357,"depth":330,"text":13396},{"id":13479,"depth":330,"text":13480},{"id":1390,"depth":330,"text":1391},"CSS container query syntax: @container at-rule, container-type, container-name, and relative units for building modular, context-aware interfaces.",{"seoTitle":26284,"datePublished":1447,"dateModified":1447,"faq":43165},[43166,43169,43171,43173],{"q":43167,"a":43168},"What is the difference between container-type: inline-size and container-type: size?","inline-size establishes a query context based only on the inline dimension (width in horizontal writing modes), which is highly performant. size establishes a context for both inline and block dimensions but triggers more frequent layout recalculations, making it less suitable for frequently resized elements.",{"q":42977,"a":43170},"Yes. Container queries work seamlessly with Grid and Flexbox. Declare the containment context on a parent element, and queried children can adjust their internal grid or flex layouts based on the container's dimensions rather than the viewport.",{"q":42983,"a":43172},"Wrap your container query syntax in an @supports (container-type: inline-size) block. Provide a baseline layout outside the block, and optionally use @media queries as a fallback for viewport-based responsiveness in older browsers.",{"q":42995,"a":43174},"Use style queries when component appearance depends on computed CSS properties, custom properties, or inherited states rather than physical dimensions. They are ideal for theme switching, accessibility preferences, and state-driven UI variations.","\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics",{"title":26284,"description":43163},"mastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Findex","IechsMy3QwFmd_RjnPio0sTKg0aOUFn6vWBNkUsT1-s",{"id":43180,"title":43181,"body":43182,"description":44268,"extension":1444,"meta":44269,"navigation":333,"path":44280,"seo":44281,"stem":44282,"__hash__":44283},"content\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fnesting-and-naming-container-queries\u002Findex.md","Nesting and Naming Container Queries: Targeting the Right Ancestor",{"type":7,"value":43183,"toc":44258},[43184,43187,43189,43209,43211,43234,43237,43239,43282,44002,44004,44040,44042,44048,44156,44163,44165,44179,44181,44187,44199,44205,44222,44224,44256],[10,43185,43181],{"id":43186},"nesting-and-naming-container-queries-targeting-the-right-ancestor",[50,43188,35783],{"id":35782},[14,43190,43191,43192,43195,43196,43199,43200,43202,43203,43205,43206,43208],{},"You build a product card whose inner media block should switch to a row layout when the card itself is wide, and you also wrap the whole card grid in a container so the grid can reflow. Now you have two query containers stacked above the media block, and a plain ",[18,43193,43194],{},"@container (min-width: 400px)"," rule silently binds to the ",[86,43197,43198],{},"closest"," one — the card — when you actually meant the outer grid, or vice versa. Worse, a beginner reflex is to put ",[18,43201,24401],{}," on the same element you want to style, expecting it to react to its own width, which never fires. This guide, part of ",[27,43204,26284],{"href":25340}," under ",[27,43207,11296],{"href":5777},", shows how to name containers so each query resolves to exactly the ancestor you intend, and how to avoid the accidental self-query trap.",[50,43210,35809],{"id":35808},[14,43212,43213,43214,43216,43217,43219,43220,43222,43223,43225,43226,43229,43230,43233],{},"The CSS containment model resolves an ",[18,43215,12001],{}," rule by walking up the ancestor chain from the styled element and stopping at the first element whose ",[18,43218,24401],{}," makes it a valid query container for the requested axis. If the rule names a container with ",[18,43221,42009],{},", the walk stops at the nearest ancestor whose ",[18,43224,26072],{}," includes ",[18,43227,43228],{},"card",". This is deliberate and matches how the cascade resolves other contextual lookups, but it means ",[86,43231,43232],{},"naming is your only reliable control"," once containers nest. Relying on \"nearest unnamed container\" is fragile: any refactor that introduces a new container between the styled element and its intended target will silently rebind the query.",[14,43235,43236],{},"The alternative — using JavaScript to measure ancestors and apply classes — reintroduces the layout-read\u002Fstyle-write cycle container queries were designed to remove, and it is hard to keep accessible because the class flip can lag a frame behind the resize. Naming containers keeps the whole resolution in the engine's single layout pass. The accessibility tradeoff is minimal: names are invisible to assistive technology and add no DOM, so the only cost is the discipline of choosing stable, meaningful names.",[50,43238,4203],{"id":4202},[133,43240,140,43242,140,43245,140,43248,140,43251,140,43254,140,43257,140,43259,140,43263,140,43265,140,43270,140,43274,140,43276,140,43279],{"viewBox":135,"role":136,"ariaLabel":43241,"xmlns":138,"style":139},"A containment tree with an outer container named page, a middle container named card, and an inner element, showing that a query for card resolves to the middle ancestor",[142,43243,43244],{},"Which named ancestor a query matches",[146,43246,43247],{},"Nested containers named page and card; the @container card query resolves to the nearest ancestor carrying the card name, skipping page.",[150,43249,43250],{"x":152,"y":153,"style":154},"Resolving @container card from a nested element",[171,43252],{"x":4146,"y":1788,"width":14990,"height":11114,"rx":43253,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},"14",[150,43255,43256],{"x":1786,"y":9105,"style":32038},"container-name: page",[171,43258],{"x":9105,"y":6642,"width":2619,"height":174,"rx":5894,"fill":177,"stroke":177,"strokeWidth":168,"opacity":22004},[150,43260,43262],{"x":165,"y":4147,"style":43261},"text-anchor:start;fill:currentColor;font:12px ui-monospace,monospace;opacity:0.9","container-name: card  ← match",[171,43264],{"x":4147,"y":5149,"width":19660,"height":182,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},[150,43266,43269],{"x":4195,"y":43267,"style":43268},"162","text-anchor:start;fill:currentColor;font:12px sans-serif;opacity:0.8","styled element (no container-type)",[150,43271,43273],{"x":4195,"y":2622,"style":43272},"text-anchor:start;fill:currentColor;font:12px ui-monospace,monospace;opacity:0.95","@container card (min-width: 420px)",[163,43275],{"x1":152,"y1":209,"x2":152,"y2":25453,"stroke":177,"strokeWidth":194},[253,43277],{"d":43278,"fill":177},"M360 128 l-6 12 l12 0 z",[150,43280,43281],{"x":152,"y":2613,"style":10252},"walk stops at the nearest ancestor named card, never the page container",[281,43283,43285],{"className":283,"code":43284,"language":285,"meta":286,"style":286},"\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\u003Cstyle>\n  \u002F* Outer container: lets the grid itself reflow with available space *\u002F\n  .feed {\n    container: page \u002F inline-size; \u002F* shorthand = container-name + container-type *\u002F\n    display: grid;\n    gap: 1.5rem;\n    padding: 1.5rem;\n  }\n\n  \u002F* When the feed is wide, show two columns of cards. Named so it binds to .feed only *\u002F\n  @container page (min-width: 700px) {\n    .feed {\n      grid-template-columns: 1fr 1fr;\n    }\n  }\n\n  \u002F* Middle container: each card scopes its own internal layout *\u002F\n  .card {\n    container: card \u002F inline-size;\n    background: #11141c;\n    color: #e8ecf4;\n    border-radius: 12px;\n    padding: 1rem;\n  }\n\n  \u002F* The media block is the STYLED element. It carries NO container-type, *\u002F\n  \u002F* so it cannot query itself. The query below names `card`, so the walk  *\u002F\n  \u002F* up the tree stops at .card and ignores the outer .feed (page).        *\u002F\n  .card__media {\n    display: grid;\n    gap: 0.75rem;\n  }\n\n  @container card (min-width: 420px) {\n    .card__media {\n      grid-template-columns: 140px 1fr; \u002F* image beside text once the CARD is wide *\u002F\n      align-items: center;\n    }\n  }\n\n  .card__media img {\n    width: 100%;\n    border-radius: 8px;\n    aspect-ratio: 4 \u002F 3;\n    object-fit: cover;\n  }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv class=\"feed\">\n    \u003Carticle class=\"card\">\n      \u003Cdiv class=\"card__media\">\n        \u003Cimg src=\"https:\u002F\u002Fplacehold.co\u002F280x210\" alt=\"Sample product photo\">\n        \u003Cdiv>\n          \u003Ch2>Named-container card\u003C\u002Fh2>\n          \u003Cp>Its inner layout reacts to the card width, not the feed width.\u003C\u002Fp>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    \u003C\u002Farticle>\n    \u003Carticle class=\"card\">\n      \u003Cdiv class=\"card__media\">\n        \u003Cimg src=\"https:\u002F\u002Fplacehold.co\u002F280x210\" alt=\"Second product photo\">\n        \u003Cdiv>\n          \u003Ch2>Second card\u003C\u002Fh2>\n          \u003Cp>Same rules, resolved independently against its own card container.\u003C\u002Fp>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    \u003C\u002Farticle>\n  \u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,43286,43287,43297,43311,43319,43333,43353,43361,43366,43373,43389,43399,43411,43423,43427,43431,43436,43443,43450,43466,43470,43474,43478,43483,43489,43496,43506,43516,43528,43540,43544,43548,43553,43558,43563,43570,43580,43592,43596,43600,43607,43614,43633,43643,43647,43651,43655,43663,43675,43687,43701,43711,43715,43723,43731,43739,43754,43768,43783,43805,43813,43827,43840,43849,43857,43865,43879,43893,43914,43922,43935,43948,43957,43966,43975,43984,43993],{"__ignoreMap":286},[290,43288,43289,43291,43293,43295],{"class":163,"line":292},[290,43290,8982],{"class":295},[290,43292,35913],{"class":299},[290,43294,8988],{"class":303},[290,43296,327],{"class":295},[290,43298,43299,43301,43303,43305,43307,43309],{"class":163,"line":330},[290,43300,296],{"class":295},[290,43302,285],{"class":299},[290,43304,8999],{"class":303},[290,43306,307],{"class":295},[290,43308,9004],{"class":310},[290,43310,327],{"class":295},[290,43312,43313,43315,43317],{"class":163,"line":337},[290,43314,296],{"class":295},[290,43316,9013],{"class":299},[290,43318,327],{"class":295},[290,43320,43321,43323,43325,43327,43329,43331],{"class":163,"line":364},[290,43322,296],{"class":295},[290,43324,9022],{"class":299},[290,43326,9025],{"class":303},[290,43328,307],{"class":295},[290,43330,9030],{"class":310},[290,43332,327],{"class":295},[290,43334,43335,43337,43339,43341,43343,43345,43347,43349,43351],{"class":163,"line":386},[290,43336,296],{"class":295},[290,43338,9022],{"class":299},[290,43340,35962],{"class":303},[290,43342,307],{"class":295},[290,43344,35967],{"class":310},[290,43346,35970],{"class":303},[290,43348,307],{"class":295},[290,43350,35975],{"class":310},[290,43352,327],{"class":295},[290,43354,43355,43357,43359],{"class":163,"line":408},[290,43356,296],{"class":295},[290,43358,1430],{"class":299},[290,43360,327],{"class":295},[290,43362,43363],{"class":163,"line":428},[290,43364,43365],{"class":455},"  \u002F* Outer container: lets the grid itself reflow with available space *\u002F\n",[290,43367,43368,43371],{"class":163,"line":517},[290,43369,43370],{"class":303},"  .feed",[290,43372,450],{"class":295},[290,43374,43375,43378,43380,43383,43386],{"class":163,"line":523},[290,43376,43377],{"class":461},"    container",[290,43379,465],{"class":295},[290,43381,43382],{"class":461},"page",[290,43384,43385],{"class":295}," \u002F inline-size; ",[290,43387,43388],{"class":455},"\u002F* shorthand = container-name + container-type *\u002F\n",[290,43390,43391,43393,43395,43397],{"class":163,"line":532},[290,43392,34590],{"class":461},[290,43394,465],{"class":295},[290,43396,9147],{"class":461},[290,43398,471],{"class":295},[290,43400,43401,43403,43405,43407,43409],{"class":163,"line":551},[290,43402,36027],{"class":461},[290,43404,465],{"class":295},[290,43406,168],{"class":461},[290,43408,801],{"class":541},[290,43410,471],{"class":295},[290,43412,43413,43415,43417,43419,43421],{"class":163,"line":586},[290,43414,36040],{"class":461},[290,43416,465],{"class":295},[290,43418,168],{"class":461},[290,43420,801],{"class":541},[290,43422,471],{"class":295},[290,43424,43425],{"class":163,"line":602},[290,43426,771],{"class":295},[290,43428,43429],{"class":163,"line":617},[290,43430,334],{"emptyLinePlaceholder":333},[290,43432,43433],{"class":163,"line":623},[290,43434,43435],{"class":455},"  \u002F* When the feed is wide, show two columns of cards. Named so it binds to .feed only *\u002F\n",[290,43437,43438,43440],{"class":163,"line":628},[290,43439,37040],{"class":541},[290,43441,43442],{"class":295}," page (min-width: 700px) {\n",[290,43444,43445,43448],{"class":163,"line":634},[290,43446,43447],{"class":303},"    .feed",[290,43449,450],{"class":295},[290,43451,43452,43454,43456,43458,43460,43462,43464],{"class":163,"line":649},[290,43453,36130],{"class":461},[290,43455,465],{"class":295},[290,43457,468],{"class":461},[290,43459,11964],{"class":541},[290,43461,804],{"class":461},[290,43463,11964],{"class":541},[290,43465,471],{"class":295},[290,43467,43468],{"class":163,"line":660},[290,43469,8200],{"class":295},[290,43471,43472],{"class":163,"line":688},[290,43473,771],{"class":295},[290,43475,43476],{"class":163,"line":693},[290,43477,334],{"emptyLinePlaceholder":333},[290,43479,43480],{"class":163,"line":698},[290,43481,43482],{"class":455},"  \u002F* Middle container: each card scopes its own internal layout *\u002F\n",[290,43484,43485,43487],{"class":163,"line":704},[290,43486,9083],{"class":303},[290,43488,450],{"class":295},[290,43490,43491,43493],{"class":163,"line":710},[290,43492,43377],{"class":461},[290,43494,43495],{"class":295},": card \u002F inline-size;\n",[290,43497,43498,43500,43502,43504],{"class":163,"line":717},[290,43499,9124],{"class":461},[290,43501,465],{"class":295},[290,43503,36057],{"class":461},[290,43505,471],{"class":295},[290,43507,43508,43510,43512,43514],{"class":163,"line":730},[290,43509,36064],{"class":461},[290,43511,465],{"class":295},[290,43513,36069],{"class":461},[290,43515,471],{"class":295},[290,43517,43518,43520,43522,43524,43526],{"class":163,"line":742},[290,43519,12759],{"class":461},[290,43521,465],{"class":295},[290,43523,5894],{"class":461},[290,43525,674],{"class":541},[290,43527,471],{"class":295},[290,43529,43530,43532,43534,43536,43538],{"class":163,"line":768},[290,43531,36040],{"class":461},[290,43533,465],{"class":295},[290,43535,468],{"class":461},[290,43537,801],{"class":541},[290,43539,471],{"class":295},[290,43541,43542],{"class":163,"line":774},[290,43543,771],{"class":295},[290,43545,43546],{"class":163,"line":779},[290,43547,334],{"emptyLinePlaceholder":333},[290,43549,43550],{"class":163,"line":784},[290,43551,43552],{"class":455},"  \u002F* The media block is the STYLED element. It carries NO container-type, *\u002F\n",[290,43554,43555],{"class":163,"line":812},[290,43556,43557],{"class":455},"  \u002F* so it cannot query itself. The query below names `card`, so the walk  *\u002F\n",[290,43559,43560],{"class":163,"line":860},[290,43561,43562],{"class":455},"  \u002F* up the tree stops at .card and ignores the outer .feed (page).        *\u002F\n",[290,43564,43565,43568],{"class":163,"line":865},[290,43566,43567],{"class":303},"  .card__media",[290,43569,450],{"class":295},[290,43571,43572,43574,43576,43578],{"class":163,"line":871},[290,43573,34590],{"class":461},[290,43575,465],{"class":295},[290,43577,9147],{"class":461},[290,43579,471],{"class":295},[290,43581,43582,43584,43586,43588,43590],{"class":163,"line":880},[290,43583,36027],{"class":461},[290,43585,465],{"class":295},[290,43587,823],{"class":461},[290,43589,801],{"class":541},[290,43591,471],{"class":295},[290,43593,43594],{"class":163,"line":896},[290,43595,771],{"class":295},[290,43597,43598],{"class":163,"line":4734},[290,43599,334],{"emptyLinePlaceholder":333},[290,43601,43602,43604],{"class":163,"line":4742},[290,43603,37040],{"class":541},[290,43605,43606],{"class":295}," card (min-width: 420px) {\n",[290,43608,43609,43612],{"class":163,"line":4761},[290,43610,43611],{"class":303},"    .card__media",[290,43613,450],{"class":295},[290,43615,43616,43618,43620,43622,43624,43626,43628,43630],{"class":163,"line":4766},[290,43617,36130],{"class":461},[290,43619,465],{"class":295},[290,43621,4195],{"class":461},[290,43623,674],{"class":541},[290,43625,804],{"class":461},[290,43627,11964],{"class":541},[290,43629,828],{"class":295},[290,43631,43632],{"class":455},"\u002F* image beside text once the CARD is wide *\u002F\n",[290,43634,43635,43637,43639,43641],{"class":163,"line":4786},[290,43636,36150],{"class":461},[290,43638,465],{"class":295},[290,43640,9157],{"class":461},[290,43642,471],{"class":295},[290,43644,43645],{"class":163,"line":9635},[290,43646,8200],{"class":295},[290,43648,43649],{"class":163,"line":9641},[290,43650,771],{"class":295},[290,43652,43653],{"class":163,"line":9647},[290,43654,334],{"emptyLinePlaceholder":333},[290,43656,43657,43659,43661],{"class":163,"line":9665},[290,43658,43567],{"class":303},[290,43660,36315],{"class":299},[290,43662,450],{"class":295},[290,43664,43665,43667,43669,43671,43673],{"class":163,"line":9683},[290,43666,9090],{"class":461},[290,43668,465],{"class":295},[290,43670,165],{"class":461},[290,43672,11018],{"class":541},[290,43674,471],{"class":295},[290,43676,43677,43679,43681,43683,43685],{"class":163,"line":9688},[290,43678,12759],{"class":461},[290,43680,465],{"class":295},[290,43682,176],{"class":461},[290,43684,674],{"class":541},[290,43686,471],{"class":295},[290,43688,43689,43691,43693,43695,43697,43699],{"class":163,"line":9694},[290,43690,36346],{"class":461},[290,43692,465],{"class":295},[290,43694,249],{"class":461},[290,43696,1203],{"class":295},[290,43698,1579],{"class":461},[290,43700,471],{"class":295},[290,43702,43703,43705,43707,43709],{"class":163,"line":9700},[290,43704,36361],{"class":461},[290,43706,465],{"class":295},[290,43708,36366],{"class":461},[290,43710,471],{"class":295},[290,43712,43713],{"class":163,"line":9710},[290,43714,771],{"class":295},[290,43716,43717,43719,43721],{"class":163,"line":9716},[290,43718,431],{"class":295},[290,43720,1430],{"class":299},[290,43722,327],{"class":295},[290,43724,43725,43727,43729],{"class":163,"line":9738},[290,43726,431],{"class":295},[290,43728,9013],{"class":299},[290,43730,327],{"class":295},[290,43732,43733,43735,43737],{"class":163,"line":9748},[290,43734,296],{"class":295},[290,43736,9239],{"class":299},[290,43738,327],{"class":295},[290,43740,43741,43743,43745,43747,43749,43752],{"class":163,"line":9754},[290,43742,367],{"class":295},[290,43744,342],{"class":299},[290,43746,314],{"class":303},[290,43748,307],{"class":295},[290,43750,43751],{"class":310},"\"feed\"",[290,43753,327],{"class":295},[290,43755,43756,43758,43760,43762,43764,43766],{"class":163,"line":9760},[290,43757,4290],{"class":295},[290,43759,11445],{"class":299},[290,43761,314],{"class":303},[290,43763,307],{"class":295},[290,43765,9295],{"class":310},[290,43767,327],{"class":295},[290,43769,43770,43772,43774,43776,43778,43781],{"class":163,"line":9777},[290,43771,36430],{"class":295},[290,43773,342],{"class":299},[290,43775,314],{"class":303},[290,43777,307],{"class":295},[290,43779,43780],{"class":310},"\"card__media\"",[290,43782,327],{"class":295},[290,43784,43785,43787,43789,43791,43793,43796,43798,43800,43803],{"class":163,"line":9787},[290,43786,36461],{"class":295},[290,43788,136],{"class":299},[290,43790,4296],{"class":303},[290,43792,307],{"class":295},[290,43794,43795],{"class":310},"\"https:\u002F\u002Fplacehold.co\u002F280x210\"",[290,43797,4341],{"class":303},[290,43799,307],{"class":295},[290,43801,43802],{"class":310},"\"Sample product photo\"",[290,43804,327],{"class":295},[290,43806,43807,43809,43811],{"class":163,"line":9793},[290,43808,36461],{"class":295},[290,43810,342],{"class":299},[290,43812,327],{"class":295},[290,43814,43815,43818,43820,43823,43825],{"class":163,"line":9799},[290,43816,43817],{"class":295},"          \u003C",[290,43819,50],{"class":299},[290,43821,43822],{"class":295},">Named-container card\u003C\u002F",[290,43824,50],{"class":299},[290,43826,327],{"class":295},[290,43828,43829,43831,43833,43836,43838],{"class":163,"line":9805},[290,43830,43817],{"class":295},[290,43832,14],{"class":299},[290,43834,43835],{"class":295},">Its inner layout reacts to the card width, not the feed width.\u003C\u002F",[290,43837,14],{"class":299},[290,43839,327],{"class":295},[290,43841,43842,43845,43847],{"class":163,"line":9811},[290,43843,43844],{"class":295},"        \u003C\u002F",[290,43846,342],{"class":299},[290,43848,327],{"class":295},[290,43850,43851,43853,43855],{"class":163,"line":9820},[290,43852,36493],{"class":295},[290,43854,342],{"class":299},[290,43856,327],{"class":295},[290,43858,43859,43861,43863],{"class":163,"line":9829},[290,43860,36502],{"class":295},[290,43862,11445],{"class":299},[290,43864,327],{"class":295},[290,43866,43867,43869,43871,43873,43875,43877],{"class":163,"line":27197},[290,43868,4290],{"class":295},[290,43870,11445],{"class":299},[290,43872,314],{"class":303},[290,43874,307],{"class":295},[290,43876,9295],{"class":310},[290,43878,327],{"class":295},[290,43880,43881,43883,43885,43887,43889,43891],{"class":163,"line":27202},[290,43882,36430],{"class":295},[290,43884,342],{"class":299},[290,43886,314],{"class":303},[290,43888,307],{"class":295},[290,43890,43780],{"class":310},[290,43892,327],{"class":295},[290,43894,43895,43897,43899,43901,43903,43905,43907,43909,43912],{"class":163,"line":27209},[290,43896,36461],{"class":295},[290,43898,136],{"class":299},[290,43900,4296],{"class":303},[290,43902,307],{"class":295},[290,43904,43795],{"class":310},[290,43906,4341],{"class":303},[290,43908,307],{"class":295},[290,43910,43911],{"class":310},"\"Second product photo\"",[290,43913,327],{"class":295},[290,43915,43916,43918,43920],{"class":163,"line":27220},[290,43917,36461],{"class":295},[290,43919,342],{"class":299},[290,43921,327],{"class":295},[290,43923,43924,43926,43928,43931,43933],{"class":163,"line":27235},[290,43925,43817],{"class":295},[290,43927,50],{"class":299},[290,43929,43930],{"class":295},">Second card\u003C\u002F",[290,43932,50],{"class":299},[290,43934,327],{"class":295},[290,43936,43937,43939,43941,43944,43946],{"class":163,"line":27240},[290,43938,43817],{"class":295},[290,43940,14],{"class":299},[290,43942,43943],{"class":295},">Same rules, resolved independently against its own card container.\u003C\u002F",[290,43945,14],{"class":299},[290,43947,327],{"class":295},[290,43949,43951,43953,43955],{"class":163,"line":43950},71,[290,43952,43844],{"class":295},[290,43954,342],{"class":299},[290,43956,327],{"class":295},[290,43958,43960,43962,43964],{"class":163,"line":43959},72,[290,43961,36493],{"class":295},[290,43963,342],{"class":299},[290,43965,327],{"class":295},[290,43967,43969,43971,43973],{"class":163,"line":43968},73,[290,43970,36502],{"class":295},[290,43972,11445],{"class":299},[290,43974,327],{"class":295},[290,43976,43978,43980,43982],{"class":163,"line":43977},74,[290,43979,4315],{"class":295},[290,43981,342],{"class":299},[290,43983,327],{"class":295},[290,43985,43987,43989,43991],{"class":163,"line":43986},75,[290,43988,431],{"class":295},[290,43990,9239],{"class":299},[290,43992,327],{"class":295},[290,43994,43996,43998,44000],{"class":163,"line":43995},76,[290,43997,431],{"class":295},[290,43999,285],{"class":299},[290,44001,327],{"class":295},[50,44003,36534],{"id":36533},[14,44005,44006,44007,44009,44010,44013,44014,44016,44017,44020,44021,44023,44024,44026,44027,44029,44030,44032,44033,44035,44036,44039],{},"The mechanism that prevents the accidental self-query is the separation between the ",[86,44008,26172],{}," element and the ",[86,44011,44012],{},"styled"," element. ",[18,44015,24401],{}," establishes a query container, but a container can never match a query that styles itself — the resolution always walks to an ",[86,44018,44019],{},"ancestor",". So the working pattern is always: put ",[18,44022,24401],{}," (or the ",[18,44025,26172],{}," shorthand) on a wrapper, and write the conditional rule against a descendant. The second half of the technique is ",[18,44028,26072],{},". When containers nest, ",[18,44031,42009],{}," filters the ancestor walk to elements whose name list includes ",[18,44034,43228],{},", letting you skip an intervening unnamed or differently named container. Using the ",[18,44037,44038],{},"container: \u003Cname> \u002F \u003Ctype>"," shorthand sets both at once and is the most robust default, because it makes every container explicitly named and therefore impossible to bind to by accident.",[50,44041,36574],{"id":36573},[14,44043,44044,44045,44047],{},"A container can answer to more than one name, which is useful for reduced-motion and theming variants that need to address the same box under different aliases. The ",[18,44046,26072],{}," property accepts a space-separated list.",[281,44049,44051],{"className":438,"code":44050,"language":440,"meta":286,"style":286},"\u002F* This container responds to BOTH `card` and `surface` queries *\u002F\n.card {\n  container-name: card surface;\n  container-type: inline-size;\n}\n\n\u002F* Generic surface rule, e.g. shared by cards, panels and tiles *\u002F\n@container surface (min-width: 600px) {\n  .card__media { gap: 1rem; }\n}\n\n\u002F* Card-specific rule on the same element *\u002F\n@container card (min-width: 420px) {\n  .card__media { grid-template-columns: 140px 1fr; }\n}\n",[18,44052,44053,44058,44064,44071,44077,44081,44085,44090,44097,44113,44117,44121,44126,44132,44152],{"__ignoreMap":286},[290,44054,44055],{"class":163,"line":292},[290,44056,44057],{"class":455},"\u002F* This container responds to BOTH `card` and `surface` queries *\u002F\n",[290,44059,44060,44062],{"class":163,"line":330},[290,44061,11528],{"class":303},[290,44063,450],{"class":295},[290,44065,44066,44068],{"class":163,"line":337},[290,44067,25575],{"class":461},[290,44069,44070],{"class":295},": card surface;\n",[290,44072,44073,44075],{"class":163,"line":364},[290,44074,11509],{"class":461},[290,44076,25565],{"class":295},[290,44078,44079],{"class":163,"line":386},[290,44080,620],{"class":295},[290,44082,44083],{"class":163,"line":408},[290,44084,334],{"emptyLinePlaceholder":333},[290,44086,44087],{"class":163,"line":428},[290,44088,44089],{"class":455},"\u002F* Generic surface rule, e.g. shared by cards, panels and tiles *\u002F\n",[290,44091,44092,44094],{"class":163,"line":517},[290,44093,12001],{"class":541},[290,44095,44096],{"class":295}," surface (min-width: 600px) {\n",[290,44098,44099,44101,44103,44105,44107,44109,44111],{"class":163,"line":523},[290,44100,43567],{"class":303},[290,44102,790],{"class":295},[290,44104,9070],{"class":461},[290,44106,465],{"class":295},[290,44108,468],{"class":461},[290,44110,801],{"class":541},[290,44112,809],{"class":295},[290,44114,44115],{"class":163,"line":532},[290,44116,620],{"class":295},[290,44118,44119],{"class":163,"line":551},[290,44120,334],{"emptyLinePlaceholder":333},[290,44122,44123],{"class":163,"line":586},[290,44124,44125],{"class":455},"\u002F* Card-specific rule on the same element *\u002F\n",[290,44127,44128,44130],{"class":163,"line":602},[290,44129,12001],{"class":541},[290,44131,43606],{"class":295},[290,44133,44134,44136,44138,44140,44142,44144,44146,44148,44150],{"class":163,"line":617},[290,44135,43567],{"class":303},[290,44137,790],{"class":295},[290,44139,11957],{"class":461},[290,44141,465],{"class":295},[290,44143,4195],{"class":461},[290,44145,674],{"class":541},[290,44147,804],{"class":461},[290,44149,11964],{"class":541},[290,44151,809],{"class":295},[290,44153,44154],{"class":163,"line":623},[290,44155,620],{"class":295},[14,44157,44158,44159,44162],{},"This lets a design-system primitive expose a stable generic name (",[18,44160,44161],{},"surface",") while a specific component layers its own name on top, so shared rules and component rules both resolve to the right box without duplicating containers.",[50,44164,1299],{"id":1298},[14,44166,44167,44168,44170,44171,44173,44174,44176,44177,42],{},"Container names, the ",[18,44169,26172],{}," shorthand, and named ",[18,44172,12001],{}," queries are part of the same size-query baseline: Chrome and Edge 105+, Safari 16.0+, and Firefox 110+. Multiple names in one ",[18,44175,26072],{}," list and the shorthand are supported across all of these versions, so no separate fallback is needed beyond the static styles you already ship for engines without container queries at all — see the guide to ",[27,44178,37935],{"href":19560},[50,44180,1316],{"id":1315},[14,44182,44183,44186],{},[62,44184,44185],{},"Can an element query its own size with a container query?","\nNo. A container query resolves against the nearest matching ancestor query container, never the element itself. To respond to an element's own width you must wrap it so the parent is the container, or split the component into a container wrapper and a styled child.",[14,44188,44189,44192,44193,44195,44196,44198],{},[62,44190,44191],{},"What does an unnamed @container query target?","\nIt targets the nearest ancestor that has a ",[18,44194,24401],{},", regardless of name. If you have several nested containers, an unnamed query binds to the closest one, which is often not the one you intended. Use ",[18,44197,26072],{}," to bind explicitly.",[14,44200,44201,44204],{},[62,44202,44203],{},"Can two containers share the same container-name?","\nYes. A name is not required to be unique. When names collide, a query with that name still resolves to the nearest ancestor carrying it, so reuse the same name only for genuinely interchangeable containers.",[14,44206,44207,8717,44210,44212,44213,44215,44216,44218,44219,44221],{},[62,44208,44209],{},"Does container-type alone create a name?",[18,44211,24401],{}," makes an element a query container but leaves it anonymous. Add ",[18,44214,26072],{},", or use the ",[18,44217,26172],{}," shorthand, to give it a name that ",[18,44220,12001],{}," queries can reference.",[50,44223,1391],{"id":1390},[1393,44225,44226,44235,44240,44245,44250],{},[1396,44227,44228,44230,44231,69,44233,42],{},[27,44229,26284],{"href":25340}," — the parent guide to ",[18,44232,24401],{},[18,44234,12001],{},[1396,44236,44237,44239],{},[27,44238,36790],{"href":11248}," — the length units that resolve against a named container.",[1396,44241,44242,44244],{},[27,44243,8763],{"href":4036}," — containment scoping and naming at scale.",[1396,44246,44247,44249],{},[27,44248,37784],{"href":19560}," — guard named queries behind a support check.",[1396,44251,44252,44255],{},[27,44253,44254],{"href":31513},"Container-Query-Triggered Keyframe Animations"," — drive animations from a named container's size.",[1430,44257,36799],{},{"title":286,"searchDepth":330,"depth":330,"links":44259},[44260,44261,44262,44263,44264,44265,44266,44267],{"id":35782,"depth":330,"text":35783},{"id":35808,"depth":330,"text":35809},{"id":4202,"depth":330,"text":4203},{"id":36533,"depth":330,"text":36534},{"id":36573,"depth":330,"text":36574},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Use container-name to target the right ancestor across nested containers, understand which container a query resolves to, and avoid accidental self-queries.",{"seoTitle":44270,"datePublished":1447,"dateModified":1447,"faq":44271},"Nesting & Naming Container Queries",[44272,44274,44276,44278],{"q":44185,"a":44273},"No. A container query resolves against the nearest matching ancestor query container, never the element itself. To respond to an element's own width you must wrap it so the parent is the container, or split the component into a container wrapper and a styled child.",{"q":44191,"a":44275},"It targets the nearest ancestor that has a container-type, regardless of name. If you have several nested containers, an unnamed query binds to the closest one, which is often not the one you intended. Use container-name to bind explicitly.",{"q":44203,"a":44277},"Yes. A name is not required to be unique. When names collide, a query with that name still resolves to the nearest ancestor carrying it, so reuse the same name only for genuinely interchangeable containers.",{"q":44209,"a":44279},"No. container-type makes an element a query container but leaves it anonymous. Add container-name, or use the container shorthand, to give it a name that @container queries can reference.","\u002Fmastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fnesting-and-naming-container-queries",{"title":43181,"description":44268},"mastering-container-queries-responsive-layouts\u002Fcontainer-query-syntax-basics\u002Fnesting-and-naming-container-queries\u002Findex","K2I313yQF-cxg8RGrtHYVVRCRXcLM8-oqHAdIZoGIlI",{"id":44285,"title":44286,"body":44287,"description":46206,"extension":1444,"meta":46207,"navigation":333,"path":46218,"seo":46219,"stem":46220,"__hash__":46221},"content\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fbuilding-a-dashboard-with-subgrid\u002Findex.md","Building a Responsive Dashboard Where Cards Align with Subgrid",{"type":7,"value":44288,"toc":46196},[44289,44292,44294,44311,44313,44319,44327,44329,45864,45874,45946,45948,45969,45973,45979,46121,46123,46133,46135,46141,46151,46157,46167,46169,46193],[10,44290,44286],{"id":44291},"building-a-responsive-dashboard-where-cards-align-with-subgrid",[50,44293,35783],{"id":35782},[14,44295,44296,44297,44300,44301,44303,44304,44308,44309,42],{},"You are building an analytics dashboard: a responsive grid of metric cards, each with a title, a chart or value area, and a footer of secondary stats. The grid auto-fits as many columns as fit, so cards land in rows of two, three, or four depending on viewport width. The problem is alignment. One card's title wraps to two lines, another's footer has three stats instead of one, and the result is a ragged row where nothing lines up. You want every card's title row, body row, and footer row to share a baseline across the entire row, with no fixed heights and no JavaScript measuring elements. This page solves exactly that with ",[18,44298,44299],{},"subgrid",", then layers ",[18,44302,12001],{}," on top so each card's interior adapts to its own width. It builds directly on the techniques in the ",[27,44305,44307],{"href":44306},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002F","CSS Grid and Subgrid Layouts"," guide under ",[27,44310,11296],{"href":5777},[50,44312,35809],{"id":35808},[14,44314,44315,44316,44318],{},"The pre-subgrid solutions were all compromises. Fixed ",[18,44317,37680],{}," on each section guesses at content length and breaks the moment a label is longer than expected. A shared flex row forces every card into one row and abandons the auto-fit wrapping you want. JavaScript that measures the tallest card and sets the rest to match works, but it runs on every resize, fights the browser's own layout pass, and adds a layout-thrash hazard plus a hydration cost.",[14,44320,44321,44323,44324,44326],{},[18,44322,44299],{}," removes the guesswork entirely. The dashboard grid owns the row tracks; each card spans those rows and inherits their sizes, so alignment is computed once by the engine during normal layout. There is no measurement step and no reflow loop. The accessibility tradeoff is favorable too: because alignment is achieved without reordering or absolute positioning, DOM order stays equal to reading order, so keyboard and screen-reader users traverse the dashboard exactly as it appears. The only cost is a ",[18,44325,2086],{}," gate for older browser versions, which is cheap.",[50,44328,4203],{"id":4202},[281,44330,44332],{"className":283,"code":44331,"language":285,"meta":286,"style":286},"\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\u003Ctitle>Subgrid dashboard\u003C\u002Ftitle>\n\u003Cstyle>\n  :root { color-scheme: light dark; }\n  body {\n    margin: 0;\n    font: 16px\u002F1.5 system-ui, sans-serif;\n    background: #f4f6fb;\n    color: #1c2430;\n  }\n\n  .dashboard {\n    display: grid;\n    \u002F* Auto-fitting columns: no breakpoints needed for the grid itself. *\u002F\n    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));\n    \u002F* The shared row tracks every card will align onto:\n       title (auto), body (flexible), footer (auto). *\u002F\n    grid-auto-rows: auto 1fr auto;\n    gap: 1.25rem;\n    padding: 1.5rem;\n    max-width: 76rem;\n    margin-inline: auto;\n  }\n\n  .card {\n    display: grid;\n    \u002F* Span the three implicit rows the parent created, and adopt them. *\u002F\n    grid-row: span 3;\n    grid-template-rows: subgrid;\n    row-gap: 0.75rem;\n\n    background: Canvas;\n    border: 1px solid #d4dbe8;\n    border-radius: 0.9rem;\n    padding: 1.1rem 1.25rem;\n    box-shadow: 0 1px 2px rgb(20 36 48 \u002F 0.06);\n\n    \u002F* Query the card's own width, not the viewport. *\u002F\n    container-type: inline-size;\n    container-name: card;\n  }\n\n  .card__title {\n    margin: 0;\n    font-size: 0.95rem;\n    font-weight: 600;\n    color: #5a6472;\n    text-transform: uppercase;\n    letter-spacing: 0.03em;\n  }\n\n  .card__value {\n    margin: 0;\n    font-size: 2.4rem;\n    font-weight: 700;\n    line-height: 1.1;\n  }\n\n  .card__delta { font-size: 0.9rem; color: #1b8a5a; }\n  .card__delta--down { color: #c23b3b; }\n\n  .card__footer {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 0.5rem 1rem;\n    margin: 0;\n    padding-top: 0.5rem;\n    border-top: 1px solid #e6eaf2;\n    font-size: 0.85rem;\n    color: #5a6472;\n    list-style: none;\n  }\n\n  \u002F* Card-level responsiveness: when a single card is wide,\n     place the value beside its delta instead of stacking. *\u002F\n  @container card (min-width: 18rem) {\n    .card__body {\n      display: grid;\n      grid-template-columns: 1fr auto;\n      align-items: baseline;\n      column-gap: 0.75rem;\n    }\n  }\n\n  \u002F* Older browsers: keep cards usable without subgrid alignment. *\u002F\n  @supports not (grid-template-rows: subgrid) {\n    .card { grid-row: auto; grid-template-rows: auto 1fr auto; }\n  }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cmain class=\"dashboard\">\n    \u003Csection class=\"card\">\n      \u003Ch2 class=\"card__title\">Monthly Recurring Revenue\u003C\u002Fh2>\n      \u003Cdiv class=\"card__body\">\n        \u003Cp class=\"card__value\">$48.2k\u003C\u002Fp>\n        \u003Cspan class=\"card__delta\">+12.4%\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n      \u003Cul class=\"card__footer\">\n        \u003Cli>New $6.1k\u003C\u002Fli>\n        \u003Cli>Churn $1.3k\u003C\u002Fli>\n      \u003C\u002Ful>\n    \u003C\u002Fsection>\n\n    \u003Csection class=\"card\">\n      \u003Ch2 class=\"card__title\">Active Users\u003C\u002Fh2>\n      \u003Cdiv class=\"card__body\">\n        \u003Cp class=\"card__value\">12,940\u003C\u002Fp>\n        \u003Cspan class=\"card__delta card__delta--down\">-2.1%\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n      \u003Cul class=\"card__footer\">\n        \u003Cli>DAU 4.2k\u003C\u002Fli>\n      \u003C\u002Ful>\n    \u003C\u002Fsection>\n\n    \u003Csection class=\"card\">\n      \u003Ch2 class=\"card__title\">Average Response Time Across All Regions\u003C\u002Fh2>\n      \u003Cdiv class=\"card__body\">\n        \u003Cp class=\"card__value\">214ms\u003C\u002Fp>\n        \u003Cspan class=\"card__delta\">+8ms\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n      \u003Cul class=\"card__footer\">\n        \u003Cli>p50 180ms\u003C\u002Fli>\n        \u003Cli>p95 410ms\u003C\u002Fli>\n        \u003Cli>p99 920ms\u003C\u002Fli>\n      \u003C\u002Ful>\n    \u003C\u002Fsection>\n  \u003C\u002Fmain>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,44333,44334,44344,44358,44366,44380,44400,44413,44421,44440,44447,44457,44482,44493,44504,44508,44512,44519,44529,44534,44567,44572,44577,44594,44606,44618,44631,44642,44646,44650,44656,44666,44671,44683,44694,44707,44711,44718,44735,44747,44763,44798,44802,44807,44813,44819,44823,44827,44834,44844,44856,44867,44878,44890,44904,44908,44912,44919,44929,44941,44951,44961,44965,44969,44996,45012,45016,45023,45033,45044,45060,45070,45083,45101,45113,45123,45134,45138,45143,45149,45155,45163,45171,45182,45197,45209,45223,45228,45233,45238,45244,45262,45292,45297,45306,45315,45324,45341,45356,45377,45393,45414,45435,45444,45460,45474,45488,45497,45506,45511,45526,45546,45561,45581,45602,45611,45626,45640,45649,45658,45663,45678,45698,45713,45733,45753,45762,45777,45791,45805,45819,45828,45837,45846,45855],{"__ignoreMap":286},[290,44335,44336,44338,44340,44342],{"class":163,"line":292},[290,44337,8982],{"class":295},[290,44339,35913],{"class":299},[290,44341,8988],{"class":303},[290,44343,327],{"class":295},[290,44345,44346,44348,44350,44352,44354,44356],{"class":163,"line":330},[290,44347,296],{"class":295},[290,44349,285],{"class":299},[290,44351,8999],{"class":303},[290,44353,307],{"class":295},[290,44355,9004],{"class":310},[290,44357,327],{"class":295},[290,44359,44360,44362,44364],{"class":163,"line":337},[290,44361,296],{"class":295},[290,44363,9013],{"class":299},[290,44365,327],{"class":295},[290,44367,44368,44370,44372,44374,44376,44378],{"class":163,"line":364},[290,44369,296],{"class":295},[290,44371,9022],{"class":299},[290,44373,9025],{"class":303},[290,44375,307],{"class":295},[290,44377,9030],{"class":310},[290,44379,327],{"class":295},[290,44381,44382,44384,44386,44388,44390,44392,44394,44396,44398],{"class":163,"line":386},[290,44383,296],{"class":295},[290,44385,9022],{"class":299},[290,44387,35962],{"class":303},[290,44389,307],{"class":295},[290,44391,35967],{"class":310},[290,44393,35970],{"class":303},[290,44395,307],{"class":295},[290,44397,35975],{"class":310},[290,44399,327],{"class":295},[290,44401,44402,44404,44406,44409,44411],{"class":163,"line":408},[290,44403,296],{"class":295},[290,44405,142],{"class":299},[290,44407,44408],{"class":295},">Subgrid dashboard\u003C\u002F",[290,44410,142],{"class":299},[290,44412,327],{"class":295},[290,44414,44415,44417,44419],{"class":163,"line":428},[290,44416,296],{"class":295},[290,44418,1430],{"class":299},[290,44420,327],{"class":295},[290,44422,44423,44425,44427,44430,44432,44435,44438],{"class":163,"line":517},[290,44424,3430],{"class":303},[290,44426,790],{"class":295},[290,44428,44429],{"class":461},"color-scheme",[290,44431,465],{"class":295},[290,44433,44434],{"class":461},"light",[290,44436,44437],{"class":461}," dark",[290,44439,809],{"class":295},[290,44441,44442,44445],{"class":163,"line":523},[290,44443,44444],{"class":299},"  body",[290,44446,450],{"class":295},[290,44448,44449,44451,44453,44455],{"class":163,"line":532},[290,44450,39260],{"class":461},[290,44452,465],{"class":295},[290,44454,487],{"class":461},[290,44456,471],{"class":295},[290,44458,44459,44462,44464,44466,44468,44470,44472,44475,44477,44480],{"class":163,"line":551},[290,44460,44461],{"class":461},"    font",[290,44463,465],{"class":295},[290,44465,9212],{"class":461},[290,44467,674],{"class":541},[290,44469,3041],{"class":295},[290,44471,168],{"class":461},[290,44473,44474],{"class":461}," system-ui",[290,44476,569],{"class":295},[290,44478,44479],{"class":461},"sans-serif",[290,44481,471],{"class":295},[290,44483,44484,44486,44488,44491],{"class":163,"line":586},[290,44485,9124],{"class":461},[290,44487,465],{"class":295},[290,44489,44490],{"class":461},"#f4f6fb",[290,44492,471],{"class":295},[290,44494,44495,44497,44499,44502],{"class":163,"line":602},[290,44496,36064],{"class":461},[290,44498,465],{"class":295},[290,44500,44501],{"class":461},"#1c2430",[290,44503,471],{"class":295},[290,44505,44506],{"class":163,"line":617},[290,44507,771],{"class":295},[290,44509,44510],{"class":163,"line":623},[290,44511,334],{"emptyLinePlaceholder":333},[290,44513,44514,44517],{"class":163,"line":628},[290,44515,44516],{"class":303},"  .dashboard",[290,44518,450],{"class":295},[290,44520,44521,44523,44525,44527],{"class":163,"line":634},[290,44522,34590],{"class":461},[290,44524,465],{"class":295},[290,44526,9147],{"class":461},[290,44528,471],{"class":295},[290,44530,44531],{"class":163,"line":649},[290,44532,44533],{"class":455},"    \u002F* Auto-fitting columns: no breakpoints needed for the grid itself. *\u002F\n",[290,44535,44536,44538,44540,44543,44545,44548,44550,44553,44555,44557,44559,44561,44563,44565],{"class":163,"line":660},[290,44537,36011],{"class":461},[290,44539,465],{"class":295},[290,44541,44542],{"class":461},"repeat",[290,44544,484],{"class":295},[290,44546,44547],{"class":461},"auto-fit",[290,44549,569],{"class":295},[290,44551,44552],{"class":461},"minmax",[290,44554,484],{"class":295},[290,44556,9212],{"class":461},[290,44558,801],{"class":541},[290,44560,569],{"class":295},[290,44562,468],{"class":461},[290,44564,11964],{"class":541},[290,44566,11616],{"class":295},[290,44568,44569],{"class":163,"line":688},[290,44570,44571],{"class":455},"    \u002F* The shared row tracks every card will align onto:\n",[290,44573,44574],{"class":163,"line":693},[290,44575,44576],{"class":455},"       title (auto), body (flexible), footer (auto). *\u002F\n",[290,44578,44579,44582,44584,44586,44588,44590,44592],{"class":163,"line":698},[290,44580,44581],{"class":461},"    grid-auto-rows",[290,44583,465],{"class":295},[290,44585,250],{"class":461},[290,44587,804],{"class":461},[290,44589,11964],{"class":541},[290,44591,18149],{"class":461},[290,44593,471],{"class":295},[290,44595,44596,44598,44600,44602,44604],{"class":163,"line":704},[290,44597,36027],{"class":461},[290,44599,465],{"class":295},[290,44601,35120],{"class":461},[290,44603,801],{"class":541},[290,44605,471],{"class":295},[290,44607,44608,44610,44612,44614,44616],{"class":163,"line":710},[290,44609,36040],{"class":461},[290,44611,465],{"class":295},[290,44613,168],{"class":461},[290,44615,801],{"class":541},[290,44617,471],{"class":295},[290,44619,44620,44623,44625,44627,44629],{"class":163,"line":717},[290,44621,44622],{"class":461},"    max-width",[290,44624,465],{"class":295},[290,44626,32042],{"class":461},[290,44628,801],{"class":541},[290,44630,471],{"class":295},[290,44632,44633,44636,44638,44640],{"class":163,"line":730},[290,44634,44635],{"class":461},"    margin-inline",[290,44637,465],{"class":295},[290,44639,250],{"class":461},[290,44641,471],{"class":295},[290,44643,44644],{"class":163,"line":742},[290,44645,771],{"class":295},[290,44647,44648],{"class":163,"line":768},[290,44649,334],{"emptyLinePlaceholder":333},[290,44651,44652,44654],{"class":163,"line":774},[290,44653,9083],{"class":303},[290,44655,450],{"class":295},[290,44657,44658,44660,44662,44664],{"class":163,"line":779},[290,44659,34590],{"class":461},[290,44661,465],{"class":295},[290,44663,9147],{"class":461},[290,44665,471],{"class":295},[290,44667,44668],{"class":163,"line":784},[290,44669,44670],{"class":455},"    \u002F* Span the three implicit rows the parent created, and adopt them. *\u002F\n",[290,44672,44673,44676,44679,44681],{"class":163,"line":812},[290,44674,44675],{"class":461},"    grid-row",[290,44677,44678],{"class":295},": span ",[290,44680,1579],{"class":461},[290,44682,471],{"class":295},[290,44684,44685,44688,44690,44692],{"class":163,"line":860},[290,44686,44687],{"class":461},"    grid-template-rows",[290,44689,465],{"class":295},[290,44691,44299],{"class":461},[290,44693,471],{"class":295},[290,44695,44696,44699,44701,44703,44705],{"class":163,"line":865},[290,44697,44698],{"class":461},"    row-gap",[290,44700,465],{"class":295},[290,44702,823],{"class":461},[290,44704,801],{"class":541},[290,44706,471],{"class":295},[290,44708,44709],{"class":163,"line":871},[290,44710,334],{"emptyLinePlaceholder":333},[290,44712,44713,44715],{"class":163,"line":880},[290,44714,9124],{"class":461},[290,44716,44717],{"class":295},": Canvas;\n",[290,44719,44720,44722,44724,44726,44728,44730,44733],{"class":163,"line":896},[290,44721,21285],{"class":461},[290,44723,465],{"class":295},[290,44725,468],{"class":461},[290,44727,674],{"class":541},[290,44729,852],{"class":461},[290,44731,44732],{"class":461}," #d4dbe8",[290,44734,471],{"class":295},[290,44736,44737,44739,44741,44743,44745],{"class":163,"line":4734},[290,44738,12759],{"class":461},[290,44740,465],{"class":295},[290,44742,3589],{"class":461},[290,44744,801],{"class":541},[290,44746,471],{"class":295},[290,44748,44749,44751,44753,44755,44757,44759,44761],{"class":163,"line":4742},[290,44750,36040],{"class":461},[290,44752,465],{"class":295},[290,44754,39227],{"class":461},[290,44756,801],{"class":541},[290,44758,14174],{"class":461},[290,44760,801],{"class":541},[290,44762,471],{"class":295},[290,44764,44765,44767,44769,44771,44773,44775,44777,44779,44781,44783,44785,44788,44791,44793,44796],{"class":163,"line":4761},[290,44766,3279],{"class":461},[290,44768,465],{"class":295},[290,44770,487],{"class":461},[290,44772,804],{"class":461},[290,44774,674],{"class":541},[290,44776,3290],{"class":461},[290,44778,674],{"class":541},[290,44780,11872],{"class":461},[290,44782,484],{"class":295},[290,44784,1785],{"class":461},[290,44786,44787],{"class":461}," 36",[290,44789,44790],{"class":461}," 48",[290,44792,1203],{"class":295},[290,44794,44795],{"class":461},"0.06",[290,44797,500],{"class":295},[290,44799,44800],{"class":163,"line":4766},[290,44801,334],{"emptyLinePlaceholder":333},[290,44803,44804],{"class":163,"line":4786},[290,44805,44806],{"class":455},"    \u002F* Query the card's own width, not the viewport. *\u002F\n",[290,44808,44809,44811],{"class":163,"line":9635},[290,44810,37025],{"class":461},[290,44812,25565],{"class":295},[290,44814,44815,44817],{"class":163,"line":9641},[290,44816,37980],{"class":461},[290,44818,37983],{"class":295},[290,44820,44821],{"class":163,"line":9647},[290,44822,771],{"class":295},[290,44824,44825],{"class":163,"line":9665},[290,44826,334],{"emptyLinePlaceholder":333},[290,44828,44829,44832],{"class":163,"line":9683},[290,44830,44831],{"class":303},"  .card__title",[290,44833,450],{"class":295},[290,44835,44836,44838,44840,44842],{"class":163,"line":9688},[290,44837,39260],{"class":461},[290,44839,465],{"class":295},[290,44841,487],{"class":461},[290,44843,471],{"class":295},[290,44845,44846,44848,44850,44852,44854],{"class":163,"line":9694},[290,44847,39218],{"class":461},[290,44849,465],{"class":295},[290,44851,1119],{"class":461},[290,44853,801],{"class":541},[290,44855,471],{"class":295},[290,44857,44858,44861,44863,44865],{"class":163,"line":9700},[290,44859,44860],{"class":461},"    font-weight",[290,44862,465],{"class":295},[290,44864,4176],{"class":461},[290,44866,471],{"class":295},[290,44868,44869,44871,44873,44876],{"class":163,"line":9710},[290,44870,36064],{"class":461},[290,44872,465],{"class":295},[290,44874,44875],{"class":461},"#5a6472",[290,44877,471],{"class":295},[290,44879,44880,44883,44885,44888],{"class":163,"line":9716},[290,44881,44882],{"class":461},"    text-transform",[290,44884,465],{"class":295},[290,44886,44887],{"class":461},"uppercase",[290,44889,471],{"class":295},[290,44891,44892,44895,44897,44900,44902],{"class":163,"line":9738},[290,44893,44894],{"class":461},"    letter-spacing",[290,44896,465],{"class":295},[290,44898,44899],{"class":461},"0.03",[290,44901,86],{"class":541},[290,44903,471],{"class":295},[290,44905,44906],{"class":163,"line":9748},[290,44907,771],{"class":295},[290,44909,44910],{"class":163,"line":9754},[290,44911,334],{"emptyLinePlaceholder":333},[290,44913,44914,44917],{"class":163,"line":9760},[290,44915,44916],{"class":303},"  .card__value",[290,44918,450],{"class":295},[290,44920,44921,44923,44925,44927],{"class":163,"line":9777},[290,44922,39260],{"class":461},[290,44924,465],{"class":295},[290,44926,487],{"class":461},[290,44928,471],{"class":295},[290,44930,44931,44933,44935,44937,44939],{"class":163,"line":9787},[290,44932,39218],{"class":461},[290,44934,465],{"class":295},[290,44936,12139],{"class":461},[290,44938,801],{"class":541},[290,44940,471],{"class":295},[290,44942,44943,44945,44947,44949],{"class":163,"line":9793},[290,44944,44860],{"class":461},[290,44946,465],{"class":295},[290,44948,26119],{"class":461},[290,44950,471],{"class":295},[290,44952,44953,44955,44957,44959],{"class":163,"line":9799},[290,44954,39248],{"class":461},[290,44956,465],{"class":295},[290,44958,39227],{"class":461},[290,44960,471],{"class":295},[290,44962,44963],{"class":163,"line":9805},[290,44964,771],{"class":295},[290,44966,44967],{"class":163,"line":9811},[290,44968,334],{"emptyLinePlaceholder":333},[290,44970,44971,44974,44976,44979,44981,44983,44985,44987,44989,44991,44994],{"class":163,"line":9820},[290,44972,44973],{"class":303},"  .card__delta",[290,44975,790],{"class":295},[290,44977,44978],{"class":461},"font-size",[290,44980,465],{"class":295},[290,44982,3589],{"class":461},[290,44984,801],{"class":541},[290,44986,828],{"class":295},[290,44988,9133],{"class":461},[290,44990,465],{"class":295},[290,44992,44993],{"class":461},"#1b8a5a",[290,44995,809],{"class":295},[290,44997,44998,45001,45003,45005,45007,45010],{"class":163,"line":9829},[290,44999,45000],{"class":303},"  .card__delta--down",[290,45002,790],{"class":295},[290,45004,9133],{"class":461},[290,45006,465],{"class":295},[290,45008,45009],{"class":461},"#c23b3b",[290,45011,809],{"class":295},[290,45013,45014],{"class":163,"line":27197},[290,45015,334],{"emptyLinePlaceholder":333},[290,45017,45018,45021],{"class":163,"line":27202},[290,45019,45020],{"class":303},"  .card__footer",[290,45022,450],{"class":295},[290,45024,45025,45027,45029,45031],{"class":163,"line":27209},[290,45026,34590],{"class":461},[290,45028,465],{"class":295},[290,45030,9055],{"class":461},[290,45032,471],{"class":295},[290,45034,45035,45038,45040,45042],{"class":163,"line":27220},[290,45036,45037],{"class":461},"    flex-wrap",[290,45039,465],{"class":295},[290,45041,9065],{"class":461},[290,45043,471],{"class":295},[290,45045,45046,45048,45050,45052,45054,45056,45058],{"class":163,"line":27235},[290,45047,36027],{"class":461},[290,45049,465],{"class":295},[290,45051,798],{"class":461},[290,45053,801],{"class":541},[290,45055,804],{"class":461},[290,45057,801],{"class":541},[290,45059,471],{"class":295},[290,45061,45062,45064,45066,45068],{"class":163,"line":27240},[290,45063,39260],{"class":461},[290,45065,465],{"class":295},[290,45067,487],{"class":461},[290,45069,471],{"class":295},[290,45071,45072,45075,45077,45079,45081],{"class":163,"line":43950},[290,45073,45074],{"class":461},"    padding-top",[290,45076,465],{"class":295},[290,45078,798],{"class":461},[290,45080,801],{"class":541},[290,45082,471],{"class":295},[290,45084,45085,45088,45090,45092,45094,45096,45099],{"class":163,"line":43959},[290,45086,45087],{"class":461},"    border-top",[290,45089,465],{"class":295},[290,45091,468],{"class":461},[290,45093,674],{"class":541},[290,45095,852],{"class":461},[290,45097,45098],{"class":461}," #e6eaf2",[290,45100,471],{"class":295},[290,45102,45103,45105,45107,45109,45111],{"class":163,"line":43968},[290,45104,39218],{"class":461},[290,45106,465],{"class":295},[290,45108,17985],{"class":461},[290,45110,801],{"class":541},[290,45112,471],{"class":295},[290,45114,45115,45117,45119,45121],{"class":163,"line":43977},[290,45116,36064],{"class":461},[290,45118,465],{"class":295},[290,45120,44875],{"class":461},[290,45122,471],{"class":295},[290,45124,45125,45128,45130,45132],{"class":163,"line":43986},[290,45126,45127],{"class":461},"    list-style",[290,45129,465],{"class":295},[290,45131,72],{"class":461},[290,45133,471],{"class":295},[290,45135,45136],{"class":163,"line":43995},[290,45137,771],{"class":295},[290,45139,45141],{"class":163,"line":45140},77,[290,45142,334],{"emptyLinePlaceholder":333},[290,45144,45146],{"class":163,"line":45145},78,[290,45147,45148],{"class":455},"  \u002F* Card-level responsiveness: when a single card is wide,\n",[290,45150,45152],{"class":163,"line":45151},79,[290,45153,45154],{"class":455},"     place the value beside its delta instead of stacking. *\u002F\n",[290,45156,45158,45160],{"class":163,"line":45157},80,[290,45159,37040],{"class":541},[290,45161,45162],{"class":295}," card (min-width: 18rem) {\n",[290,45164,45166,45169],{"class":163,"line":45165},81,[290,45167,45168],{"class":303},"    .card__body",[290,45170,450],{"class":295},[290,45172,45174,45176,45178,45180],{"class":163,"line":45173},82,[290,45175,37053],{"class":461},[290,45177,465],{"class":295},[290,45179,9147],{"class":461},[290,45181,471],{"class":295},[290,45183,45185,45187,45189,45191,45193,45195],{"class":163,"line":45184},83,[290,45186,36130],{"class":461},[290,45188,465],{"class":295},[290,45190,468],{"class":461},[290,45192,11964],{"class":541},[290,45194,18149],{"class":461},[290,45196,471],{"class":295},[290,45198,45200,45202,45204,45207],{"class":163,"line":45199},84,[290,45201,36150],{"class":461},[290,45203,465],{"class":295},[290,45205,45206],{"class":461},"baseline",[290,45208,471],{"class":295},[290,45210,45212,45215,45217,45219,45221],{"class":163,"line":45211},85,[290,45213,45214],{"class":461},"      column-gap",[290,45216,465],{"class":295},[290,45218,823],{"class":461},[290,45220,801],{"class":541},[290,45222,471],{"class":295},[290,45224,45226],{"class":163,"line":45225},86,[290,45227,8200],{"class":295},[290,45229,45231],{"class":163,"line":45230},87,[290,45232,771],{"class":295},[290,45234,45236],{"class":163,"line":45235},88,[290,45237,334],{"emptyLinePlaceholder":333},[290,45239,45241],{"class":163,"line":45240},89,[290,45242,45243],{"class":455},"  \u002F* Older browsers: keep cards usable without subgrid alignment. *\u002F\n",[290,45245,45247,45249,45251,45253,45256,45258,45260],{"class":163,"line":45246},90,[290,45248,36183],{"class":541},[290,45250,2116],{"class":541},[290,45252,3595],{"class":295},[290,45254,45255],{"class":461},"grid-template-rows",[290,45257,465],{"class":295},[290,45259,44299],{"class":461},[290,45261,646],{"class":295},[290,45263,45265,45267,45269,45272,45274,45276,45278,45280,45282,45284,45286,45288,45290],{"class":163,"line":45264},91,[290,45266,26128],{"class":303},[290,45268,790],{"class":295},[290,45270,45271],{"class":461},"grid-row",[290,45273,465],{"class":295},[290,45275,250],{"class":461},[290,45277,828],{"class":295},[290,45279,45255],{"class":461},[290,45281,465],{"class":295},[290,45283,250],{"class":461},[290,45285,804],{"class":461},[290,45287,11964],{"class":541},[290,45289,18149],{"class":461},[290,45291,809],{"class":295},[290,45293,45295],{"class":163,"line":45294},92,[290,45296,771],{"class":295},[290,45298,45300,45302,45304],{"class":163,"line":45299},93,[290,45301,431],{"class":295},[290,45303,1430],{"class":299},[290,45305,327],{"class":295},[290,45307,45309,45311,45313],{"class":163,"line":45308},94,[290,45310,431],{"class":295},[290,45312,9013],{"class":299},[290,45314,327],{"class":295},[290,45316,45318,45320,45322],{"class":163,"line":45317},95,[290,45319,296],{"class":295},[290,45321,9239],{"class":299},[290,45323,327],{"class":295},[290,45325,45327,45329,45332,45334,45336,45339],{"class":163,"line":45326},96,[290,45328,367],{"class":295},[290,45330,45331],{"class":299},"main",[290,45333,314],{"class":303},[290,45335,307],{"class":295},[290,45337,45338],{"class":310},"\"dashboard\"",[290,45340,327],{"class":295},[290,45342,45344,45346,45348,45350,45352,45354],{"class":163,"line":45343},97,[290,45345,4290],{"class":295},[290,45347,5944],{"class":299},[290,45349,314],{"class":303},[290,45351,307],{"class":295},[290,45353,9295],{"class":310},[290,45355,327],{"class":295},[290,45357,45359,45361,45363,45365,45367,45370,45373,45375],{"class":163,"line":45358},98,[290,45360,36430],{"class":295},[290,45362,50],{"class":299},[290,45364,314],{"class":303},[290,45366,307],{"class":295},[290,45368,45369],{"class":310},"\"card__title\"",[290,45371,45372],{"class":295},">Monthly Recurring Revenue\u003C\u002F",[290,45374,50],{"class":299},[290,45376,327],{"class":295},[290,45378,45380,45382,45384,45386,45388,45391],{"class":163,"line":45379},99,[290,45381,36430],{"class":295},[290,45383,342],{"class":299},[290,45385,314],{"class":303},[290,45387,307],{"class":295},[290,45389,45390],{"class":310},"\"card__body\"",[290,45392,327],{"class":295},[290,45394,45396,45398,45400,45402,45404,45407,45410,45412],{"class":163,"line":45395},100,[290,45397,36461],{"class":295},[290,45399,14],{"class":299},[290,45401,314],{"class":303},[290,45403,307],{"class":295},[290,45405,45406],{"class":310},"\"card__value\"",[290,45408,45409],{"class":295},">$48.2k\u003C\u002F",[290,45411,14],{"class":299},[290,45413,327],{"class":295},[290,45415,45417,45419,45421,45423,45425,45428,45431,45433],{"class":163,"line":45416},101,[290,45418,36461],{"class":295},[290,45420,290],{"class":299},[290,45422,314],{"class":303},[290,45424,307],{"class":295},[290,45426,45427],{"class":310},"\"card__delta\"",[290,45429,45430],{"class":295},">+12.4%\u003C\u002F",[290,45432,290],{"class":299},[290,45434,327],{"class":295},[290,45436,45438,45440,45442],{"class":163,"line":45437},102,[290,45439,36493],{"class":295},[290,45441,342],{"class":299},[290,45443,327],{"class":295},[290,45445,45447,45449,45451,45453,45455,45458],{"class":163,"line":45446},103,[290,45448,36430],{"class":295},[290,45450,1393],{"class":299},[290,45452,314],{"class":303},[290,45454,307],{"class":295},[290,45456,45457],{"class":310},"\"card__footer\"",[290,45459,327],{"class":295},[290,45461,45463,45465,45467,45470,45472],{"class":163,"line":45462},104,[290,45464,36461],{"class":295},[290,45466,1396],{"class":299},[290,45468,45469],{"class":295},">New $6.1k\u003C\u002F",[290,45471,1396],{"class":299},[290,45473,327],{"class":295},[290,45475,45477,45479,45481,45484,45486],{"class":163,"line":45476},105,[290,45478,36461],{"class":295},[290,45480,1396],{"class":299},[290,45482,45483],{"class":295},">Churn $1.3k\u003C\u002F",[290,45485,1396],{"class":299},[290,45487,327],{"class":295},[290,45489,45491,45493,45495],{"class":163,"line":45490},106,[290,45492,36493],{"class":295},[290,45494,1393],{"class":299},[290,45496,327],{"class":295},[290,45498,45500,45502,45504],{"class":163,"line":45499},107,[290,45501,36502],{"class":295},[290,45503,5944],{"class":299},[290,45505,327],{"class":295},[290,45507,45509],{"class":163,"line":45508},108,[290,45510,334],{"emptyLinePlaceholder":333},[290,45512,45514,45516,45518,45520,45522,45524],{"class":163,"line":45513},109,[290,45515,4290],{"class":295},[290,45517,5944],{"class":299},[290,45519,314],{"class":303},[290,45521,307],{"class":295},[290,45523,9295],{"class":310},[290,45525,327],{"class":295},[290,45527,45529,45531,45533,45535,45537,45539,45542,45544],{"class":163,"line":45528},110,[290,45530,36430],{"class":295},[290,45532,50],{"class":299},[290,45534,314],{"class":303},[290,45536,307],{"class":295},[290,45538,45369],{"class":310},[290,45540,45541],{"class":295},">Active Users\u003C\u002F",[290,45543,50],{"class":299},[290,45545,327],{"class":295},[290,45547,45549,45551,45553,45555,45557,45559],{"class":163,"line":45548},111,[290,45550,36430],{"class":295},[290,45552,342],{"class":299},[290,45554,314],{"class":303},[290,45556,307],{"class":295},[290,45558,45390],{"class":310},[290,45560,327],{"class":295},[290,45562,45564,45566,45568,45570,45572,45574,45577,45579],{"class":163,"line":45563},112,[290,45565,36461],{"class":295},[290,45567,14],{"class":299},[290,45569,314],{"class":303},[290,45571,307],{"class":295},[290,45573,45406],{"class":310},[290,45575,45576],{"class":295},">12,940\u003C\u002F",[290,45578,14],{"class":299},[290,45580,327],{"class":295},[290,45582,45584,45586,45588,45590,45592,45595,45598,45600],{"class":163,"line":45583},113,[290,45585,36461],{"class":295},[290,45587,290],{"class":299},[290,45589,314],{"class":303},[290,45591,307],{"class":295},[290,45593,45594],{"class":310},"\"card__delta card__delta--down\"",[290,45596,45597],{"class":295},">-2.1%\u003C\u002F",[290,45599,290],{"class":299},[290,45601,327],{"class":295},[290,45603,45605,45607,45609],{"class":163,"line":45604},114,[290,45606,36493],{"class":295},[290,45608,342],{"class":299},[290,45610,327],{"class":295},[290,45612,45614,45616,45618,45620,45622,45624],{"class":163,"line":45613},115,[290,45615,36430],{"class":295},[290,45617,1393],{"class":299},[290,45619,314],{"class":303},[290,45621,307],{"class":295},[290,45623,45457],{"class":310},[290,45625,327],{"class":295},[290,45627,45629,45631,45633,45636,45638],{"class":163,"line":45628},116,[290,45630,36461],{"class":295},[290,45632,1396],{"class":299},[290,45634,45635],{"class":295},">DAU 4.2k\u003C\u002F",[290,45637,1396],{"class":299},[290,45639,327],{"class":295},[290,45641,45643,45645,45647],{"class":163,"line":45642},117,[290,45644,36493],{"class":295},[290,45646,1393],{"class":299},[290,45648,327],{"class":295},[290,45650,45652,45654,45656],{"class":163,"line":45651},118,[290,45653,36502],{"class":295},[290,45655,5944],{"class":299},[290,45657,327],{"class":295},[290,45659,45661],{"class":163,"line":45660},119,[290,45662,334],{"emptyLinePlaceholder":333},[290,45664,45666,45668,45670,45672,45674,45676],{"class":163,"line":45665},120,[290,45667,4290],{"class":295},[290,45669,5944],{"class":299},[290,45671,314],{"class":303},[290,45673,307],{"class":295},[290,45675,9295],{"class":310},[290,45677,327],{"class":295},[290,45679,45681,45683,45685,45687,45689,45691,45694,45696],{"class":163,"line":45680},121,[290,45682,36430],{"class":295},[290,45684,50],{"class":299},[290,45686,314],{"class":303},[290,45688,307],{"class":295},[290,45690,45369],{"class":310},[290,45692,45693],{"class":295},">Average Response Time Across All Regions\u003C\u002F",[290,45695,50],{"class":299},[290,45697,327],{"class":295},[290,45699,45701,45703,45705,45707,45709,45711],{"class":163,"line":45700},122,[290,45702,36430],{"class":295},[290,45704,342],{"class":299},[290,45706,314],{"class":303},[290,45708,307],{"class":295},[290,45710,45390],{"class":310},[290,45712,327],{"class":295},[290,45714,45716,45718,45720,45722,45724,45726,45729,45731],{"class":163,"line":45715},123,[290,45717,36461],{"class":295},[290,45719,14],{"class":299},[290,45721,314],{"class":303},[290,45723,307],{"class":295},[290,45725,45406],{"class":310},[290,45727,45728],{"class":295},">214ms\u003C\u002F",[290,45730,14],{"class":299},[290,45732,327],{"class":295},[290,45734,45736,45738,45740,45742,45744,45746,45749,45751],{"class":163,"line":45735},124,[290,45737,36461],{"class":295},[290,45739,290],{"class":299},[290,45741,314],{"class":303},[290,45743,307],{"class":295},[290,45745,45427],{"class":310},[290,45747,45748],{"class":295},">+8ms\u003C\u002F",[290,45750,290],{"class":299},[290,45752,327],{"class":295},[290,45754,45756,45758,45760],{"class":163,"line":45755},125,[290,45757,36493],{"class":295},[290,45759,342],{"class":299},[290,45761,327],{"class":295},[290,45763,45765,45767,45769,45771,45773,45775],{"class":163,"line":45764},126,[290,45766,36430],{"class":295},[290,45768,1393],{"class":299},[290,45770,314],{"class":303},[290,45772,307],{"class":295},[290,45774,45457],{"class":310},[290,45776,327],{"class":295},[290,45778,45780,45782,45784,45787,45789],{"class":163,"line":45779},127,[290,45781,36461],{"class":295},[290,45783,1396],{"class":299},[290,45785,45786],{"class":295},">p50 180ms\u003C\u002F",[290,45788,1396],{"class":299},[290,45790,327],{"class":295},[290,45792,45794,45796,45798,45801,45803],{"class":163,"line":45793},128,[290,45795,36461],{"class":295},[290,45797,1396],{"class":299},[290,45799,45800],{"class":295},">p95 410ms\u003C\u002F",[290,45802,1396],{"class":299},[290,45804,327],{"class":295},[290,45806,45808,45810,45812,45815,45817],{"class":163,"line":45807},129,[290,45809,36461],{"class":295},[290,45811,1396],{"class":299},[290,45813,45814],{"class":295},">p99 920ms\u003C\u002F",[290,45816,1396],{"class":299},[290,45818,327],{"class":295},[290,45820,45822,45824,45826],{"class":163,"line":45821},130,[290,45823,36493],{"class":295},[290,45825,1393],{"class":299},[290,45827,327],{"class":295},[290,45829,45831,45833,45835],{"class":163,"line":45830},131,[290,45832,36502],{"class":295},[290,45834,5944],{"class":299},[290,45836,327],{"class":295},[290,45838,45840,45842,45844],{"class":163,"line":45839},132,[290,45841,4315],{"class":295},[290,45843,45331],{"class":299},[290,45845,327],{"class":295},[290,45847,45849,45851,45853],{"class":163,"line":45848},133,[290,45850,431],{"class":295},[290,45852,9239],{"class":299},[290,45854,327],{"class":295},[290,45856,45858,45860,45862],{"class":163,"line":45857},134,[290,45859,431],{"class":295},[290,45861,285],{"class":299},[290,45863,327],{"class":295},[14,45865,45866,45867,45870,45871,42],{},"Notice that the three cards carry wildly different content: a two-line title on the third card, a single footer stat on the second, three on the third. Yet the value rows align, the footer top-borders align, and the cards are the same height per row. That alignment is produced entirely by the parent's ",[18,45868,45869],{},"grid-auto-rows"," plus each card's ",[18,45872,45873],{},"grid-template-rows: subgrid",[133,45875,140,45877,140,45880,140,45883,140,45886,140,45889,140,45892,140,45895,140,45899,140,45902,140,45906,140,45908,140,45910,140,45913,140,45916,140,45919,140,45923,140,45927,140,45930,140,45933,140,45936,140,45939,140,45942],{"viewBox":2588,"role":136,"ariaLabel":45876,"xmlns":138,"style":139},"Three dashboard cards of differing content sharing aligned title, value, and footer rows via subgrid",[142,45878,45879],{},"Dashboard cards aligned by subgrid row lines",[146,45881,45882],{},"Three cards with different amounts of content share three horizontal baselines for the title, value, and footer rows because each card subgrids onto the parent's rows.",[150,45884,45885],{"x":152,"y":26451,"style":1781},"Shared baselines via subgrid rows",[163,45887],{"x1":4146,"y1":4196,"x2":25430,"y2":4196,"stroke":177,"strokeWidth":168,"strokeDashArray":45888},[5911,249],[163,45890],{"x1":4146,"y1":17572,"x2":25430,"y2":17572,"stroke":177,"strokeWidth":168,"strokeDashArray":45891},[5911,249],[163,45893],{"x1":4146,"y1":2637,"x2":25430,"y2":2637,"stroke":177,"strokeWidth":168,"strokeDashArray":45894},[5911,249],[150,45896,45898],{"x":175,"y":159,"style":45897},"text-anchor:start;fill:currentColor;font:11px ui-monospace,monospace;opacity:0.7","title line",[150,45900,45901],{"x":175,"y":1822,"style":45897},"value line",[150,45903,45905],{"x":175,"y":45904,"style":45897},"242","footer line",[171,45907],{"x":4146,"y":1788,"width":6721,"height":538,"rx":247,"fill":177,"opacity":3241},[171,45909],{"x":5914,"y":1788,"width":6721,"height":538,"rx":247,"fill":177,"opacity":3241},[171,45911],{"x":45912,"y":1788,"width":6721,"height":538,"rx":247,"fill":177,"opacity":3241},"484",[150,45914,45915],{"x":5149,"y":9105,"style":10262},"MRR",[150,45917,45918],{"x":152,"y":9105,"style":10262},"Users",[150,45920,45922],{"x":45921,"y":9105,"style":10262},"582","Latency",[150,45924,45926],{"x":5149,"y":17543,"style":45925},"text-anchor:middle;fill:currentColor;font:600 16px sans-serif","$48.2k",[150,45928,45929],{"x":152,"y":17543,"style":45925},"12,940",[150,45931,45932],{"x":45921,"y":17543,"style":45925},"214ms",[150,45934,45935],{"x":5149,"y":220,"style":10252},"2 stats",[150,45937,45938],{"x":152,"y":220,"style":10252},"1 stat",[150,45940,45941],{"x":45921,"y":220,"style":10252},"3 stats",[150,45943,45945],{"x":152,"y":45944,"style":1839},"304","different content, same row lines",[50,45947,36534],{"id":36533},[14,45949,45950,45951,45954,45955,45958,45959,45961,45962,45965,45966,45968],{},"The whole effect hinges on two declarations working as a pair. On the parent, ",[18,45952,45953],{},"grid-auto-rows: auto 1fr auto"," defines the three row tracks that the implicit rows will cycle through. On each card, ",[18,45956,45957],{},"grid-row: span 3"," claims three of those rows and ",[18,45960,45873],{}," makes the card adopt their exact sizes instead of computing its own. Because all cards in a row span the same three parent rows, and those rows size to fit the tallest content in each band, every card's bands align. Drop the ",[18,45963,45964],{},"span 3"," and a card inherits only one line pair, collapsing back to a single shared track; drop ",[18,45967,44299],{}," and each card sizes its rows independently and alignment vanishes. Both halves are required.",[50,45970,45972],{"id":45971},"variation-or-extension-reduced-motion-aware-card-entrance","Variation or extension: reduced-motion aware card entrance",[14,45974,45975,45976,45978],{},"Dashboards often animate cards in on load. Keep that motion optional. The animation below fades and lifts each card, but collapses to an instant fade for anyone who has requested reduced motion, the same discipline taught in the ",[27,45977,1420],{"href":1419}," guide where animating layout-affecting changes is covered.",[281,45980,45982],{"className":438,"code":45981,"language":440,"meta":286,"style":286},"@media (prefers-reduced-motion: no-preference) {\n  .card {\n    animation: card-in 360ms ease-out both;\n  }\n}\n\n@keyframes card-in {\n  from { opacity: 0; transform: translateY(8px); }\n  to   { opacity: 1; transform: none; }\n}\n\n\u002F* Reduced-motion users still get the content, just without movement. *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .card { animation: none; }\n}\n",[18,45983,45984,45990,45996,46013,46017,46021,46025,46034,46062,46084,46088,46092,46097,46103,46117],{"__ignoreMap":286},[290,45985,45986,45988],{"class":163,"line":292},[290,45987,874],{"class":541},[290,45989,16018],{"class":295},[290,45991,45992,45994],{"class":163,"line":330},[290,45993,9083],{"class":303},[290,45995,450],{"class":295},[290,45997,45998,46000,46003,46005,46007,46009,46011],{"class":163,"line":337},[290,45999,6152],{"class":461},[290,46001,46002],{"class":295},": card-in ",[290,46004,152],{"class":461},[290,46006,542],{"class":541},[290,46008,1889],{"class":461},[290,46010,22990],{"class":461},[290,46012,471],{"class":295},[290,46014,46015],{"class":163,"line":364},[290,46016,771],{"class":295},[290,46018,46019],{"class":163,"line":386},[290,46020,620],{"class":295},[290,46022,46023],{"class":163,"line":408},[290,46024,334],{"emptyLinePlaceholder":333},[290,46026,46027,46029,46032],{"class":163,"line":428},[290,46028,3968],{"class":541},[290,46030,46031],{"class":1561}," card-in",[290,46033,450],{"class":295},[290,46035,46036,46038,46040,46042,46044,46046,46048,46050,46052,46054,46056,46058,46060],{"class":163,"line":517},[290,46037,6191],{"class":303},[290,46039,790],{"class":295},[290,46041,76],{"class":461},[290,46043,465],{"class":295},[290,46045,487],{"class":461},[290,46047,828],{"class":295},[290,46049,103],{"class":461},[290,46051,465],{"class":295},[290,46053,481],{"class":461},[290,46055,484],{"class":295},[290,46057,176],{"class":461},[290,46059,674],{"class":541},[290,46061,1122],{"class":295},[290,46063,46064,46066,46068,46070,46072,46074,46076,46078,46080,46082],{"class":163,"line":523},[290,46065,6072],{"class":303},[290,46067,6208],{"class":295},[290,46069,76],{"class":461},[290,46071,465],{"class":295},[290,46073,468],{"class":461},[290,46075,828],{"class":295},[290,46077,103],{"class":461},[290,46079,465],{"class":295},[290,46081,72],{"class":461},[290,46083,809],{"class":295},[290,46085,46086],{"class":163,"line":532},[290,46087,620],{"class":295},[290,46089,46090],{"class":163,"line":551},[290,46091,334],{"emptyLinePlaceholder":333},[290,46093,46094],{"class":163,"line":586},[290,46095,46096],{"class":455},"\u002F* Reduced-motion users still get the content, just without movement. *\u002F\n",[290,46098,46099,46101],{"class":163,"line":602},[290,46100,874],{"class":541},[290,46102,877],{"class":295},[290,46104,46105,46107,46109,46111,46113,46115],{"class":163,"line":617},[290,46106,9083],{"class":303},[290,46108,790],{"class":295},[290,46110,5178],{"class":461},[290,46112,465],{"class":295},[290,46114,72],{"class":461},[290,46116,809],{"class":295},[290,46118,46119],{"class":163,"line":623},[290,46120,620],{"class":295},[50,46122,1299],{"id":1298},[14,46124,46125,46126,46128,46129,46132],{},"Subgrid is supported in Firefox 71+, Safari 16+, and Chrome\u002FEdge 117+, which covers every current evergreen browser as of 2026, and the ",[18,46127,12001],{}," query layer is baseline since 2023. The only feature needing a fallback is subgrid in older versions, which the ",[18,46130,46131],{},"@supports not (grid-template-rows: subgrid)"," block above handles by restoring per-card row tracks; cards stay fully usable, they just lose cross-row baseline alignment.",[50,46134,1316],{"id":1315},[14,46136,46137,46140],{},[62,46138,46139],{},"Why do my dashboard cards refuse to align across a row without subgrid?","\nEach card runs its own independent grid, so its internal rows are sized only by its own content. Subgrid makes every card share the parent grid's row lines, so the title, body, and footer rows line up across all cards.",[14,46142,46143,46146,46147,3942,46149,42],{},[62,46144,46145],{},"How many parent rows should a card span when using subgrid?","\nSpan exactly as many rows as the shared internal sections you want aligned. For a title, body, and footer card that is three rows, written as ",[18,46148,45957],{},[18,46150,45873],{},[14,46152,46153,46156],{},[62,46154,46155],{},"Can I combine subgrid alignment with container queries on the same card?","\nYes. Subgrid governs how the card aligns with its siblings on the dashboard grid, while a container query restyles the card's interior based on the card's own width. They operate on different layers and do not conflict.",[14,46158,46159,46162,46163,46166],{},[62,46160,46161],{},"What happens to subgrid in browsers that do not support it?","\nWithout a gate the card falls back to its own row tracks and loses cross-row alignment but stays usable. Wrap the subgrid rules in ",[18,46164,46165],{},"@supports (grid-template-rows: subgrid)"," and provide fixed inner rows as the baseline.",[50,46168,1391],{"id":1390},[1393,46170,46171,46176,46183,46188],{},[1396,46172,46173,46175],{},[27,46174,44307],{"href":44306}," — the parent guide covering track sizing and subgrid in depth.",[1396,46177,46178,46182],{},[27,46179,46181],{"href":46180},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fholy-grail-layout-with-grid\u002F","Holy-Grail Layout with Grid"," — named areas for a full page skeleton.",[1396,46184,46185,46187],{},[27,46186,10100],{"href":1426}," — card internals that adapt to their own width.",[1396,46189,46190,46192],{},[27,46191,1420],{"href":1419}," — animate card entrances accessibly.",[1430,46194,46195],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":286,"searchDepth":330,"depth":330,"links":46197},[46198,46199,46200,46201,46202,46203,46204,46205],{"id":35782,"depth":330,"text":35783},{"id":35808,"depth":330,"text":35809},{"id":4202,"depth":330,"text":4203},{"id":36533,"depth":330,"text":36534},{"id":45971,"depth":330,"text":45972},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Build a responsive dashboard where cards align across rows using CSS subgrid, then adapt each card's internals with @container queries — full HTML and CSS.",{"seoTitle":46208,"datePublished":1447,"dateModified":1447,"faq":46209},"Build a Dashboard with CSS Subgrid",[46210,46212,46214,46216],{"q":46139,"a":46211},"Each card runs its own independent grid, so its internal rows are sized only by its own content. Subgrid makes every card share the parent grid's row lines, so the title, body, and footer rows line up across all cards.",{"q":46145,"a":46213},"Span exactly as many rows as the shared internal sections you want aligned. For a title, body, and footer card that is three rows, written as grid-row: span 3 with grid-template-rows: subgrid.",{"q":46155,"a":46215},"Yes. Subgrid governs how the card aligns with its siblings on the dashboard grid, while a container query restyles the card's interior based on the card's own width. They operate on different layers and do not conflict.",{"q":46161,"a":46217},"Without a gate the card falls back to its own row tracks and loses cross-row alignment but stays usable. Wrap the subgrid rules in @supports (grid-template-rows: subgrid) and provide fixed inner rows as the baseline.","\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fbuilding-a-dashboard-with-subgrid",{"title":44286,"description":46206},"mastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fbuilding-a-dashboard-with-subgrid\u002Findex","tSvEufb2BBt7goe2ZJTkAkM1QV_EWd9L7us6jDJPL48",{"id":46223,"title":46224,"body":46225,"description":47395,"extension":1444,"meta":47396,"navigation":333,"path":47409,"seo":47410,"stem":47411,"__hash__":47412},"content\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fholy-grail-layout-with-grid\u002Findex.md","The Holy-Grail Layout with CSS Grid and Container Queries",{"type":7,"value":46226,"toc":47385},[46227,46230,46232,46251,46253,46259,46273,46317,46319,47102,47126,47128,47145,47149,47157,47277,47289,47291,47314,47316,47322,47330,47350,47356,47358,47382],[10,46228,46224],{"id":46229},"the-holy-grail-layout-with-css-grid-and-container-queries",[50,46231,35783],{"id":35782},[14,46233,46234,46235,46238,46239,46242,46243,46245,46246,46248,46249,25342],{},"The holy-grail layout is the canonical web page shape: a header across the top, a footer across the bottom, and three columns between them, a navigation rail on the left, a flexible content column in the middle, and a complementary sidebar on the right. The classic requirements are that the center column appears first in the source for accessibility and SEO, the side columns hold their useful widths without squeezing the content, the footer sticks to the bottom even on short pages, and the whole thing collapses to a single column on narrow screens. For two decades this required float hacks or fragile flexbox nesting. With CSS Grid and ",[18,46236,46237],{},"grid-template-areas"," it is a dozen lines. This page builds that layout with named areas and ",[18,46240,46241],{},"minmax()",", then makes the widgets inside it component-adaptive with ",[18,46244,12001],{},". It sits under ",[27,46247,11296],{"href":5777}," and extends the ",[27,46250,44307],{"href":44306},[50,46252,35809],{"id":35808},[14,46254,46255,46256,46258],{},"A JavaScript layout engine or a CSS framework grid is overkill here; the page skeleton is fundamentally a static two-dimensional arrangement, which is precisely what Grid was designed for. ",[18,46257,46237],{}," is the right tool over raw line numbers because the template strings are a literal ASCII picture of the page, so the layout is readable at a glance and re-mappable per breakpoint without touching markup. That decoupling of visual position from source order is the key accessibility win: you author the HTML in reading order (content first) and let the grid paint it wherever it belongs, so keyboard and screen-reader users get a sane traversal regardless of the visual column order.",[14,46260,46261,46263,46264,46266,46267,46269,46270,46272],{},[18,46262,46241],{}," earns its place because side columns have a usability range, not a single correct width. A pure ",[18,46265,11964],{}," track would let the nav shrink to nothing on medium screens; a fixed ",[18,46268,801],{}," width would not flex at all. ",[18,46271,46241],{}," expresses the real constraint. Where this approach has limits: do not reach for container queries to collapse the columns, because the page skeleton responds to the viewport, not to a component's width. Use viewport media queries for the macro layout and reserve container queries for the widgets inside the regions.",[133,46274,140,46276,140,46279,140,46282,140,46285,140,46287,140,46289,140,46291,140,46293,140,46296,140,46298,140,46300,140,46303,140,46305,140,46308,140,46311,140,46314],{"viewBox":6614,"role":136,"ariaLabel":46275,"xmlns":138,"style":139},"Holy-grail layout area map with header and footer full width and three columns nav, main, and aside between them",[142,46277,46278],{},"Holy-grail layout area map",[146,46280,46281],{},"A labeled diagram showing a full-width header, a full-width footer, and three middle columns named nav, main, and aside, sized with minmax on the sides and 1fr in the center.",[150,46283,46284],{"x":152,"y":153,"style":154},"grid-template-areas map",[171,46286],{"x":4146,"y":5139,"width":14990,"height":1788,"rx":176,"fill":177,"opacity":566,"stroke":167,"strokeWidth":1789},[150,46288,4218],{"x":152,"y":2605,"style":19647},[171,46290],{"x":4146,"y":1843,"width":1822,"height":2602,"rx":176,"fill":177,"opacity":40019,"stroke":167,"strokeWidth":1789},[150,46292,18917],{"x":12450,"y":174,"style":19647},[150,46294,46295],{"x":12450,"y":209,"style":10252},"minmax(12,16rem)",[171,46297],{"x":209,"y":1843,"width":35897,"height":2602,"rx":176,"fill":177,"opacity":11349,"stroke":167,"strokeWidth":1789},[150,46299,45331],{"x":152,"y":174,"style":19647},[150,46301,46302],{"x":152,"y":209,"style":10252},"1fr (content first)",[171,46304],{"x":2626,"y":1843,"width":1822,"height":2602,"rx":176,"fill":177,"opacity":40019,"stroke":167,"strokeWidth":1789},[150,46306,46307],{"x":12469,"y":174,"style":19647},"aside",[150,46309,46310],{"x":12469,"y":209,"style":10252},"minmax(10,14rem)",[171,46312],{"x":4146,"y":46313,"width":14990,"height":5139,"rx":176,"fill":177,"opacity":566,"stroke":167,"strokeWidth":1789},"308",[150,46315,46316],{"x":152,"y":8944,"style":19647},"footer",[50,46318,4203],{"id":4202},[281,46320,46322],{"className":283,"code":46321,"language":285,"meta":286,"style":286},"\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\u003Ctitle>Holy-grail layout\u003C\u002Ftitle>\n\u003Cstyle>\n  :root { color-scheme: light dark; }\n  * { box-sizing: border-box; }\n  body { margin: 0; font: 16px\u002F1.5 system-ui, sans-serif; color: #1c2430; }\n\n  .page {\n    display: grid;\n    min-height: 100dvh;           \u002F* Footer sticks to the bottom on short pages. *\u002F\n    grid-template-columns:\n      minmax(12rem, 16rem)        \u002F* nav: never too narrow, never too wide *\u002F\n      1fr                          \u002F* main: absorbs remaining space *\u002F\n      minmax(10rem, 14rem);       \u002F* aside: clamped complementary column *\u002F\n    grid-template-rows: auto 1fr auto; \u002F* header, content band, footer *\u002F\n    grid-template-areas:\n      \"header header header\"\n      \"nav    main   aside\"\n      \"footer footer footer\";\n  }\n\n  .page__header { grid-area: header; background: #1c2a4a; color: #fff; padding: 1rem 1.5rem; }\n  .page__nav    { grid-area: nav;    background: #eef2fa; padding: 1rem; }\n  .page__main   { grid-area: main;   padding: 1.5rem; }\n  .page__aside  { grid-area: aside;  background: #f6f4ee; padding: 1rem; }\n  .page__footer { grid-area: footer; background: #1c2a4a; color: #fff; padding: 1rem 1.5rem; }\n\n  \u002F* Collapse to a single column on narrow viewports by re-mapping\n     the SAME areas — no markup change required. *\u002F\n  @media (max-width: 48rem) {\n    .page {\n      grid-template-columns: 1fr;\n      grid-template-areas:\n        \"header\"\n        \"main\"\n        \"nav\"\n        \"aside\"\n        \"footer\";\n    }\n  }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv class=\"page\">\n    \u003Cheader class=\"page__header\">Site header\u003C\u002Fheader>\n    \u003C!-- Content first in source for accessibility; grid paints it center. -->\n    \u003Cmain class=\"page__main\">\n      \u003Ch1>Main content\u003C\u002Fh1>\n      \u003Cp>The center column comes first in the DOM but renders between the rails.\u003C\u002Fp>\n    \u003C\u002Fmain>\n    \u003Cnav class=\"page__nav\" aria-label=\"Primary\">Navigation\u003C\u002Fnav>\n    \u003Caside class=\"page__aside\">Sidebar\u003C\u002Faside>\n    \u003Cfooter class=\"page__footer\">Site footer\u003C\u002Ffooter>\n  \u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,46323,46324,46334,46348,46356,46370,46390,46403,46411,46427,46443,46486,46490,46497,46507,46524,46530,46553,46563,46585,46604,46611,46616,46621,46628,46632,46636,46680,46712,46734,46765,46807,46811,46816,46821,46837,46844,46856,46863,46868,46873,46878,46883,46890,46894,46898,46906,46914,46922,46937,46957,46962,46977,46990,47003,47011,47038,47058,47078,47086,47094],{"__ignoreMap":286},[290,46325,46326,46328,46330,46332],{"class":163,"line":292},[290,46327,8982],{"class":295},[290,46329,35913],{"class":299},[290,46331,8988],{"class":303},[290,46333,327],{"class":295},[290,46335,46336,46338,46340,46342,46344,46346],{"class":163,"line":330},[290,46337,296],{"class":295},[290,46339,285],{"class":299},[290,46341,8999],{"class":303},[290,46343,307],{"class":295},[290,46345,9004],{"class":310},[290,46347,327],{"class":295},[290,46349,46350,46352,46354],{"class":163,"line":337},[290,46351,296],{"class":295},[290,46353,9013],{"class":299},[290,46355,327],{"class":295},[290,46357,46358,46360,46362,46364,46366,46368],{"class":163,"line":364},[290,46359,296],{"class":295},[290,46361,9022],{"class":299},[290,46363,9025],{"class":303},[290,46365,307],{"class":295},[290,46367,9030],{"class":310},[290,46369,327],{"class":295},[290,46371,46372,46374,46376,46378,46380,46382,46384,46386,46388],{"class":163,"line":386},[290,46373,296],{"class":295},[290,46375,9022],{"class":299},[290,46377,35962],{"class":303},[290,46379,307],{"class":295},[290,46381,35967],{"class":310},[290,46383,35970],{"class":303},[290,46385,307],{"class":295},[290,46387,35975],{"class":310},[290,46389,327],{"class":295},[290,46391,46392,46394,46396,46399,46401],{"class":163,"line":408},[290,46393,296],{"class":295},[290,46395,142],{"class":299},[290,46397,46398],{"class":295},">Holy-grail layout\u003C\u002F",[290,46400,142],{"class":299},[290,46402,327],{"class":295},[290,46404,46405,46407,46409],{"class":163,"line":428},[290,46406,296],{"class":295},[290,46408,1430],{"class":299},[290,46410,327],{"class":295},[290,46412,46413,46415,46417,46419,46421,46423,46425],{"class":163,"line":517},[290,46414,3430],{"class":303},[290,46416,790],{"class":295},[290,46418,44429],{"class":461},[290,46420,465],{"class":295},[290,46422,44434],{"class":461},[290,46424,44437],{"class":461},[290,46426,809],{"class":295},[290,46428,46429,46431,46433,46436,46438,46441],{"class":163,"line":523},[290,46430,2802],{"class":299},[290,46432,790],{"class":295},[290,46434,46435],{"class":461},"box-sizing",[290,46437,465],{"class":295},[290,46439,46440],{"class":461},"border-box",[290,46442,809],{"class":295},[290,46444,46445,46447,46449,46451,46453,46455,46457,46460,46462,46464,46466,46468,46470,46472,46474,46476,46478,46480,46482,46484],{"class":163,"line":532},[290,46446,44444],{"class":299},[290,46448,790],{"class":295},[290,46450,2725],{"class":461},[290,46452,465],{"class":295},[290,46454,487],{"class":461},[290,46456,828],{"class":295},[290,46458,46459],{"class":461},"font",[290,46461,465],{"class":295},[290,46463,9212],{"class":461},[290,46465,674],{"class":541},[290,46467,3041],{"class":295},[290,46469,168],{"class":461},[290,46471,44474],{"class":461},[290,46473,569],{"class":295},[290,46475,44479],{"class":461},[290,46477,828],{"class":295},[290,46479,9133],{"class":461},[290,46481,465],{"class":295},[290,46483,44501],{"class":461},[290,46485,809],{"class":295},[290,46487,46488],{"class":163,"line":551},[290,46489,334],{"emptyLinePlaceholder":333},[290,46491,46492,46495],{"class":163,"line":586},[290,46493,46494],{"class":303},"  .page",[290,46496,450],{"class":295},[290,46498,46499,46501,46503,46505],{"class":163,"line":602},[290,46500,34590],{"class":461},[290,46502,465],{"class":295},[290,46504,9147],{"class":461},[290,46506,471],{"class":295},[290,46508,46509,46511,46513,46515,46518,46521],{"class":163,"line":617},[290,46510,20469],{"class":461},[290,46512,465],{"class":295},[290,46514,165],{"class":461},[290,46516,46517],{"class":541},"dvh",[290,46519,46520],{"class":295},";           ",[290,46522,46523],{"class":455},"\u002F* Footer sticks to the bottom on short pages. *\u002F\n",[290,46525,46526,46528],{"class":163,"line":623},[290,46527,36011],{"class":461},[290,46529,529],{"class":295},[290,46531,46532,46535,46537,46539,46541,46543,46545,46547,46550],{"class":163,"line":628},[290,46533,46534],{"class":461},"      minmax",[290,46536,484],{"class":295},[290,46538,5894],{"class":461},[290,46540,801],{"class":541},[290,46542,569],{"class":295},[290,46544,9212],{"class":461},[290,46546,801],{"class":541},[290,46548,46549],{"class":295},")        ",[290,46551,46552],{"class":455},"\u002F* nav: never too narrow, never too wide *\u002F\n",[290,46554,46555,46558,46560],{"class":163,"line":634},[290,46556,46557],{"class":461},"      1",[290,46559,11964],{"class":541},[290,46561,46562],{"class":455},"                          \u002F* main: absorbs remaining space *\u002F\n",[290,46564,46565,46567,46569,46571,46573,46575,46577,46579,46582],{"class":163,"line":649},[290,46566,46534],{"class":461},[290,46568,484],{"class":295},[290,46570,836],{"class":461},[290,46572,801],{"class":541},[290,46574,569],{"class":295},[290,46576,43253],{"class":461},[290,46578,801],{"class":541},[290,46580,46581],{"class":295},");       ",[290,46583,46584],{"class":455},"\u002F* aside: clamped complementary column *\u002F\n",[290,46586,46587,46589,46591,46593,46595,46597,46599,46601],{"class":163,"line":660},[290,46588,44687],{"class":461},[290,46590,465],{"class":295},[290,46592,250],{"class":461},[290,46594,804],{"class":461},[290,46596,11964],{"class":541},[290,46598,18149],{"class":461},[290,46600,828],{"class":295},[290,46602,46603],{"class":455},"\u002F* header, content band, footer *\u002F\n",[290,46605,46606,46609],{"class":163,"line":688},[290,46607,46608],{"class":461},"    grid-template-areas",[290,46610,529],{"class":295},[290,46612,46613],{"class":163,"line":693},[290,46614,46615],{"class":310},"      \"header header header\"\n",[290,46617,46618],{"class":163,"line":698},[290,46619,46620],{"class":310},"      \"nav    main   aside\"\n",[290,46622,46623,46626],{"class":163,"line":704},[290,46624,46625],{"class":310},"      \"footer footer footer\"",[290,46627,471],{"class":295},[290,46629,46630],{"class":163,"line":710},[290,46631,771],{"class":295},[290,46633,46634],{"class":163,"line":717},[290,46635,334],{"emptyLinePlaceholder":333},[290,46637,46638,46641,46643,46646,46649,46651,46653,46656,46658,46660,46662,46664,46666,46668,46670,46672,46674,46676,46678],{"class":163,"line":730},[290,46639,46640],{"class":303},"  .page__header",[290,46642,790],{"class":295},[290,46644,46645],{"class":461},"grid-area",[290,46647,46648],{"class":295},": header; ",[290,46650,1217],{"class":461},[290,46652,465],{"class":295},[290,46654,46655],{"class":461},"#1c2a4a",[290,46657,828],{"class":295},[290,46659,9133],{"class":461},[290,46661,465],{"class":295},[290,46663,9138],{"class":461},[290,46665,828],{"class":295},[290,46667,793],{"class":461},[290,46669,465],{"class":295},[290,46671,468],{"class":461},[290,46673,801],{"class":541},[290,46675,10442],{"class":461},[290,46677,801],{"class":541},[290,46679,809],{"class":295},[290,46681,46682,46685,46688,46690,46693,46695,46697,46700,46702,46704,46706,46708,46710],{"class":163,"line":742},[290,46683,46684],{"class":303},"  .page__nav",[290,46686,46687],{"class":295},"    { ",[290,46689,46645],{"class":461},[290,46691,46692],{"class":295},": nav;    ",[290,46694,1217],{"class":461},[290,46696,465],{"class":295},[290,46698,46699],{"class":461},"#eef2fa",[290,46701,828],{"class":295},[290,46703,793],{"class":461},[290,46705,465],{"class":295},[290,46707,468],{"class":461},[290,46709,801],{"class":541},[290,46711,809],{"class":295},[290,46713,46714,46717,46719,46721,46724,46726,46728,46730,46732],{"class":163,"line":768},[290,46715,46716],{"class":303},"  .page__main",[290,46718,6208],{"class":295},[290,46720,46645],{"class":461},[290,46722,46723],{"class":295},": main;   ",[290,46725,793],{"class":461},[290,46727,465],{"class":295},[290,46729,168],{"class":461},[290,46731,801],{"class":541},[290,46733,809],{"class":295},[290,46735,46736,46739,46741,46743,46746,46748,46750,46753,46755,46757,46759,46761,46763],{"class":163,"line":774},[290,46737,46738],{"class":303},"  .page__aside",[290,46740,4612],{"class":295},[290,46742,46645],{"class":461},[290,46744,46745],{"class":295},": aside;  ",[290,46747,1217],{"class":461},[290,46749,465],{"class":295},[290,46751,46752],{"class":461},"#f6f4ee",[290,46754,828],{"class":295},[290,46756,793],{"class":461},[290,46758,465],{"class":295},[290,46760,468],{"class":461},[290,46762,801],{"class":541},[290,46764,809],{"class":295},[290,46766,46767,46770,46772,46774,46777,46779,46781,46783,46785,46787,46789,46791,46793,46795,46797,46799,46801,46803,46805],{"class":163,"line":779},[290,46768,46769],{"class":303},"  .page__footer",[290,46771,790],{"class":295},[290,46773,46645],{"class":461},[290,46775,46776],{"class":295},": footer; ",[290,46778,1217],{"class":461},[290,46780,465],{"class":295},[290,46782,46655],{"class":461},[290,46784,828],{"class":295},[290,46786,9133],{"class":461},[290,46788,465],{"class":295},[290,46790,9138],{"class":461},[290,46792,828],{"class":295},[290,46794,793],{"class":461},[290,46796,465],{"class":295},[290,46798,468],{"class":461},[290,46800,801],{"class":541},[290,46802,10442],{"class":461},[290,46804,801],{"class":541},[290,46806,809],{"class":295},[290,46808,46809],{"class":163,"line":784},[290,46810,334],{"emptyLinePlaceholder":333},[290,46812,46813],{"class":163,"line":812},[290,46814,46815],{"class":455},"  \u002F* Collapse to a single column on narrow viewports by re-mapping\n",[290,46817,46818],{"class":163,"line":860},[290,46819,46820],{"class":455},"     the SAME areas — no markup change required. *\u002F\n",[290,46822,46823,46825,46827,46829,46831,46833,46835],{"class":163,"line":865},[290,46824,26109],{"class":541},[290,46826,3595],{"class":295},[290,46828,41191],{"class":461},[290,46830,465],{"class":295},[290,46832,5139],{"class":461},[290,46834,801],{"class":541},[290,46836,646],{"class":295},[290,46838,46839,46842],{"class":163,"line":871},[290,46840,46841],{"class":303},"    .page",[290,46843,450],{"class":295},[290,46845,46846,46848,46850,46852,46854],{"class":163,"line":880},[290,46847,36130],{"class":461},[290,46849,465],{"class":295},[290,46851,468],{"class":461},[290,46853,11964],{"class":541},[290,46855,471],{"class":295},[290,46857,46858,46861],{"class":163,"line":896},[290,46859,46860],{"class":461},"      grid-template-areas",[290,46862,529],{"class":295},[290,46864,46865],{"class":163,"line":4734},[290,46866,46867],{"class":310},"        \"header\"\n",[290,46869,46870],{"class":163,"line":4742},[290,46871,46872],{"class":310},"        \"main\"\n",[290,46874,46875],{"class":163,"line":4761},[290,46876,46877],{"class":310},"        \"nav\"\n",[290,46879,46880],{"class":163,"line":4766},[290,46881,46882],{"class":310},"        \"aside\"\n",[290,46884,46885,46888],{"class":163,"line":4786},[290,46886,46887],{"class":310},"        \"footer\"",[290,46889,471],{"class":295},[290,46891,46892],{"class":163,"line":9635},[290,46893,8200],{"class":295},[290,46895,46896],{"class":163,"line":9641},[290,46897,771],{"class":295},[290,46899,46900,46902,46904],{"class":163,"line":9647},[290,46901,431],{"class":295},[290,46903,1430],{"class":299},[290,46905,327],{"class":295},[290,46907,46908,46910,46912],{"class":163,"line":9665},[290,46909,431],{"class":295},[290,46911,9013],{"class":299},[290,46913,327],{"class":295},[290,46915,46916,46918,46920],{"class":163,"line":9683},[290,46917,296],{"class":295},[290,46919,9239],{"class":299},[290,46921,327],{"class":295},[290,46923,46924,46926,46928,46930,46932,46935],{"class":163,"line":9688},[290,46925,367],{"class":295},[290,46927,342],{"class":299},[290,46929,314],{"class":303},[290,46931,307],{"class":295},[290,46933,46934],{"class":310},"\"page\"",[290,46936,327],{"class":295},[290,46938,46939,46941,46943,46945,46947,46950,46953,46955],{"class":163,"line":9694},[290,46940,4290],{"class":295},[290,46942,4218],{"class":299},[290,46944,314],{"class":303},[290,46946,307],{"class":295},[290,46948,46949],{"class":310},"\"page__header\"",[290,46951,46952],{"class":295},">Site header\u003C\u002F",[290,46954,4218],{"class":299},[290,46956,327],{"class":295},[290,46958,46959],{"class":163,"line":9700},[290,46960,46961],{"class":455},"    \u003C!-- Content first in source for accessibility; grid paints it center. -->\n",[290,46963,46964,46966,46968,46970,46972,46975],{"class":163,"line":9710},[290,46965,4290],{"class":295},[290,46967,45331],{"class":299},[290,46969,314],{"class":303},[290,46971,307],{"class":295},[290,46973,46974],{"class":310},"\"page__main\"",[290,46976,327],{"class":295},[290,46978,46979,46981,46983,46986,46988],{"class":163,"line":9716},[290,46980,36430],{"class":295},[290,46982,10],{"class":299},[290,46984,46985],{"class":295},">Main content\u003C\u002F",[290,46987,10],{"class":299},[290,46989,327],{"class":295},[290,46991,46992,46994,46996,46999,47001],{"class":163,"line":9738},[290,46993,36430],{"class":295},[290,46995,14],{"class":299},[290,46997,46998],{"class":295},">The center column comes first in the DOM but renders between the rails.\u003C\u002F",[290,47000,14],{"class":299},[290,47002,327],{"class":295},[290,47004,47005,47007,47009],{"class":163,"line":9748},[290,47006,36502],{"class":295},[290,47008,45331],{"class":299},[290,47010,327],{"class":295},[290,47012,47013,47015,47017,47019,47021,47024,47026,47028,47031,47034,47036],{"class":163,"line":9754},[290,47014,4290],{"class":295},[290,47016,18917],{"class":299},[290,47018,314],{"class":303},[290,47020,307],{"class":295},[290,47022,47023],{"class":310},"\"page__nav\"",[290,47025,19002],{"class":303},[290,47027,307],{"class":295},[290,47029,47030],{"class":310},"\"Primary\"",[290,47032,47033],{"class":295},">Navigation\u003C\u002F",[290,47035,18917],{"class":299},[290,47037,327],{"class":295},[290,47039,47040,47042,47044,47046,47048,47051,47054,47056],{"class":163,"line":9760},[290,47041,4290],{"class":295},[290,47043,46307],{"class":299},[290,47045,314],{"class":303},[290,47047,307],{"class":295},[290,47049,47050],{"class":310},"\"page__aside\"",[290,47052,47053],{"class":295},">Sidebar\u003C\u002F",[290,47055,46307],{"class":299},[290,47057,327],{"class":295},[290,47059,47060,47062,47064,47066,47068,47071,47074,47076],{"class":163,"line":9777},[290,47061,4290],{"class":295},[290,47063,46316],{"class":299},[290,47065,314],{"class":303},[290,47067,307],{"class":295},[290,47069,47070],{"class":310},"\"page__footer\"",[290,47072,47073],{"class":295},">Site footer\u003C\u002F",[290,47075,46316],{"class":299},[290,47077,327],{"class":295},[290,47079,47080,47082,47084],{"class":163,"line":9787},[290,47081,4315],{"class":295},[290,47083,342],{"class":299},[290,47085,327],{"class":295},[290,47087,47088,47090,47092],{"class":163,"line":9793},[290,47089,431],{"class":295},[290,47091,9239],{"class":299},[290,47093,327],{"class":295},[290,47095,47096,47098,47100],{"class":163,"line":9799},[290,47097,431],{"class":295},[290,47099,285],{"class":299},[290,47101,327],{"class":295},[14,47103,47104,47105,47108,47109,47112,47113,69,47115,47117,47118,47121,47122,47125],{},"Two details make this robust. ",[18,47106,47107],{},"min-height: 100dvh"," plus the ",[18,47110,47111],{},"auto 1fr auto"," row template gives the classic sticky footer: the middle row stretches to fill leftover height so the footer is pinned to the bottom even when content is short. And the narrow-viewport media query re-declares only ",[18,47114,46237],{},[18,47116,11957],{},", re-mapping the identical markup into a single stacked column. The ",[18,47119,47120],{},"\u003Cmain>"," is first in source but placed in the center visually, satisfying the accessibility requirement without ",[18,47123,47124],{},"order"," tricks.",[50,47127,36534],{"id":36533},[14,47129,47130,47131,69,47133,47135,47136,47138,47139,47141,47142,47144],{},"The mechanism that makes this layout both readable and re-mappable is the relationship between ",[18,47132,46237],{},[18,47134,46645],{},". Each child names the region it occupies with ",[18,47137,46645],{},", and the parent's ",[18,47140,46237],{}," strings spell out a grid of those names, one string per row, one token per column. The browser matches names to regions, so the template is a literal map of the page. Changing the layout, including collapsing three columns to one, means rewriting the strings, never the HTML. Repeating a name across cells (as ",[18,47143,4218],{}," spans all three columns) merges those cells into one rectangular area automatically. This name-based placement is what lets the same DOM serve a desktop three-column shape and a mobile single-column stack from CSS alone.",[50,47146,47148],{"id":47147},"variation-or-extension-container-adaptive-widgets-inside-the-columns","Variation or extension: container-adaptive widgets inside the columns",[14,47150,47151,47152,47154,47155,25342],{},"The page skeleton responds to the viewport, but the widgets living in the nav and aside should respond to the width of their own column, which varies with the ",[18,47153,46241],{}," tracks. Declare containment on a region and let its contents reflow per column width, exactly the pattern from the ",[27,47156,26284],{"href":25340},[281,47158,47160],{"className":438,"code":47159,"language":440,"meta":286,"style":286},".page__aside { container-type: inline-size; container-name: rail; }\n\n\u002F* A promo card stacks by default, goes side-by-side when the rail is wide. *\u002F\n.rail-card { display: grid; gap: 0.5rem; }\n\n@container rail (min-width: 13rem) {\n  .rail-card {\n    grid-template-columns: 3rem 1fr;\n    align-items: center;\n    column-gap: 0.75rem;\n  }\n}\n",[18,47161,47162,47178,47182,47187,47212,47216,47223,47230,47246,47256,47269,47273],{"__ignoreMap":286},[290,47163,47164,47167,47169,47171,47173,47175],{"class":163,"line":292},[290,47165,47166],{"class":303},".page__aside",[290,47168,790],{"class":295},[290,47170,24401],{"class":461},[290,47172,11512],{"class":295},[290,47174,26072],{"class":461},[290,47176,47177],{"class":295},": rail; }\n",[290,47179,47180],{"class":163,"line":330},[290,47181,334],{"emptyLinePlaceholder":333},[290,47183,47184],{"class":163,"line":337},[290,47185,47186],{"class":455},"\u002F* A promo card stacks by default, goes side-by-side when the rail is wide. *\u002F\n",[290,47188,47189,47192,47194,47196,47198,47200,47202,47204,47206,47208,47210],{"class":163,"line":364},[290,47190,47191],{"class":303},".rail-card",[290,47193,790],{"class":295},[290,47195,59],{"class":461},[290,47197,465],{"class":295},[290,47199,9147],{"class":461},[290,47201,828],{"class":295},[290,47203,9070],{"class":461},[290,47205,465],{"class":295},[290,47207,798],{"class":461},[290,47209,801],{"class":541},[290,47211,809],{"class":295},[290,47213,47214],{"class":163,"line":386},[290,47215,334],{"emptyLinePlaceholder":333},[290,47217,47218,47220],{"class":163,"line":408},[290,47219,12001],{"class":541},[290,47221,47222],{"class":295}," rail (min-width: 13rem) {\n",[290,47224,47225,47228],{"class":163,"line":428},[290,47226,47227],{"class":303},"  .rail-card",[290,47229,450],{"class":295},[290,47231,47232,47234,47236,47238,47240,47242,47244],{"class":163,"line":517},[290,47233,36011],{"class":461},[290,47235,465],{"class":295},[290,47237,1579],{"class":461},[290,47239,801],{"class":541},[290,47241,804],{"class":461},[290,47243,11964],{"class":541},[290,47245,471],{"class":295},[290,47247,47248,47250,47252,47254],{"class":163,"line":523},[290,47249,42363],{"class":461},[290,47251,465],{"class":295},[290,47253,9157],{"class":461},[290,47255,471],{"class":295},[290,47257,47258,47261,47263,47265,47267],{"class":163,"line":532},[290,47259,47260],{"class":461},"    column-gap",[290,47262,465],{"class":295},[290,47264,823],{"class":461},[290,47266,801],{"class":541},[290,47268,471],{"class":295},[290,47270,47271],{"class":163,"line":551},[290,47272,771],{"class":295},[290,47274,47275],{"class":163,"line":586},[290,47276,620],{"class":295},[14,47278,47279,47280,47283,47284,3041,47286,47288],{},"For an RTL variant, none of the area logic changes: because the layout uses logical column placement, switching the document to ",[18,47281,47282],{},"direction: rtl"," mirrors the nav and aside automatically, with the ",[18,47285,4218],{},[18,47287,46316],{}," spans unaffected.",[50,47290,1299],{"id":1298},[14,47292,47293,47294,47296,47297,47299,47300,47303,47304,47307,47308,47310,47311,47313],{},"Every feature used here is universally available: CSS Grid and ",[18,47295,46237],{}," ship in Chrome\u002FEdge 57+, Firefox 52+, and Safari 10.1+, and ",[18,47298,46241],{}," arrived alongside Grid. The ",[18,47301,47302],{},"100dvh"," dynamic viewport unit is supported in Chrome\u002FEdge 108+, Safari 15.4+, and Firefox 101+; if you must support older versions, provide a ",[18,47305,47306],{},"min-height: 100vh"," fallback before the ",[18,47309,47302],{}," declaration. The ",[18,47312,12001],{}," enhancement for the rail widgets is baseline since 2023.",[50,47315,1316],{"id":1315},[14,47317,47318,47321],{},[62,47319,47320],{},"What is the holy-grail layout in CSS?","\nIt is a classic page structure with a full-width header and footer plus three middle columns: a fixed-ish left navigation, a flexible center content area, and a fixed-ish right sidebar. CSS Grid solves it cleanly with named template areas.",[14,47323,47324,47329],{},[62,47325,18658,47326,47328],{},[18,47327,46237],{}," instead of line numbers for the holy-grail layout?","\nNamed areas make the layout self-documenting: the template strings visually map to the page, and reordering regions is a one-line change. They also let you re-map the same markup to different shapes at each breakpoint without touching the HTML.",[14,47331,47332,47338,47341,47342,47345,47346,47349],{},[62,47333,47334,47335,47337],{},"How does ",[18,47336,46241],{}," keep the side columns usable?",[18,47339,47340],{},"minmax(min, max)"," clamps a track between a floor and a ceiling. Giving the nav ",[18,47343,47344],{},"minmax(12rem, 16rem)"," means it never collapses below a readable width and never grows past its useful maximum, while the center column on ",[18,47347,47348],{},"1fr"," absorbs the rest.",[14,47351,47352,47355],{},[62,47353,47354],{},"Can container queries change the holy-grail layout?","\nThey change components inside it rather than the page skeleton itself. Use viewport media queries to collapse the three columns to one on small screens, and container queries to adapt the internals of widgets that live in the columns.",[50,47357,1391],{"id":1390},[1393,47359,47360,47365,47372,47377],{},[1396,47361,47362,47364],{},[27,47363,44307],{"href":44306}," — the parent guide on track sizing and subgrid.",[1396,47366,47367,47371],{},[27,47368,47370],{"href":47369},"\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fbuilding-a-dashboard-with-subgrid\u002F","Building a Dashboard with Subgrid"," — align card internals across a grid with subgrid.",[1396,47373,47374,47376],{},[27,47375,37796],{"href":37795}," — when to use viewport versus container breakpoints.",[1396,47378,47379,47381],{},[27,47380,18535],{"href":18534}," — add polish to nav links in the rail.",[1430,47383,47384],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":286,"searchDepth":330,"depth":330,"links":47386},[47387,47388,47389,47390,47391,47392,47393,47394],{"id":35782,"depth":330,"text":35783},{"id":35808,"depth":330,"text":35809},{"id":4202,"depth":330,"text":4203},{"id":36533,"depth":330,"text":36534},{"id":47147,"depth":330,"text":47148},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Build the holy-grail layout (header, footer, three columns) with grid-template-areas and minmax(), then make it component-adaptive with container queries.",{"seoTitle":47397,"datePublished":1447,"dateModified":1447,"faq":47398},"Holy-Grail Layout with CSS Grid",[47399,47401,47404,47407],{"q":47320,"a":47400},"It is a classic page structure with a full-width header and footer plus three middle columns: a fixed-ish left navigation, a flexible center content area, and a fixed-ish right sidebar. CSS Grid solves it cleanly with named template areas.",{"q":47402,"a":47403},"Why use grid-template-areas instead of line numbers for the holy-grail layout?","Named areas make the layout self-documenting: the template strings visually map to the page, and reordering regions is a one-line change. They also let you re-map the same markup to different shapes at each breakpoint without touching the HTML.",{"q":47405,"a":47406},"How does minmax() keep the side columns usable?","minmax(min, max) clamps a track between a floor and a ceiling. Giving the nav minmax(12rem, 16rem) means it never collapses below a readable width and never grows past its useful maximum, while the center column on 1fr absorbs the rest.",{"q":47354,"a":47408},"They change components inside it rather than the page skeleton itself. Use viewport media queries to collapse the three columns to one on small screens, and container queries to adapt the internals of widgets that live in the columns.","\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fholy-grail-layout-with-grid",{"title":46224,"description":47395},"mastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Fholy-grail-layout-with-grid\u002Findex","B6w78KonA9eBMT58erNVrGgfYo2jSGMtzmF6N6XpWD4",{"id":47414,"title":47415,"body":47416,"description":49823,"extension":1444,"meta":49824,"navigation":333,"path":49839,"seo":49840,"stem":49841,"__hash__":49842},"content\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Findex.md","CSS Grid and Subgrid Layouts for Responsive Interfaces",{"type":7,"value":47417,"toc":49802},[47418,47421,47443,47507,47512,47541,47543,47545,47548,47572,47574,47578,47593,47609,47612,47614,47616,47757,47764,47766,47770,47774,47780,47852,47858,47861,48035,48039,48048,48116,48120,48123,48261,48267,48270,48396,48398,48402,48411,49350,49362,49364,49366,49378,49395,49397,49399,49451,49453,49455,49552,49559,49638,49640,49642,49729,49731,49733,49739,49750,49756,49768,49770,49772,49799],[10,47419,47415],{"id":47420},"css-grid-and-subgrid-layouts-for-responsive-interfaces",[14,47422,47423,47424,47426,47427,47429,47430,8393,47432,47434,47435,47437,47438,47442],{},"CSS Grid is the only layout system on the web that controls both axes simultaneously, and ",[18,47425,44299],{}," extends that power across component boundaries so nested elements can share a parent's track lines. This guide sits under ",[27,47428,11296],{"href":5777}," and focuses on building responsive structures with track sizing, ",[18,47431,46237],{},[18,47433,44299],{},", then pairing them with the element-aware breakpoints you learn in the ",[27,47436,26284],{"href":25340}," guide. Where Grid decides where boxes go, the intrinsic keywords covered in ",[27,47439,47441],{"href":47440},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002F","Intrinsic Sizing Techniques"," decide how large each track grows, so the two are constantly used together.",[133,47444,140,47446,140,47449,140,47452,140,47455,140,47457,140,47461,140,47463,140,47466,140,47468,140,47470,140,47472,140,47475,140,47478,140,47482,140,47485,140,47488,140,47492,140,47495,140,47498,140,47501,140,47504],{"viewBox":8866,"role":136,"ariaLabel":47445,"xmlns":138,"style":139},"A parent grid with a child that uses subgrid, showing track lines inherited from the parent",[142,47447,47448],{},"Parent grid sharing track lines with a subgrid child",[146,47450,47451],{},"A three-column parent grid contains a child element declared as subgrid; dashed lines show the child reusing the parent's column lines so its contents align with sibling cells.",[150,47453,47454],{"x":152,"y":153,"style":154},"Subgrid inherits parent track lines",[171,47456],{"x":4146,"y":1788,"width":14990,"height":4147,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},[150,47458,47460],{"x":1788,"y":32042,"style":47459},"text-anchor:start;fill:currentColor;font:13px ui-monospace,monospace;opacity:0.85","parent grid",[163,47462],{"x1":35016,"y1":1788,"x2":35016,"y2":17572,"stroke":177,"strokeWidth":194},[163,47464],{"x1":47465,"y1":1788,"x2":47465,"y2":17572,"stroke":177,"strokeWidth":194},"466",[171,47467],{"x":1788,"y":4196,"width":2602,"height":29485,"rx":5901,"fill":177,"opacity":22004},[171,47469],{"x":2601,"y":4196,"width":2602,"height":29485,"rx":5901,"fill":177,"opacity":22004},[171,47471],{"x":45912,"y":4196,"width":2602,"height":29485,"rx":5901,"fill":177,"opacity":22004},[150,47473,47474],{"x":4150,"y":182,"style":4181},"col 1",[150,47476,47477],{"x":152,"y":182,"style":4181},"col 2",[150,47479,47481],{"x":47480,"y":182,"style":4181},"574","col 3",[171,47483],{"x":1788,"y":6729,"width":47484,"height":4147,"rx":836,"fill":72,"stroke":177,"strokeWidth":168},"608",[150,47486,47487],{"x":5892,"y":6641,"style":47459},"child: subgrid",[163,47489],{"x1":35016,"y1":6729,"x2":35016,"y2":47490,"stroke":177,"strokeWidth":194,"strokeDashArray":47491},"352",[5911,249],[163,47493],{"x1":47465,"y1":6729,"x2":47465,"y2":47490,"stroke":177,"strokeWidth":194,"strokeDashArray":47494},[5911,249],[150,47496,47497],{"x":4150,"y":6740,"style":4181},"aligns to col 1",[150,47499,47500],{"x":152,"y":6740,"style":4181},"aligns to col 2",[150,47502,47503],{"x":47480,"y":6740,"style":4181},"aligns to col 3",[150,47505,47506],{"x":152,"y":22584,"style":1839},"dashed lines = inherited from parent, not redefined",[14,47508,47509],{},[62,47510,47511],{},"Key implementation points:",[1393,47513,47514,47525,47531,47536],{},[1396,47515,47516,47517,569,47519,569,47521,47524],{},"Sizing tracks with ",[18,47518,11964],{},[18,47520,46241],{},[18,47522,47523],{},"repeat()",", and intrinsic keywords",[1396,47526,47527,47528,47530],{},"Naming regions with ",[18,47529,46237],{}," for self-documenting layouts",[1396,47532,47533,47534],{},"Propagating track lines into nested components with ",[18,47535,44299],{},[1396,47537,47538,47539],{},"Adapting a Grid component's internals with ",[18,47540,12001],{},[47,47542],{},[50,47544,6747],{"id":6746},[14,47546,47547],{},"This guide assumes you are comfortable with the core layout primitives before adding subgrid on top:",[1393,47549,47550,47557,47563,47566],{},[1396,47551,47552,47553,47556],{},"You can set ",[18,47554,47555],{},"display: grid"," and read the difference between explicit and implicit tracks.",[1396,47558,47559,47560,47562],{},"You understand ",[18,47561,11964],{}," units and how they distribute leftover space after fixed and content-based tracks resolve.",[1396,47564,47565],{},"You know what a grid line is (the numbered boundaries between tracks) versus a grid track (the space between two lines).",[1396,47567,47568,47569,47571],{},"You have a working mental model of ",[18,47570,25409],{},"; if not, start with the container-queries guide linked above.",[47,47573],{},[50,47575,47577],{"id":47576},"core-concept-the-grid-formatting-context-and-track-propagation","Core concept: the grid formatting context and track propagation",[14,47579,47580,47581,47583,47584,47587,47588,69,47590,47592],{},"When you set ",[18,47582,47555],{},", the element becomes a grid container and establishes a ",[62,47585,47586],{},"grid formatting context",". The container computes a set of column lines and row lines from ",[18,47589,11957],{},[18,47591,45255],{},"; every direct child is then placed between those lines, either automatically or via explicit placement.",[14,47594,47595,47596,47598,47599,12091,47602,47604,47605,47608],{},"A normal nested grid starts a brand-new, independent formatting context. Its lines bear no relationship to the parent's lines, which is why two sibling cards laid out with independent inner grids never align their internal rows. ",[18,47597,44299],{}," changes this. Per the CSS Grid Layout Module Level 2 specification, declaring ",[18,47600,47601],{},"grid-template-columns: subgrid",[18,47603,45873],{},") tells the element to ",[62,47606,47607],{},"adopt the track sizes and line positions of the area it spans in its parent grid",", rather than computing its own. The subgrid still creates a formatting context for its own children, but the lines those children place against are the parent's lines projected into the child.",[14,47610,47611],{},"The practical consequence: items inside separate subgrids that span the same parent tracks line up to the pixel, with no shared wrapper and no magic numbers. This is the mechanism that finally makes \"align the buttons across a row of cards\" a pure-CSS problem.",[47,47613],{},[50,47615,6867],{"id":6866},[2250,47617,47618,47628],{},[2253,47619,47620],{},[2256,47621,47622,47624,47626],{},[2259,47623,23133],{},[2259,47625,6882],{},[2259,47627,6885],{},[2269,47629,47630,47656,47670,47687,47705,47720,47741],{},[2256,47631,47632,47638,47652],{},[2274,47633,47634,1203,47636],{},[18,47635,11957],{},[18,47637,45255],{},[2274,47639,47640,47641,569,47643,569,47645,47647,47648,47651],{},"track list, ",[18,47642,72],{},[18,47644,44299],{},[18,47646,47523],{}," expressions, ",[18,47649,47650],{},"masonry"," (experimental)",[2274,47653,47654],{},[18,47655,72],{},[2256,47657,47658,47662,47668],{},[2274,47659,47660],{},[18,47661,44299],{},[2274,47663,47664,47665],{},"used as the entire value of a template axis; optionally followed by a line-name list in ",[18,47666,47667],{},"[ ]",[2274,47669,6903],{},[2256,47671,47672,47676,47685],{},[2274,47673,47674],{},[18,47675,47340],{},[2274,47677,47678,47679,47681,47682,47684],{},"any two track sizes; ",[18,47680,32765],{}," cannot be an ",[18,47683,11964],{}," unit",[2274,47686,6903],{},[2256,47688,47689,47694,47703],{},[2274,47690,47691],{},[18,47692,47693],{},"repeat(count, tracks)",[2274,47695,47696,47697,1203,47700,47702],{},"integer count, or ",[18,47698,47699],{},"auto-fill",[18,47701,44547],{}," for intrinsic repetition",[2274,47704,6903],{},[2256,47706,47707,47711,47718],{},[2274,47708,47709],{},[18,47710,11964],{},[2274,47712,47713,47714,47717],{},"positive ",[18,47715,47716],{},"\u003Cflex>"," value distributing free space",[2274,47719,6903],{},[2256,47721,47722,47732,47737],{},[2274,47723,47724,3595,47726,1203,47729,3914],{},[18,47725,9070],{},[18,47727,47728],{},"row-gap",[18,47730,47731],{},"column-gap",[2274,47733,47734,47735],{},"length, percentage, or ",[18,47736,7032],{},[2274,47738,47739],{},[18,47740,7032],{},[2256,47742,47743,47747,47753],{},[2274,47744,47745],{},[18,47746,46237],{},[2274,47748,47749,47750,47752],{},"strings of named cells; ",[18,47751,42],{}," denotes an empty cell",[2274,47754,47755],{},[18,47756,72],{},[14,47758,47759,47760,47763],{},"A subgrid only inherits the axis on which you declare it. You can write ",[18,47761,47762],{},"grid-template-columns: subgrid; grid-template-rows: 1fr auto;"," to share columns with the parent while defining your own rows. Gaps are inherited from the parent along a subgridded axis by default, but you can override them on the subgrid itself.",[47,47765],{},[50,47767,47769],{"id":47768},"step-by-step-implementation","Step-by-step implementation",[2757,47771,47773],{"id":47772},"step-1-build-a-responsive-parent-grid-with-intrinsic-track-sizing","Step 1 — Build a responsive parent grid with intrinsic track sizing",[14,47775,47776,47777,47779],{},"Start with a parent grid that holds a sidebar and a flexible content column. Use ",[18,47778,46241],{}," so the sidebar never collapses below a usable width and never bloats past a maximum.",[281,47781,47783],{"className":438,"code":47782,"language":440,"meta":286,"style":286},".layout {\n  display: grid;\n  \u002F* Sidebar between 14rem and 20rem; content takes the rest. *\u002F\n  grid-template-columns: minmax(14rem, 20rem) 1fr;\n  gap: 2rem;\n}\n",[18,47784,47785,47792,47802,47807,47836,47848],{"__ignoreMap":286},[290,47786,47787,47790],{"class":163,"line":292},[290,47788,47789],{"class":303},".layout",[290,47791,450],{"class":295},[290,47793,47794,47796,47798,47800],{"class":163,"line":330},[290,47795,17742],{"class":461},[290,47797,465],{"class":295},[290,47799,9147],{"class":461},[290,47801,471],{"class":295},[290,47803,47804],{"class":163,"line":337},[290,47805,47806],{"class":455},"  \u002F* Sidebar between 14rem and 20rem; content takes the rest. *\u002F\n",[290,47808,47809,47812,47814,47816,47818,47820,47822,47824,47826,47828,47830,47832,47834],{"class":163,"line":364},[290,47810,47811],{"class":461},"  grid-template-columns",[290,47813,465],{"class":295},[290,47815,44552],{"class":461},[290,47817,484],{"class":295},[290,47819,43253],{"class":461},[290,47821,801],{"class":541},[290,47823,569],{"class":295},[290,47825,1785],{"class":461},[290,47827,801],{"class":541},[290,47829,490],{"class":295},[290,47831,468],{"class":461},[290,47833,11964],{"class":541},[290,47835,471],{"class":295},[290,47837,47838,47840,47842,47844,47846],{"class":163,"line":386},[290,47839,26819],{"class":461},[290,47841,465],{"class":295},[290,47843,194],{"class":461},[290,47845,801],{"class":541},[290,47847,471],{"class":295},[290,47849,47850],{"class":163,"line":408},[290,47851,620],{"class":295},[2757,47853,47855,47856],{"id":47854},"step-2-name-regions-with-grid-template-areas","Step 2 — Name regions with ",[18,47857,46237],{},[14,47859,47860],{},"For multi-region layouts, named areas read far better than line numbers and make the structure obvious at a glance.",[281,47862,47864],{"className":438,"code":47863,"language":440,"meta":286,"style":286},".layout {\n  display: grid;\n  grid-template-columns: minmax(14rem, 20rem) 1fr;\n  grid-template-rows: auto 1fr auto;\n  grid-template-areas:\n    \"sidebar header\"\n    \"sidebar main\"\n    \"sidebar footer\";\n  gap: 1.5rem;\n}\n\n.layout > .sidebar { grid-area: sidebar; }\n.layout > .header  { grid-area: header; }\n.layout > .main    { grid-area: main; }\n.layout > .footer  { grid-area: footer; }\n",[18,47865,47866,47872,47882,47910,47927,47934,47939,47944,47951,47963,47967,47971,47987,48003,48019],{"__ignoreMap":286},[290,47867,47868,47870],{"class":163,"line":292},[290,47869,47789],{"class":303},[290,47871,450],{"class":295},[290,47873,47874,47876,47878,47880],{"class":163,"line":330},[290,47875,17742],{"class":461},[290,47877,465],{"class":295},[290,47879,9147],{"class":461},[290,47881,471],{"class":295},[290,47883,47884,47886,47888,47890,47892,47894,47896,47898,47900,47902,47904,47906,47908],{"class":163,"line":337},[290,47885,47811],{"class":461},[290,47887,465],{"class":295},[290,47889,44552],{"class":461},[290,47891,484],{"class":295},[290,47893,43253],{"class":461},[290,47895,801],{"class":541},[290,47897,569],{"class":295},[290,47899,1785],{"class":461},[290,47901,801],{"class":541},[290,47903,490],{"class":295},[290,47905,468],{"class":461},[290,47907,11964],{"class":541},[290,47909,471],{"class":295},[290,47911,47912,47915,47917,47919,47921,47923,47925],{"class":163,"line":364},[290,47913,47914],{"class":461},"  grid-template-rows",[290,47916,465],{"class":295},[290,47918,250],{"class":461},[290,47920,804],{"class":461},[290,47922,11964],{"class":541},[290,47924,18149],{"class":461},[290,47926,471],{"class":295},[290,47928,47929,47932],{"class":163,"line":386},[290,47930,47931],{"class":461},"  grid-template-areas",[290,47933,529],{"class":295},[290,47935,47936],{"class":163,"line":408},[290,47937,47938],{"class":310},"    \"sidebar header\"\n",[290,47940,47941],{"class":163,"line":428},[290,47942,47943],{"class":310},"    \"sidebar main\"\n",[290,47945,47946,47949],{"class":163,"line":517},[290,47947,47948],{"class":310},"    \"sidebar footer\"",[290,47950,471],{"class":295},[290,47952,47953,47955,47957,47959,47961],{"class":163,"line":523},[290,47954,26819],{"class":461},[290,47956,465],{"class":295},[290,47958,168],{"class":461},[290,47960,801],{"class":541},[290,47962,471],{"class":295},[290,47964,47965],{"class":163,"line":532},[290,47966,620],{"class":295},[290,47968,47969],{"class":163,"line":551},[290,47970,334],{"emptyLinePlaceholder":333},[290,47972,47973,47975,47977,47980,47982,47984],{"class":163,"line":586},[290,47974,47789],{"class":303},[290,47976,24017],{"class":541},[290,47978,47979],{"class":303}," .sidebar",[290,47981,790],{"class":295},[290,47983,46645],{"class":461},[290,47985,47986],{"class":295},": sidebar; }\n",[290,47988,47989,47991,47993,47996,47998,48000],{"class":163,"line":602},[290,47990,47789],{"class":303},[290,47992,24017],{"class":541},[290,47994,47995],{"class":303}," .header",[290,47997,4612],{"class":295},[290,47999,46645],{"class":461},[290,48001,48002],{"class":295},": header; }\n",[290,48004,48005,48007,48009,48012,48014,48016],{"class":163,"line":617},[290,48006,47789],{"class":303},[290,48008,24017],{"class":541},[290,48010,48011],{"class":303}," .main",[290,48013,46687],{"class":295},[290,48015,46645],{"class":461},[290,48017,48018],{"class":295},": main; }\n",[290,48020,48021,48023,48025,48028,48030,48032],{"class":163,"line":623},[290,48022,47789],{"class":303},[290,48024,24017],{"class":541},[290,48026,48027],{"class":303}," .footer",[290,48029,4612],{"class":295},[290,48031,46645],{"class":461},[290,48033,48034],{"class":295},": footer; }\n",[2757,48036,48038],{"id":48037},"step-3-lay-out-a-repeating-set-of-cards","Step 3 — Lay out a repeating set of cards",[14,48040,1499,48041,3942,48043,69,48045,48047],{},[18,48042,47523],{},[18,48044,44547],{},[18,48046,46241],{}," so the column count adapts to available width without any breakpoints.",[281,48049,48051],{"className":438,"code":48050,"language":440,"meta":286,"style":286},".cards {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));\n  gap: 1.5rem;\n}\n",[18,48052,48053,48060,48070,48100,48112],{"__ignoreMap":286},[290,48054,48055,48058],{"class":163,"line":292},[290,48056,48057],{"class":303},".cards",[290,48059,450],{"class":295},[290,48061,48062,48064,48066,48068],{"class":163,"line":330},[290,48063,17742],{"class":461},[290,48065,465],{"class":295},[290,48067,9147],{"class":461},[290,48069,471],{"class":295},[290,48071,48072,48074,48076,48078,48080,48082,48084,48086,48088,48090,48092,48094,48096,48098],{"class":163,"line":337},[290,48073,47811],{"class":461},[290,48075,465],{"class":295},[290,48077,44542],{"class":461},[290,48079,484],{"class":295},[290,48081,44547],{"class":461},[290,48083,569],{"class":295},[290,48085,44552],{"class":461},[290,48087,484],{"class":295},[290,48089,9212],{"class":461},[290,48091,801],{"class":541},[290,48093,569],{"class":295},[290,48095,468],{"class":461},[290,48097,11964],{"class":541},[290,48099,11616],{"class":295},[290,48101,48102,48104,48106,48108,48110],{"class":163,"line":364},[290,48103,26819],{"class":461},[290,48105,465],{"class":295},[290,48107,168],{"class":461},[290,48109,801],{"class":541},[290,48111,471],{"class":295},[290,48113,48114],{"class":163,"line":386},[290,48115,620],{"class":295},[2757,48117,48119],{"id":48118},"step-4-make-each-card-a-subgrid-so-internals-align-across-the-row","Step 4 — Make each card a subgrid so internals align across the row",[14,48121,48122],{},"Give each card three internal rows (media, body, actions) and have the card inherit those rows from a parent that defines them. The cards now share row lines, so every action bar sits on the same baseline regardless of body length.",[281,48124,48126],{"className":438,"code":48125,"language":440,"meta":286,"style":286},".cards {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));\n  \u002F* Define the three shared row tracks on the parent. *\u002F\n  grid-template-rows: auto 1fr auto;\n  gap: 1.5rem;\n}\n\n.card {\n  display: grid;\n  \u002F* Each card spans all three parent rows and adopts their sizes. *\u002F\n  grid-row: span 3;\n  grid-template-rows: subgrid;\n}\n",[18,48127,48128,48134,48144,48174,48179,48195,48207,48211,48215,48221,48231,48236,48247,48257],{"__ignoreMap":286},[290,48129,48130,48132],{"class":163,"line":292},[290,48131,48057],{"class":303},[290,48133,450],{"class":295},[290,48135,48136,48138,48140,48142],{"class":163,"line":330},[290,48137,17742],{"class":461},[290,48139,465],{"class":295},[290,48141,9147],{"class":461},[290,48143,471],{"class":295},[290,48145,48146,48148,48150,48152,48154,48156,48158,48160,48162,48164,48166,48168,48170,48172],{"class":163,"line":337},[290,48147,47811],{"class":461},[290,48149,465],{"class":295},[290,48151,44542],{"class":461},[290,48153,484],{"class":295},[290,48155,44547],{"class":461},[290,48157,569],{"class":295},[290,48159,44552],{"class":461},[290,48161,484],{"class":295},[290,48163,9212],{"class":461},[290,48165,801],{"class":541},[290,48167,569],{"class":295},[290,48169,468],{"class":461},[290,48171,11964],{"class":541},[290,48173,11616],{"class":295},[290,48175,48176],{"class":163,"line":364},[290,48177,48178],{"class":455},"  \u002F* Define the three shared row tracks on the parent. *\u002F\n",[290,48180,48181,48183,48185,48187,48189,48191,48193],{"class":163,"line":386},[290,48182,47914],{"class":461},[290,48184,465],{"class":295},[290,48186,250],{"class":461},[290,48188,804],{"class":461},[290,48190,11964],{"class":541},[290,48192,18149],{"class":461},[290,48194,471],{"class":295},[290,48196,48197,48199,48201,48203,48205],{"class":163,"line":408},[290,48198,26819],{"class":461},[290,48200,465],{"class":295},[290,48202,168],{"class":461},[290,48204,801],{"class":541},[290,48206,471],{"class":295},[290,48208,48209],{"class":163,"line":428},[290,48210,620],{"class":295},[290,48212,48213],{"class":163,"line":517},[290,48214,334],{"emptyLinePlaceholder":333},[290,48216,48217,48219],{"class":163,"line":523},[290,48218,11528],{"class":303},[290,48220,450],{"class":295},[290,48222,48223,48225,48227,48229],{"class":163,"line":532},[290,48224,17742],{"class":461},[290,48226,465],{"class":295},[290,48228,9147],{"class":461},[290,48230,471],{"class":295},[290,48232,48233],{"class":163,"line":551},[290,48234,48235],{"class":455},"  \u002F* Each card spans all three parent rows and adopts their sizes. *\u002F\n",[290,48237,48238,48241,48243,48245],{"class":163,"line":586},[290,48239,48240],{"class":461},"  grid-row",[290,48242,44678],{"class":295},[290,48244,1579],{"class":461},[290,48246,471],{"class":295},[290,48248,48249,48251,48253,48255],{"class":163,"line":602},[290,48250,47914],{"class":461},[290,48252,465],{"class":295},[290,48254,44299],{"class":461},[290,48256,471],{"class":295},[290,48258,48259],{"class":163,"line":617},[290,48260,620],{"class":295},[2757,48262,48264,48265],{"id":48263},"step-5-adapt-the-cards-internals-with-container","Step 5 — Adapt the card's internals with ",[18,48266,12001],{},[14,48268,48269],{},"Now make each card responsive to its own width rather than the viewport. Declaring containment lets the inner layout reflow per card, which is exactly what you want in a grid where cards vary in width.",[281,48271,48273],{"className":438,"code":48272,"language":440,"meta":286,"style":286},".card {\n  display: grid;\n  grid-row: span 3;\n  grid-template-rows: subgrid;\n  container-type: inline-size;\n  container-name: card;\n}\n\n@container card (min-width: 22rem) {\n  .card__body {\n    \u002F* Wider cards show media beside text instead of stacked. *\u002F\n    display: grid;\n    grid-template-columns: 8rem 1fr;\n    gap: 1rem;\n  }\n}\n",[18,48274,48275,48281,48291,48301,48311,48317,48323,48327,48331,48338,48345,48350,48360,48376,48388,48392],{"__ignoreMap":286},[290,48276,48277,48279],{"class":163,"line":292},[290,48278,11528],{"class":303},[290,48280,450],{"class":295},[290,48282,48283,48285,48287,48289],{"class":163,"line":330},[290,48284,17742],{"class":461},[290,48286,465],{"class":295},[290,48288,9147],{"class":461},[290,48290,471],{"class":295},[290,48292,48293,48295,48297,48299],{"class":163,"line":337},[290,48294,48240],{"class":461},[290,48296,44678],{"class":295},[290,48298,1579],{"class":461},[290,48300,471],{"class":295},[290,48302,48303,48305,48307,48309],{"class":163,"line":364},[290,48304,47914],{"class":461},[290,48306,465],{"class":295},[290,48308,44299],{"class":461},[290,48310,471],{"class":295},[290,48312,48313,48315],{"class":163,"line":386},[290,48314,11509],{"class":461},[290,48316,25565],{"class":295},[290,48318,48319,48321],{"class":163,"line":408},[290,48320,25575],{"class":461},[290,48322,37983],{"class":295},[290,48324,48325],{"class":163,"line":428},[290,48326,620],{"class":295},[290,48328,48329],{"class":163,"line":517},[290,48330,334],{"emptyLinePlaceholder":333},[290,48332,48333,48335],{"class":163,"line":523},[290,48334,12001],{"class":541},[290,48336,48337],{"class":295}," card (min-width: 22rem) {\n",[290,48339,48340,48343],{"class":163,"line":532},[290,48341,48342],{"class":303},"  .card__body",[290,48344,450],{"class":295},[290,48346,48347],{"class":163,"line":551},[290,48348,48349],{"class":455},"    \u002F* Wider cards show media beside text instead of stacked. *\u002F\n",[290,48351,48352,48354,48356,48358],{"class":163,"line":586},[290,48353,34590],{"class":461},[290,48355,465],{"class":295},[290,48357,9147],{"class":461},[290,48359,471],{"class":295},[290,48361,48362,48364,48366,48368,48370,48372,48374],{"class":163,"line":602},[290,48363,36011],{"class":461},[290,48365,465],{"class":295},[290,48367,176],{"class":461},[290,48369,801],{"class":541},[290,48371,804],{"class":461},[290,48373,11964],{"class":541},[290,48375,471],{"class":295},[290,48377,48378,48380,48382,48384,48386],{"class":163,"line":617},[290,48379,36027],{"class":461},[290,48381,465],{"class":295},[290,48383,468],{"class":461},[290,48385,801],{"class":541},[290,48387,471],{"class":295},[290,48389,48390],{"class":163,"line":623},[290,48391,771],{"class":295},[290,48393,48394],{"class":163,"line":628},[290,48395,620],{"class":295},[47,48397],{},[50,48399,48401],{"id":48400},"annotated-production-example","Annotated production example",[14,48403,48404,48405,48407,48408,48410],{},"A complete, copy-paste pricing-cards section. The outer grid auto-fits columns, the shared row tracks plus ",[18,48406,44299],{}," keep titles, descriptions, and call-to-action buttons aligned across all cards, and a ",[18,48409,12001],{}," query upgrades each card's internal layout when it has room.",[281,48412,48414],{"className":283,"code":48413,"language":285,"meta":286,"style":286},"\u003Csection class=\"pricing\">\n  \u003Carticle class=\"plan\">\n    \u003Cheader class=\"plan__head\">\u003Ch3>Starter\u003C\u002Fh3>\u003C\u002Fheader>\n    \u003Cdiv class=\"plan__body\">\n      \u003Cp class=\"plan__price\">$0\u003C\u002Fp>\n      \u003Cp class=\"plan__copy\">For trying things out on a single project.\u003C\u002Fp>\n    \u003C\u002Fdiv>\n    \u003Cfooter class=\"plan__foot\">\u003Ca class=\"plan__cta\" href=\"#\">Choose\u003C\u002Fa>\u003C\u002Ffooter>\n  \u003C\u002Farticle>\n  \u003Carticle class=\"plan\">\n    \u003Cheader class=\"plan__head\">\u003Ch3>Team\u003C\u002Fh3>\u003C\u002Fheader>\n    \u003Cdiv class=\"plan__body\">\n      \u003Cp class=\"plan__price\">$24\u003C\u002Fp>\n      \u003Cp class=\"plan__copy\">Shared workspaces, roles, and longer history for small teams that ship together.\u003C\u002Fp>\n    \u003C\u002Fdiv>\n    \u003Cfooter class=\"plan__foot\">\u003Ca class=\"plan__cta\" href=\"#\">Choose\u003C\u002Fa>\u003C\u002Ffooter>\n  \u003C\u002Farticle>\n  \u003Carticle class=\"plan\">\n    \u003Cheader class=\"plan__head\">\u003Ch3>Scale\u003C\u002Fh3>\u003C\u002Fheader>\n    \u003Cdiv class=\"plan__body\">\n      \u003Cp class=\"plan__price\">$99\u003C\u002Fp>\n      \u003Cp class=\"plan__copy\">SSO and audit logs.\u003C\u002Fp>\n    \u003C\u002Fdiv>\n    \u003Cfooter class=\"plan__foot\">\u003Ca class=\"plan__cta\" href=\"#\">Choose\u003C\u002Fa>\u003C\u002Ffooter>\n  \u003C\u002Farticle>\n\u003C\u002Fsection>\n\n\u003Cstyle>\n  .pricing {\n    display: grid;\n    \u002F* Columns grow and shrink without breakpoints. *\u002F\n    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));\n    \u002F* The three shared rows every card will subgrid onto. *\u002F\n    grid-template-rows: auto 1fr auto;\n    gap: 1.5rem;\n    padding: 1.5rem;\n  }\n\n  .plan {\n    display: grid;\n    grid-row: span 3;            \u002F* Occupy all three parent rows. *\u002F\n    grid-template-rows: subgrid; \u002F* Adopt the parent's row sizes\u002Flines. *\u002F\n    gap: 0.75rem;\n    padding: 1.25rem;\n    border: 1px solid #d0d7e2;\n    border-radius: 0.75rem;\n    container-type: inline-size; \u002F* Query the card, not the viewport. *\u002F\n    container-name: plan;\n  }\n\n  .plan__price { font-size: 2rem; font-weight: 700; margin: 0; }\n  .plan__copy  { margin: 0; color: #5a6472; }\n  .plan__cta {\n    display: inline-block;\n    padding: 0.6rem 1rem;\n    border-radius: 0.5rem;\n    background: #2952cc;\n    color: #fff;\n    text-decoration: none;\n  }\n\n  \u002F* When a single card is wide enough, give the body two columns. *\u002F\n  @container plan (min-width: 20rem) {\n    .plan__body {\n      display: grid;\n      grid-template-columns: max-content 1fr;\n      align-items: baseline;\n      column-gap: 1rem;\n    }\n  }\n\u003C\u002Fstyle>\n",[18,48415,48416,48431,48446,48474,48489,48509,48529,48537,48579,48587,48601,48627,48641,48660,48679,48687,48725,48733,48747,48774,48788,48807,48826,48834,48872,48880,48888,48892,48900,48907,48917,48922,48953,48958,48974,48986,48998,49002,49006,49013,49023,49037,49050,49062,49074,49091,49103,49112,49119,49123,49127,49161,49184,49191,49201,49217,49229,49240,49250,49261,49265,49269,49274,49281,49288,49298,49312,49322,49334,49338,49342],{"__ignoreMap":286},[290,48417,48418,48420,48422,48424,48426,48429],{"class":163,"line":292},[290,48419,296],{"class":295},[290,48421,5944],{"class":299},[290,48423,314],{"class":303},[290,48425,307],{"class":295},[290,48427,48428],{"class":310},"\"pricing\"",[290,48430,327],{"class":295},[290,48432,48433,48435,48437,48439,48441,48444],{"class":163,"line":330},[290,48434,367],{"class":295},[290,48436,11445],{"class":299},[290,48438,314],{"class":303},[290,48440,307],{"class":295},[290,48442,48443],{"class":310},"\"plan\"",[290,48445,327],{"class":295},[290,48447,48448,48450,48452,48454,48456,48459,48461,48463,48466,48468,48470,48472],{"class":163,"line":337},[290,48449,4290],{"class":295},[290,48451,4218],{"class":299},[290,48453,314],{"class":303},[290,48455,307],{"class":295},[290,48457,48458],{"class":310},"\"plan__head\"",[290,48460,9303],{"class":295},[290,48462,2757],{"class":299},[290,48464,48465],{"class":295},">Starter\u003C\u002F",[290,48467,2757],{"class":299},[290,48469,11472],{"class":295},[290,48471,4218],{"class":299},[290,48473,327],{"class":295},[290,48475,48476,48478,48480,48482,48484,48487],{"class":163,"line":364},[290,48477,4290],{"class":295},[290,48479,342],{"class":299},[290,48481,314],{"class":303},[290,48483,307],{"class":295},[290,48485,48486],{"class":310},"\"plan__body\"",[290,48488,327],{"class":295},[290,48490,48491,48493,48495,48497,48499,48502,48505,48507],{"class":163,"line":386},[290,48492,36430],{"class":295},[290,48494,14],{"class":299},[290,48496,314],{"class":303},[290,48498,307],{"class":295},[290,48500,48501],{"class":310},"\"plan__price\"",[290,48503,48504],{"class":295},">$0\u003C\u002F",[290,48506,14],{"class":299},[290,48508,327],{"class":295},[290,48510,48511,48513,48515,48517,48519,48522,48525,48527],{"class":163,"line":408},[290,48512,36430],{"class":295},[290,48514,14],{"class":299},[290,48516,314],{"class":303},[290,48518,307],{"class":295},[290,48520,48521],{"class":310},"\"plan__copy\"",[290,48523,48524],{"class":295},">For trying things out on a single project.\u003C\u002F",[290,48526,14],{"class":299},[290,48528,327],{"class":295},[290,48530,48531,48533,48535],{"class":163,"line":428},[290,48532,36502],{"class":295},[290,48534,342],{"class":299},[290,48536,327],{"class":295},[290,48538,48539,48541,48543,48545,48547,48550,48552,48554,48556,48558,48561,48563,48565,48568,48571,48573,48575,48577],{"class":163,"line":517},[290,48540,4290],{"class":295},[290,48542,46316],{"class":299},[290,48544,314],{"class":303},[290,48546,307],{"class":295},[290,48548,48549],{"class":310},"\"plan__foot\"",[290,48551,9303],{"class":295},[290,48553,27],{"class":299},[290,48555,314],{"class":303},[290,48557,307],{"class":295},[290,48559,48560],{"class":310},"\"plan__cta\"",[290,48562,17687],{"class":303},[290,48564,307],{"class":295},[290,48566,48567],{"class":310},"\"#\"",[290,48569,48570],{"class":295},">Choose\u003C\u002F",[290,48572,27],{"class":299},[290,48574,11472],{"class":295},[290,48576,46316],{"class":299},[290,48578,327],{"class":295},[290,48580,48581,48583,48585],{"class":163,"line":523},[290,48582,4315],{"class":295},[290,48584,11445],{"class":299},[290,48586,327],{"class":295},[290,48588,48589,48591,48593,48595,48597,48599],{"class":163,"line":532},[290,48590,367],{"class":295},[290,48592,11445],{"class":299},[290,48594,314],{"class":303},[290,48596,307],{"class":295},[290,48598,48443],{"class":310},[290,48600,327],{"class":295},[290,48602,48603,48605,48607,48609,48611,48613,48615,48617,48619,48621,48623,48625],{"class":163,"line":551},[290,48604,4290],{"class":295},[290,48606,4218],{"class":299},[290,48608,314],{"class":303},[290,48610,307],{"class":295},[290,48612,48458],{"class":310},[290,48614,9303],{"class":295},[290,48616,2757],{"class":299},[290,48618,32156],{"class":295},[290,48620,2757],{"class":299},[290,48622,11472],{"class":295},[290,48624,4218],{"class":299},[290,48626,327],{"class":295},[290,48628,48629,48631,48633,48635,48637,48639],{"class":163,"line":586},[290,48630,4290],{"class":295},[290,48632,342],{"class":299},[290,48634,314],{"class":303},[290,48636,307],{"class":295},[290,48638,48486],{"class":310},[290,48640,327],{"class":295},[290,48642,48643,48645,48647,48649,48651,48653,48656,48658],{"class":163,"line":602},[290,48644,36430],{"class":295},[290,48646,14],{"class":299},[290,48648,314],{"class":303},[290,48650,307],{"class":295},[290,48652,48501],{"class":310},[290,48654,48655],{"class":295},">$24\u003C\u002F",[290,48657,14],{"class":299},[290,48659,327],{"class":295},[290,48661,48662,48664,48666,48668,48670,48672,48675,48677],{"class":163,"line":617},[290,48663,36430],{"class":295},[290,48665,14],{"class":299},[290,48667,314],{"class":303},[290,48669,307],{"class":295},[290,48671,48521],{"class":310},[290,48673,48674],{"class":295},">Shared workspaces, roles, and longer history for small teams that ship together.\u003C\u002F",[290,48676,14],{"class":299},[290,48678,327],{"class":295},[290,48680,48681,48683,48685],{"class":163,"line":623},[290,48682,36502],{"class":295},[290,48684,342],{"class":299},[290,48686,327],{"class":295},[290,48688,48689,48691,48693,48695,48697,48699,48701,48703,48705,48707,48709,48711,48713,48715,48717,48719,48721,48723],{"class":163,"line":628},[290,48690,4290],{"class":295},[290,48692,46316],{"class":299},[290,48694,314],{"class":303},[290,48696,307],{"class":295},[290,48698,48549],{"class":310},[290,48700,9303],{"class":295},[290,48702,27],{"class":299},[290,48704,314],{"class":303},[290,48706,307],{"class":295},[290,48708,48560],{"class":310},[290,48710,17687],{"class":303},[290,48712,307],{"class":295},[290,48714,48567],{"class":310},[290,48716,48570],{"class":295},[290,48718,27],{"class":299},[290,48720,11472],{"class":295},[290,48722,46316],{"class":299},[290,48724,327],{"class":295},[290,48726,48727,48729,48731],{"class":163,"line":634},[290,48728,4315],{"class":295},[290,48730,11445],{"class":299},[290,48732,327],{"class":295},[290,48734,48735,48737,48739,48741,48743,48745],{"class":163,"line":649},[290,48736,367],{"class":295},[290,48738,11445],{"class":299},[290,48740,314],{"class":303},[290,48742,307],{"class":295},[290,48744,48443],{"class":310},[290,48746,327],{"class":295},[290,48748,48749,48751,48753,48755,48757,48759,48761,48763,48766,48768,48770,48772],{"class":163,"line":660},[290,48750,4290],{"class":295},[290,48752,4218],{"class":299},[290,48754,314],{"class":303},[290,48756,307],{"class":295},[290,48758,48458],{"class":310},[290,48760,9303],{"class":295},[290,48762,2757],{"class":299},[290,48764,48765],{"class":295},">Scale\u003C\u002F",[290,48767,2757],{"class":299},[290,48769,11472],{"class":295},[290,48771,4218],{"class":299},[290,48773,327],{"class":295},[290,48775,48776,48778,48780,48782,48784,48786],{"class":163,"line":688},[290,48777,4290],{"class":295},[290,48779,342],{"class":299},[290,48781,314],{"class":303},[290,48783,307],{"class":295},[290,48785,48486],{"class":310},[290,48787,327],{"class":295},[290,48789,48790,48792,48794,48796,48798,48800,48803,48805],{"class":163,"line":693},[290,48791,36430],{"class":295},[290,48793,14],{"class":299},[290,48795,314],{"class":303},[290,48797,307],{"class":295},[290,48799,48501],{"class":310},[290,48801,48802],{"class":295},">$99\u003C\u002F",[290,48804,14],{"class":299},[290,48806,327],{"class":295},[290,48808,48809,48811,48813,48815,48817,48819,48822,48824],{"class":163,"line":698},[290,48810,36430],{"class":295},[290,48812,14],{"class":299},[290,48814,314],{"class":303},[290,48816,307],{"class":295},[290,48818,48521],{"class":310},[290,48820,48821],{"class":295},">SSO and audit logs.\u003C\u002F",[290,48823,14],{"class":299},[290,48825,327],{"class":295},[290,48827,48828,48830,48832],{"class":163,"line":704},[290,48829,36502],{"class":295},[290,48831,342],{"class":299},[290,48833,327],{"class":295},[290,48835,48836,48838,48840,48842,48844,48846,48848,48850,48852,48854,48856,48858,48860,48862,48864,48866,48868,48870],{"class":163,"line":710},[290,48837,4290],{"class":295},[290,48839,46316],{"class":299},[290,48841,314],{"class":303},[290,48843,307],{"class":295},[290,48845,48549],{"class":310},[290,48847,9303],{"class":295},[290,48849,27],{"class":299},[290,48851,314],{"class":303},[290,48853,307],{"class":295},[290,48855,48560],{"class":310},[290,48857,17687],{"class":303},[290,48859,307],{"class":295},[290,48861,48567],{"class":310},[290,48863,48570],{"class":295},[290,48865,27],{"class":299},[290,48867,11472],{"class":295},[290,48869,46316],{"class":299},[290,48871,327],{"class":295},[290,48873,48874,48876,48878],{"class":163,"line":717},[290,48875,4315],{"class":295},[290,48877,11445],{"class":299},[290,48879,327],{"class":295},[290,48881,48882,48884,48886],{"class":163,"line":730},[290,48883,431],{"class":295},[290,48885,5944],{"class":299},[290,48887,327],{"class":295},[290,48889,48890],{"class":163,"line":742},[290,48891,334],{"emptyLinePlaceholder":333},[290,48893,48894,48896,48898],{"class":163,"line":768},[290,48895,296],{"class":295},[290,48897,1430],{"class":299},[290,48899,327],{"class":295},[290,48901,48902,48905],{"class":163,"line":774},[290,48903,48904],{"class":303},"  .pricing",[290,48906,450],{"class":295},[290,48908,48909,48911,48913,48915],{"class":163,"line":779},[290,48910,34590],{"class":461},[290,48912,465],{"class":295},[290,48914,9147],{"class":461},[290,48916,471],{"class":295},[290,48918,48919],{"class":163,"line":784},[290,48920,48921],{"class":455},"    \u002F* Columns grow and shrink without breakpoints. *\u002F\n",[290,48923,48924,48926,48928,48930,48932,48934,48936,48938,48940,48943,48945,48947,48949,48951],{"class":163,"line":812},[290,48925,36011],{"class":461},[290,48927,465],{"class":295},[290,48929,44542],{"class":461},[290,48931,484],{"class":295},[290,48933,44547],{"class":461},[290,48935,569],{"class":295},[290,48937,44552],{"class":461},[290,48939,484],{"class":295},[290,48941,48942],{"class":461},"15",[290,48944,801],{"class":541},[290,48946,569],{"class":295},[290,48948,468],{"class":461},[290,48950,11964],{"class":541},[290,48952,11616],{"class":295},[290,48954,48955],{"class":163,"line":860},[290,48956,48957],{"class":455},"    \u002F* The three shared rows every card will subgrid onto. *\u002F\n",[290,48959,48960,48962,48964,48966,48968,48970,48972],{"class":163,"line":865},[290,48961,44687],{"class":461},[290,48963,465],{"class":295},[290,48965,250],{"class":461},[290,48967,804],{"class":461},[290,48969,11964],{"class":541},[290,48971,18149],{"class":461},[290,48973,471],{"class":295},[290,48975,48976,48978,48980,48982,48984],{"class":163,"line":871},[290,48977,36027],{"class":461},[290,48979,465],{"class":295},[290,48981,168],{"class":461},[290,48983,801],{"class":541},[290,48985,471],{"class":295},[290,48987,48988,48990,48992,48994,48996],{"class":163,"line":880},[290,48989,36040],{"class":461},[290,48991,465],{"class":295},[290,48993,168],{"class":461},[290,48995,801],{"class":541},[290,48997,471],{"class":295},[290,48999,49000],{"class":163,"line":896},[290,49001,771],{"class":295},[290,49003,49004],{"class":163,"line":4734},[290,49005,334],{"emptyLinePlaceholder":333},[290,49007,49008,49011],{"class":163,"line":4742},[290,49009,49010],{"class":303},"  .plan",[290,49012,450],{"class":295},[290,49014,49015,49017,49019,49021],{"class":163,"line":4761},[290,49016,34590],{"class":461},[290,49018,465],{"class":295},[290,49020,9147],{"class":461},[290,49022,471],{"class":295},[290,49024,49025,49027,49029,49031,49034],{"class":163,"line":4766},[290,49026,44675],{"class":461},[290,49028,44678],{"class":295},[290,49030,1579],{"class":461},[290,49032,49033],{"class":295},";            ",[290,49035,49036],{"class":455},"\u002F* Occupy all three parent rows. *\u002F\n",[290,49038,49039,49041,49043,49045,49047],{"class":163,"line":4786},[290,49040,44687],{"class":461},[290,49042,465],{"class":295},[290,49044,44299],{"class":461},[290,49046,828],{"class":295},[290,49048,49049],{"class":455},"\u002F* Adopt the parent's row sizes\u002Flines. *\u002F\n",[290,49051,49052,49054,49056,49058,49060],{"class":163,"line":9635},[290,49053,36027],{"class":461},[290,49055,465],{"class":295},[290,49057,823],{"class":461},[290,49059,801],{"class":541},[290,49061,471],{"class":295},[290,49063,49064,49066,49068,49070,49072],{"class":163,"line":9641},[290,49065,36040],{"class":461},[290,49067,465],{"class":295},[290,49069,35120],{"class":461},[290,49071,801],{"class":541},[290,49073,471],{"class":295},[290,49075,49076,49078,49080,49082,49084,49086,49089],{"class":163,"line":9647},[290,49077,21285],{"class":461},[290,49079,465],{"class":295},[290,49081,468],{"class":461},[290,49083,674],{"class":541},[290,49085,852],{"class":461},[290,49087,49088],{"class":461}," #d0d7e2",[290,49090,471],{"class":295},[290,49092,49093,49095,49097,49099,49101],{"class":163,"line":9665},[290,49094,12759],{"class":461},[290,49096,465],{"class":295},[290,49098,823],{"class":461},[290,49100,801],{"class":541},[290,49102,471],{"class":295},[290,49104,49105,49107,49109],{"class":163,"line":9683},[290,49106,37025],{"class":461},[290,49108,11512],{"class":295},[290,49110,49111],{"class":455},"\u002F* Query the card, not the viewport. *\u002F\n",[290,49113,49114,49116],{"class":163,"line":9688},[290,49115,37980],{"class":461},[290,49117,49118],{"class":295},": plan;\n",[290,49120,49121],{"class":163,"line":9694},[290,49122,771],{"class":295},[290,49124,49125],{"class":163,"line":9700},[290,49126,334],{"emptyLinePlaceholder":333},[290,49128,49129,49132,49134,49136,49138,49140,49142,49144,49147,49149,49151,49153,49155,49157,49159],{"class":163,"line":9710},[290,49130,49131],{"class":303},"  .plan__price",[290,49133,790],{"class":295},[290,49135,44978],{"class":461},[290,49137,465],{"class":295},[290,49139,194],{"class":461},[290,49141,801],{"class":541},[290,49143,828],{"class":295},[290,49145,49146],{"class":461},"font-weight",[290,49148,465],{"class":295},[290,49150,26119],{"class":461},[290,49152,828],{"class":295},[290,49154,2725],{"class":461},[290,49156,465],{"class":295},[290,49158,487],{"class":461},[290,49160,809],{"class":295},[290,49162,49163,49166,49168,49170,49172,49174,49176,49178,49180,49182],{"class":163,"line":9716},[290,49164,49165],{"class":303},"  .plan__copy",[290,49167,4612],{"class":295},[290,49169,2725],{"class":461},[290,49171,465],{"class":295},[290,49173,487],{"class":461},[290,49175,828],{"class":295},[290,49177,9133],{"class":461},[290,49179,465],{"class":295},[290,49181,44875],{"class":461},[290,49183,809],{"class":295},[290,49185,49186,49189],{"class":163,"line":9738},[290,49187,49188],{"class":303},"  .plan__cta",[290,49190,450],{"class":295},[290,49192,49193,49195,49197,49199],{"class":163,"line":9748},[290,49194,34590],{"class":461},[290,49196,465],{"class":295},[290,49198,17747],{"class":461},[290,49200,471],{"class":295},[290,49202,49203,49205,49207,49209,49211,49213,49215],{"class":163,"line":9754},[290,49204,36040],{"class":461},[290,49206,465],{"class":295},[290,49208,232],{"class":461},[290,49210,801],{"class":541},[290,49212,804],{"class":461},[290,49214,801],{"class":541},[290,49216,471],{"class":295},[290,49218,49219,49221,49223,49225,49227],{"class":163,"line":9760},[290,49220,12759],{"class":461},[290,49222,465],{"class":295},[290,49224,798],{"class":461},[290,49226,801],{"class":541},[290,49228,471],{"class":295},[290,49230,49231,49233,49235,49238],{"class":163,"line":9777},[290,49232,9124],{"class":461},[290,49234,465],{"class":295},[290,49236,49237],{"class":461},"#2952cc",[290,49239,471],{"class":295},[290,49241,49242,49244,49246,49248],{"class":163,"line":9787},[290,49243,36064],{"class":461},[290,49245,465],{"class":295},[290,49247,9138],{"class":461},[290,49249,471],{"class":295},[290,49251,49252,49255,49257,49259],{"class":163,"line":9793},[290,49253,49254],{"class":461},"    text-decoration",[290,49256,465],{"class":295},[290,49258,72],{"class":461},[290,49260,471],{"class":295},[290,49262,49263],{"class":163,"line":9799},[290,49264,771],{"class":295},[290,49266,49267],{"class":163,"line":9805},[290,49268,334],{"emptyLinePlaceholder":333},[290,49270,49271],{"class":163,"line":9811},[290,49272,49273],{"class":455},"  \u002F* When a single card is wide enough, give the body two columns. *\u002F\n",[290,49275,49276,49278],{"class":163,"line":9820},[290,49277,37040],{"class":541},[290,49279,49280],{"class":295}," plan (min-width: 20rem) {\n",[290,49282,49283,49286],{"class":163,"line":9829},[290,49284,49285],{"class":303},"    .plan__body",[290,49287,450],{"class":295},[290,49289,49290,49292,49294,49296],{"class":163,"line":27197},[290,49291,37053],{"class":461},[290,49293,465],{"class":295},[290,49295,9147],{"class":461},[290,49297,471],{"class":295},[290,49299,49300,49302,49304,49306,49308,49310],{"class":163,"line":27202},[290,49301,36130],{"class":461},[290,49303,465],{"class":295},[290,49305,17909],{"class":461},[290,49307,804],{"class":461},[290,49309,11964],{"class":541},[290,49311,471],{"class":295},[290,49313,49314,49316,49318,49320],{"class":163,"line":27209},[290,49315,36150],{"class":461},[290,49317,465],{"class":295},[290,49319,45206],{"class":461},[290,49321,471],{"class":295},[290,49323,49324,49326,49328,49330,49332],{"class":163,"line":27220},[290,49325,45214],{"class":461},[290,49327,465],{"class":295},[290,49329,468],{"class":461},[290,49331,801],{"class":541},[290,49333,471],{"class":295},[290,49335,49336],{"class":163,"line":27235},[290,49337,8200],{"class":295},[290,49339,49340],{"class":163,"line":27240},[290,49341,771],{"class":295},[290,49343,49344,49346,49348],{"class":163,"line":43950},[290,49345,431],{"class":295},[290,49347,1430],{"class":299},[290,49349,327],{"class":295},[14,49351,49352,49353,60,49356,49358,49359,49361],{},"Because every ",[18,49354,49355],{},".plan",[18,49357,44299],{}," on rows, the price row, the copy row, and the footer row line up across all three cards even though the middle card has far more copy. Remove ",[18,49360,45873],{}," and the footers immediately fall out of alignment, which is the single clearest demonstration of what subgrid buys you.",[47,49363],{},[50,49365,8301],{"id":8300},[14,49367,49368,49369,49371,49372,49374,49375,49377],{},"Grid layout is resolved on the main thread during layout, so the cost scales with track and item count, not with ",[18,49370,44299],{}," specifically. Subgrid is essentially free: it reuses already-computed parent lines rather than running a second independent track-sizing pass. The performance pitfalls people attribute to Grid are almost always ",[18,49373,42056],{}," (which forces block-size containment) used where ",[18,49376,26044],{}," would do, so prefer inline-size containment on grid children as covered in the syntax-basics guide.",[14,49379,49380,49381,69,49383,49385,49386,49388,49389,49391,49392,49394],{},"For accessibility, remember that grid placement is purely visual. ",[18,49382,46237],{},[18,49384,47124],{},"-style repositioning do ",[62,49387,10198],{}," change DOM order, and screen readers and keyboard tab order follow the DOM. Author the markup in a sensible reading order first, then position with Grid; never use Grid to \"fix\" a source order that reads wrong. Respect motion preferences when grid changes animate: if you transition track sizes or animate cards reflowing, gate the motion behind ",[18,49390,2584],{},", the same pattern used throughout the ",[27,49393,1420],{"href":1419}," guide, where animating layout-affecting grid changes is discussed in depth. Maintain a visible focus indicator on interactive grid items and ensure a 3:1 contrast ratio for any track separators per WCAG 1.4.11.",[47,49396],{},[50,49398,8358],{"id":8357},[3017,49400,49401,49410,49419,49425,49437],{},[1396,49402,49403,49406,49407,49409],{},[62,49404,49405],{},"Turn on the grid overlay."," In Chrome or Edge DevTools, open the Elements panel, find the grid container, and click the small ",[18,49408,9147],{}," badge next to it. Firefox exposes the same control in the Layout panel under \"Grid\".",[1396,49411,49412,49415,49416,49418],{},[62,49413,49414],{},"Show line numbers and area names."," In the Layout panel, enable \"Display line numbers\" and \"Display area names\". This confirms ",[18,49417,46237],{}," mapped where you expect.",[1396,49420,49421,49424],{},[62,49422,49423],{},"Inspect subgrid inheritance."," Select the subgrid child and toggle its overlay too. You should see its lines coincide with the parent's lines along the subgridded axis. If they diverge, the child is not actually spanning the tracks you think it is.",[1396,49426,49427,49430,49431,1203,49433,49436],{},[62,49428,49429],{},"Verify item spans."," In the Styles pane, check the computed ",[18,49432,45271],{},[18,49434,49435],{},"grid-column",". A subgrid that only spans one track inherits only one line pair and will look like a normal grid.",[1396,49438,49439,49442,49443,49445,49446,9852,49448,49450],{},[62,49440,49441],{},"Profile reflow."," Use the Performance panel to record a resize. Look for repeated ",[18,49444,13462],{}," events; if they spike, confirm you are not using ",[18,49447,42056],{},[18,49449,26044],{}," suffices.",[47,49452],{},[50,49454,8432],{"id":8431},[2250,49456,49457,49471],{},[2253,49458,49459],{},[2256,49460,49461,49463,49465,49467,49469],{},[2259,49462,3737],{},[2259,49464,24818],{},[2259,49466,2287],{},[2259,49468,2297],{},[2259,49470,2267],{},[2269,49472,49473,49491,49507,49521,49537],{},[2256,49474,49475,49480,49483,49486,49488],{},[2274,49476,49477,49478,3914],{},"CSS Grid (",[18,49479,47555],{},[2274,49481,49482],{},"57+",[2274,49484,49485],{},"52+",[2274,49487,3764],{},[2274,49489,49490],{},"Universally available",[2256,49492,49493,49497,49499,49502,49504],{},[2274,49494,49495],{},[18,49496,44299],{},[2274,49498,24904],{},[2274,49500,49501],{},"71+",[2274,49503,8465],{},[2274,49505,49506],{},"Cross-browser baseline since late 2023",[2256,49508,49509,49513,49515,49517,49519],{},[2274,49510,49511],{},[18,49512,46237],{},[2274,49514,49482],{},[2274,49516,49485],{},[2274,49518,3764],{},[2274,49520,49490],{},[2256,49522,49523,49529,49531,49533,49535],{},[2274,49524,49525,1203,49527],{},[18,49526,46241],{},[18,49528,47523],{},[2274,49530,49482],{},[2274,49532,49485],{},[2274,49534,3764],{},[2274,49536,49490],{},[2256,49538,49539,49544,49546,49548,49550],{},[2274,49540,49541],{},[18,49542,49543],{},"repeat(auto-fit, …)",[2274,49545,49482],{},[2274,49547,49485],{},[2274,49549,3764],{},[2274,49551,49490],{},[14,49553,49554,49555,49558],{},"Subgrid is the only feature here needing a gate for older versions. Wrap subgrid enhancements in ",[18,49556,49557],{},"@supports (grid-template-columns: subgrid)"," and provide a non-subgrid baseline (for example, fixed inner row sizes) outside it.",[281,49560,49562],{"className":438,"code":49561,"language":440,"meta":286,"style":286},".plan { display: grid; grid-template-rows: 3rem auto 3rem; }\n\n@supports (grid-template-columns: subgrid) {\n  .plan { grid-row: span 3; grid-template-rows: subgrid; }\n}\n",[18,49563,49564,49594,49598,49612,49634],{"__ignoreMap":286},[290,49565,49566,49568,49570,49572,49574,49576,49578,49580,49582,49584,49586,49588,49590,49592],{"class":163,"line":292},[290,49567,49355],{"class":303},[290,49569,790],{"class":295},[290,49571,59],{"class":461},[290,49573,465],{"class":295},[290,49575,9147],{"class":461},[290,49577,828],{"class":295},[290,49579,45255],{"class":461},[290,49581,465],{"class":295},[290,49583,1579],{"class":461},[290,49585,801],{"class":541},[290,49587,18149],{"class":461},[290,49589,19770],{"class":461},[290,49591,801],{"class":541},[290,49593,809],{"class":295},[290,49595,49596],{"class":163,"line":330},[290,49597,334],{"emptyLinePlaceholder":333},[290,49599,49600,49602,49604,49606,49608,49610],{"class":163,"line":337},[290,49601,2086],{"class":541},[290,49603,3595],{"class":295},[290,49605,11957],{"class":461},[290,49607,465],{"class":295},[290,49609,44299],{"class":461},[290,49611,646],{"class":295},[290,49613,49614,49616,49618,49620,49622,49624,49626,49628,49630,49632],{"class":163,"line":364},[290,49615,49010],{"class":303},[290,49617,790],{"class":295},[290,49619,45271],{"class":461},[290,49621,44678],{"class":295},[290,49623,1579],{"class":461},[290,49625,828],{"class":295},[290,49627,45255],{"class":461},[290,49629,465],{"class":295},[290,49631,44299],{"class":461},[290,49633,809],{"class":295},[290,49635,49636],{"class":163,"line":386},[290,49637,620],{"class":295},[47,49639],{},[50,49641,8548],{"id":8547},[2250,49643,49644,49654],{},[2253,49645,49646],{},[2256,49647,49648,49650,49652],{},[2259,49649,8557],{},[2259,49651,8560],{},[2259,49653,8563],{},[2269,49655,49656,49674,49688,49701,49714],{},[2256,49657,49658,49661,49667],{},[2274,49659,49660],{},"Subgrid axis ignores its own track list",[2274,49662,49663,49664,49666],{},"Once an axis is ",[18,49665,44299],{},", explicit tracks on that axis are discarded",[2274,49668,49669,49670,49673],{},"Remove the conflicting ",[18,49671,49672],{},"grid-template-*"," value, or define your own tracks on the other axis only",[2256,49675,49676,49679,49682],{},[2274,49677,49678],{},"Card internals don't align across the row",[2274,49680,49681],{},"The child spans only one row, so it inherits a single line pair",[2274,49683,25028,49684,49687],{},[18,49685,49686],{},"grid-row: span N"," matching the number of shared parent rows",[2256,49689,49690,49695,49698],{},[2274,49691,49692,49694],{},[18,49693,46237],{}," shifts reading order",[2274,49696,49697],{},"Visual placement diverged from DOM order",[2274,49699,49700],{},"Author markup in reading order first; never reorder content-critical regions with Grid",[2256,49702,49703,49706,49709],{},[2274,49704,49705],{},"Subgrid gaps look wrong",[2274,49707,49708],{},"Subgrid inherits parent gaps unless overridden",[2274,49710,25110,49711,49713],{},[18,49712,9070],{}," explicitly on the subgrid if you need a different inner spacing",[2256,49715,49716,49719,49724],{},[2274,49717,49718],{},"Layout thrash on resize",[2274,49720,49721,49723],{},[18,49722,42056],{}," forces block containment",[2274,49725,1499,49726,49728],{},[18,49727,25409],{}," on grid children unless block size truly drives layout",[47,49730],{},[50,49732,1316],{"id":1315},[14,49734,49735,49738],{},[62,49736,49737],{},"What does subgrid actually inherit from its parent grid?","\nA subgrid adopts the parent grid's track sizes and line positions along the axis you declare it on. The child's own items then place onto those inherited lines, so they align with the parent grid and with sibling subgrids.",[14,49740,49741,49744,49745,69,49747,49749],{},[62,49742,49743],{},"Can I use subgrid on both rows and columns at once?","\nYes. Declare ",[18,49746,45873],{},[18,49748,47601],{}," on the same element to inherit both axes. Each axis is independent, so you can subgrid one axis and define your own tracks on the other.",[14,49751,49752,49755],{},[62,49753,49754],{},"Do I still need container queries if I use Grid and subgrid?","\nThey solve different problems. Grid and subgrid handle two-dimensional placement and cross-component alignment, while container queries change a component's internal layout based on its own width. They compose well together.",[14,49757,49758,49764,49765,49767],{},[62,49759,49760,49761,49763],{},"Why does my subgrid element ignore its ",[18,49762,45255],{}," value?","\nBecause once an axis is set to ",[18,49766,44299],{},", any explicit track list on that axis is ignored. Tracks come entirely from the parent. Remove the conflicting declaration or switch that axis off subgrid.",[47,49769],{},[50,49771,1391],{"id":1390},[1393,49773,49774,49779,49784,49789,49794],{},[1396,49775,49776,49778],{},[27,49777,26284],{"href":25340}," — declare containment so grid children adapt to their own width.",[1396,49780,49781,49783],{},[27,49782,47441],{"href":47440}," — the keywords that size your grid tracks.",[1396,49785,49786,49788],{},[27,49787,47370],{"href":47369}," — apply subgrid alignment across a real dashboard.",[1396,49790,49791,49793],{},[27,49792,46181],{"href":46180}," — the classic page skeleton with named areas.",[1396,49795,49796,49798],{},[27,49797,1420],{"href":1419}," — animate grid and layout changes accessibly.",[1430,49800,49801],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":286,"searchDepth":330,"depth":330,"links":49803},[49804,49805,49806,49807,49816,49817,49818,49819,49820,49821,49822],{"id":6746,"depth":330,"text":6747},{"id":47576,"depth":330,"text":47577},{"id":6866,"depth":330,"text":6867},{"id":47768,"depth":330,"text":47769,"children":49808},[49809,49810,49812,49813,49814],{"id":47772,"depth":337,"text":47773},{"id":47854,"depth":337,"text":49811},"Step 2 — Name regions with grid-template-areas",{"id":48037,"depth":337,"text":48038},{"id":48118,"depth":337,"text":48119},{"id":48263,"depth":337,"text":49815},"Step 5 — Adapt the card's internals with @container",{"id":48400,"depth":330,"text":48401},{"id":8300,"depth":330,"text":8301},{"id":8357,"depth":330,"text":8358},{"id":8431,"depth":330,"text":8432},{"id":8547,"depth":330,"text":8548},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Master CSS Grid and subgrid for responsive layouts: track sizing, grid-template-areas, inherited subgrid track lines, and pairing Grid with container queries.",{"seoTitle":49825,"datePublished":1447,"dateModified":1447,"faq":49826},"CSS Grid & Subgrid Layouts Guide",[49827,49829,49831,49833,49836],{"q":49737,"a":49828},"A subgrid adopts the parent grid's track sizes and line positions along the axis you declare it on. The child's own items then place onto those inherited lines, so they align with the parent grid and with sibling subgrids.",{"q":49743,"a":49830},"Yes. Declare grid-template-rows: subgrid and grid-template-columns: subgrid on the same element to inherit both axes. Each axis is independent, so you can subgrid one axis and define your own tracks on the other.",{"q":49754,"a":49832},"They solve different problems. Grid and subgrid handle two-dimensional placement and cross-component alignment, while container queries change a component's internal layout based on its own width. They compose well together.",{"q":49834,"a":49835},"Why does my subgrid element ignore its grid-template-rows value?","Because once an axis is set to subgrid, any explicit track list on that axis is ignored. Tracks come entirely from the parent. Remove the conflicting declaration or switch that axis off subgrid.",{"q":49837,"a":49838},"Is subgrid safe to use in production in 2026?","Yes. Subgrid reached cross-browser support with Firefox 71, Safari 16, and Chrome\u002FEdge 117, so all current evergreen browsers support it. Use @supports (grid-template-columns: subgrid) to gate enhancements for older versions.","\u002Fmastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts",{"title":47415,"description":49823},"mastering-container-queries-responsive-layouts\u002Fcss-grid-and-subgrid-layouts\u002Findex","-C_zK6fGqW4451qsu-tItT5eQh3VPqVJb_w2ZudiW6E",{"id":49844,"title":49845,"body":49846,"description":51206,"extension":1444,"meta":51207,"navigation":333,"path":51218,"seo":51219,"stem":51220,"__hash__":51221},"content\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Fclamp-vs-media-query-typography\u002Findex.md","clamp() Fluid Type vs Media-Query Step Typography: Tradeoffs and Anatomy",{"type":7,"value":49847,"toc":51197},[49848,49851,49866,49870,49873,49899,49907,49909,49911,49917,50841,50847,50895,50899,50945,50947,50951,50954,51098,51101,51103,51123,51125,51139,51145,51155,51164,51166,51194],[10,49849,49845],{"id":49850},"clamp-fluid-type-vs-media-query-step-typography-tradeoffs-and-anatomy",[14,49852,49853,49854,49856,49857,49859,49860,49862,49863,49865],{},"Typography sizing forces an early architectural decision: should a font size interpolate smoothly between two viewport widths, or jump in discrete steps at named breakpoints? This guide compares CSS ",[18,49855,12276],{}," fluid type against stepped ",[18,49858,874],{}," typography for the exact case of building a heading and body scale that stays readable from a 320px phone to a 1440px desktop. It sits within ",[27,49861,12361],{"href":11312},", part of the broader ",[27,49864,11296],{"href":5777}," reference. The decision is not purely aesthetic — it changes how many declarations you maintain, how the type behaves under zoom, and whether you can guarantee a legible minimum.",[50,49867,49869],{"id":49868},"why-pick-fluid-interpolation-over-discrete-steps","Why pick fluid interpolation over discrete steps",[14,49871,49872],{},"Stepped typography assigns a fixed size per breakpoint band. Inside each band the size is constant, then it snaps at the next query. This is predictable and easy to reason about, but it produces visible jumps as the window resizes, and it multiplies declarations: every element that scales needs a rule in every band. A six-element scale across four breakpoints is twenty-four declarations to keep in sync.",[14,49874,1517,49875,49877,49878,49880,49881,49883,49884,49886,49887,49889,49890,49892,49893,49895,49896,49898],{},[18,49876,12276],{}," approach replaces that matrix with one declaration per element. The size tracks the viewport continuously, so there are no jumps, and the slope is computed once. The tradeoff is that the middle term uses a viewport unit (",[18,49879,12094],{},") or container unit (",[18,49882,11404],{},") that does not respond to browser zoom the way ",[18,49885,801],{}," does. That is the crux of the accessibility conversation: a naive fluid formula whose floor is also expressed in ",[18,49888,12094],{}," can shrink below a readable size when a user zooms, because zoom changes the effective ",[18,49891,801],{}," but not the ",[18,49894,12094],{}," reference. The defense is to express the floor and ceiling in ",[18,49897,801],{}," so the clamp can never resolve below a size you have explicitly approved. There is no JavaScript resize listener in either approach, so neither incurs main-thread layout thrash; the difference is purely in the cascade.",[14,49900,49901,49902,49904,49905,42],{},"A second consideration is art direction. Sometimes a design genuinely wants distinct type treatments at distinct widths — a compact mobile headline that becomes a different weight and tracking on desktop, not merely a larger version of itself. Smooth interpolation cannot express that discontinuity; stepped queries can. When the relationship between sizes is linear, prefer ",[18,49903,12276],{},". When it is a deliberate redesign per band, prefer ",[18,49906,874],{},[47,49908],{},[50,49910,4203],{"id":4202},[14,49912,49913,49914,49916],{},"The block below builds the same three-level scale twice: once with stepped media queries, once with ",[18,49915,12276],{},". Both render side by side so you can compare resize behavior directly. Comments mark the non-obvious rules.",[281,49918,49920],{"className":283,"code":49919,"language":285,"meta":286,"style":286},"\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\u003Cstyle>\n  :root {\n    \u002F* Type-scale bounds expressed in rem so browser zoom always applies.\n       The fluid slope lives in vw; the floor\u002Fceiling never do. *\u002F\n    --step-0-min: 1rem;     --step-0-max: 1.125rem;\n    --step-1-min: 1.25rem;  --step-1-max: 1.75rem;\n    --step-2-min: 1.75rem;  --step-2-max: 3rem;\n  }\n\n  body { margin: 0; font-family: system-ui, sans-serif; line-height: 1.4; }\n  .panel { padding: 2rem; max-width: 38rem; }\n  .panel + .panel { border-top: 2px solid #ccc; }\n  .label { font: 600 0.75rem ui-monospace, monospace; color: #555; }\n\n  \u002F* --- Approach 1: stepped media-query typography --- *\u002F\n  .stepped h2 { font-size: var(--step-2-min); }\n  .stepped h3 { font-size: var(--step-1-min); }\n  .stepped p  { font-size: var(--step-0-min); }\n\n  @media (min-width: 30rem) {\n    .stepped h2 { font-size: 2.25rem; }\n    .stepped h3 { font-size: 1.5rem; }\n  }\n  @media (min-width: 60rem) {\n    \u002F* Each element needs a rule in every band — the maintenance cost *\u002F\n    .stepped h2 { font-size: var(--step-2-max); }\n    .stepped h3 { font-size: var(--step-1-max); }\n    .stepped p  { font-size: var(--step-0-max); }\n  }\n\n  \u002F* --- Approach 2: fluid clamp() typography --- *\u002F\n  \u002F* clamp(min, preferred, max): the preferred term interpolates,\n     the rem bounds cap it. One declaration replaces the whole matrix. *\u002F\n  .fluid h2 { font-size: clamp(var(--step-2-min), 1.2rem + 4vw, var(--step-2-max)); }\n  .fluid h3 { font-size: clamp(var(--step-1-min), 1rem  + 1.6vw, var(--step-1-max)); }\n  .fluid p  { font-size: clamp(var(--step-0-min), 0.95rem + 0.4vw, var(--step-0-max)); }\n\n  \u002F* Headings need tighter line-height as they grow; unitless keeps it proportional *\u002F\n  h2, h3 { line-height: 1.15; letter-spacing: -0.01em; }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Csection class=\"panel stepped\">\n    \u003Cp class=\"label\">stepped @media (jumps at 30rem \u002F 60rem)\u003C\u002Fp>\n    \u003Ch2>Display heading\u003C\u002Fh2>\n    \u003Ch3>Section heading\u003C\u002Fh3>\n    \u003Cp>Body copy holds a fixed size inside each breakpoint band, then snaps.\u003C\u002Fp>\n  \u003C\u002Fsection>\n\n  \u003Csection class=\"panel fluid\">\n    \u003Cp class=\"label\">fluid clamp() (smooth)\u003C\u002Fp>\n    \u003Ch2>Display heading\u003C\u002Fh2>\n    \u003Ch3>Section heading\u003C\u002Fh3>\n    \u003Cp>Body copy interpolates continuously between the rem floor and ceiling.\u003C\u002Fp>\n  \u003C\u002Fsection>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,49921,49922,49932,49946,49954,49968,49988,49996,50002,50007,50012,50037,50062,50086,50090,50094,50131,50158,50185,50221,50225,50230,50252,50274,50295,50299,50315,50335,50353,50357,50373,50378,50398,50418,50438,50442,50446,50451,50456,50461,50506,50552,50597,50601,50606,50637,50645,50653,50661,50676,50696,50709,50721,50734,50742,50746,50761,50780,50792,50804,50817,50825,50833],{"__ignoreMap":286},[290,49923,49924,49926,49928,49930],{"class":163,"line":292},[290,49925,8982],{"class":295},[290,49927,35913],{"class":299},[290,49929,8988],{"class":303},[290,49931,327],{"class":295},[290,49933,49934,49936,49938,49940,49942,49944],{"class":163,"line":330},[290,49935,296],{"class":295},[290,49937,285],{"class":299},[290,49939,8999],{"class":303},[290,49941,307],{"class":295},[290,49943,9004],{"class":310},[290,49945,327],{"class":295},[290,49947,49948,49950,49952],{"class":163,"line":337},[290,49949,296],{"class":295},[290,49951,9013],{"class":299},[290,49953,327],{"class":295},[290,49955,49956,49958,49960,49962,49964,49966],{"class":163,"line":364},[290,49957,296],{"class":295},[290,49959,9022],{"class":299},[290,49961,9025],{"class":303},[290,49963,307],{"class":295},[290,49965,9030],{"class":310},[290,49967,327],{"class":295},[290,49969,49970,49972,49974,49976,49978,49980,49982,49984,49986],{"class":163,"line":386},[290,49971,296],{"class":295},[290,49973,9022],{"class":299},[290,49975,35962],{"class":303},[290,49977,307],{"class":295},[290,49979,35967],{"class":310},[290,49981,35970],{"class":303},[290,49983,307],{"class":295},[290,49985,35975],{"class":310},[290,49987,327],{"class":295},[290,49989,49990,49992,49994],{"class":163,"line":408},[290,49991,296],{"class":295},[290,49993,1430],{"class":299},[290,49995,327],{"class":295},[290,49997,49998,50000],{"class":163,"line":428},[290,49999,3430],{"class":303},[290,50001,450],{"class":295},[290,50003,50004],{"class":163,"line":517},[290,50005,50006],{"class":455},"    \u002F* Type-scale bounds expressed in rem so browser zoom always applies.\n",[290,50008,50009],{"class":163,"line":523},[290,50010,50011],{"class":455},"       The fluid slope lives in vw; the floor\u002Fceiling never do. *\u002F\n",[290,50013,50014,50017,50019,50021,50023,50025,50028,50030,50033,50035],{"class":163,"line":532},[290,50015,50016],{"class":1561},"    --step-0-min",[290,50018,465],{"class":295},[290,50020,468],{"class":461},[290,50022,801],{"class":541},[290,50024,23058],{"class":295},[290,50026,50027],{"class":1561},"--step-0-max",[290,50029,465],{"class":295},[290,50031,50032],{"class":461},"1.125",[290,50034,801],{"class":541},[290,50036,471],{"class":295},[290,50038,50039,50042,50044,50046,50048,50050,50053,50055,50058,50060],{"class":163,"line":551},[290,50040,50041],{"class":1561},"    --step-1-min",[290,50043,465],{"class":295},[290,50045,35120],{"class":461},[290,50047,801],{"class":541},[290,50049,18154],{"class":295},[290,50051,50052],{"class":1561},"--step-1-max",[290,50054,465],{"class":295},[290,50056,50057],{"class":461},"1.75",[290,50059,801],{"class":541},[290,50061,471],{"class":295},[290,50063,50064,50067,50069,50071,50073,50075,50078,50080,50082,50084],{"class":163,"line":586},[290,50065,50066],{"class":1561},"    --step-2-min",[290,50068,465],{"class":295},[290,50070,50057],{"class":461},[290,50072,801],{"class":541},[290,50074,18154],{"class":295},[290,50076,50077],{"class":1561},"--step-2-max",[290,50079,465],{"class":295},[290,50081,1579],{"class":461},[290,50083,801],{"class":541},[290,50085,471],{"class":295},[290,50087,50088],{"class":163,"line":602},[290,50089,771],{"class":295},[290,50091,50092],{"class":163,"line":617},[290,50093,334],{"emptyLinePlaceholder":333},[290,50095,50096,50098,50100,50102,50104,50106,50108,50111,50113,50116,50118,50120,50122,50125,50127,50129],{"class":163,"line":623},[290,50097,44444],{"class":299},[290,50099,790],{"class":295},[290,50101,2725],{"class":461},[290,50103,465],{"class":295},[290,50105,487],{"class":461},[290,50107,828],{"class":295},[290,50109,50110],{"class":461},"font-family",[290,50112,465],{"class":295},[290,50114,50115],{"class":461},"system-ui",[290,50117,569],{"class":295},[290,50119,44479],{"class":461},[290,50121,828],{"class":295},[290,50123,50124],{"class":461},"line-height",[290,50126,465],{"class":295},[290,50128,1824],{"class":461},[290,50130,809],{"class":295},[290,50132,50133,50135,50137,50139,50141,50143,50145,50147,50149,50151,50154,50156],{"class":163,"line":628},[290,50134,6431],{"class":303},[290,50136,790],{"class":295},[290,50138,793],{"class":461},[290,50140,465],{"class":295},[290,50142,194],{"class":461},[290,50144,801],{"class":541},[290,50146,828],{"class":295},[290,50148,41191],{"class":461},[290,50150,465],{"class":295},[290,50152,50153],{"class":461},"38",[290,50155,801],{"class":541},[290,50157,809],{"class":295},[290,50159,50160,50162,50164,50167,50169,50172,50174,50176,50178,50180,50183],{"class":163,"line":634},[290,50161,6431],{"class":303},[290,50163,3592],{"class":541},[290,50165,50166],{"class":303}," .panel",[290,50168,790],{"class":295},[290,50170,50171],{"class":461},"border-top",[290,50173,465],{"class":295},[290,50175,194],{"class":461},[290,50177,674],{"class":541},[290,50179,852],{"class":461},[290,50181,50182],{"class":461}," #ccc",[290,50184,809],{"class":295},[290,50186,50187,50190,50192,50194,50196,50198,50201,50203,50206,50208,50211,50213,50215,50217,50219],{"class":163,"line":649},[290,50188,50189],{"class":303},"  .label",[290,50191,790],{"class":295},[290,50193,46459],{"class":461},[290,50195,465],{"class":295},[290,50197,4176],{"class":461},[290,50199,50200],{"class":461}," 0.75",[290,50202,801],{"class":541},[290,50204,50205],{"class":461}," ui-monospace",[290,50207,569],{"class":295},[290,50209,50210],{"class":461},"monospace",[290,50212,828],{"class":295},[290,50214,9133],{"class":461},[290,50216,465],{"class":295},[290,50218,15996],{"class":461},[290,50220,809],{"class":295},[290,50222,50223],{"class":163,"line":660},[290,50224,334],{"emptyLinePlaceholder":333},[290,50226,50227],{"class":163,"line":688},[290,50228,50229],{"class":455},"  \u002F* --- Approach 1: stepped media-query typography --- *\u002F\n",[290,50231,50232,50235,50237,50239,50241,50243,50245,50247,50250],{"class":163,"line":693},[290,50233,50234],{"class":303},"  .stepped",[290,50236,39206],{"class":299},[290,50238,790],{"class":295},[290,50240,44978],{"class":461},[290,50242,465],{"class":295},[290,50244,1622],{"class":461},[290,50246,484],{"class":295},[290,50248,50249],{"class":1561},"--step-2-min",[290,50251,1122],{"class":295},[290,50253,50254,50256,50259,50261,50263,50265,50267,50269,50272],{"class":163,"line":698},[290,50255,50234],{"class":303},[290,50257,50258],{"class":299}," h3",[290,50260,790],{"class":295},[290,50262,44978],{"class":461},[290,50264,465],{"class":295},[290,50266,1622],{"class":461},[290,50268,484],{"class":295},[290,50270,50271],{"class":1561},"--step-1-min",[290,50273,1122],{"class":295},[290,50275,50276,50278,50280,50282,50284,50286,50288,50290,50293],{"class":163,"line":704},[290,50277,50234],{"class":303},[290,50279,39281],{"class":299},[290,50281,4612],{"class":295},[290,50283,44978],{"class":461},[290,50285,465],{"class":295},[290,50287,1622],{"class":461},[290,50289,484],{"class":295},[290,50291,50292],{"class":1561},"--step-0-min",[290,50294,1122],{"class":295},[290,50296,50297],{"class":163,"line":710},[290,50298,334],{"emptyLinePlaceholder":333},[290,50300,50301,50303,50305,50307,50309,50311,50313],{"class":163,"line":717},[290,50302,26109],{"class":541},[290,50304,3595],{"class":295},[290,50306,26114],{"class":461},[290,50308,465],{"class":295},[290,50310,2598],{"class":461},[290,50312,801],{"class":541},[290,50314,646],{"class":295},[290,50316,50317,50320,50322,50324,50326,50328,50331,50333],{"class":163,"line":730},[290,50318,50319],{"class":303},"    .stepped",[290,50321,39206],{"class":299},[290,50323,790],{"class":295},[290,50325,44978],{"class":461},[290,50327,465],{"class":295},[290,50329,50330],{"class":461},"2.25",[290,50332,801],{"class":541},[290,50334,809],{"class":295},[290,50336,50337,50339,50341,50343,50345,50347,50349,50351],{"class":163,"line":742},[290,50338,50319],{"class":303},[290,50340,50258],{"class":299},[290,50342,790],{"class":295},[290,50344,44978],{"class":461},[290,50346,465],{"class":295},[290,50348,168],{"class":461},[290,50350,801],{"class":541},[290,50352,809],{"class":295},[290,50354,50355],{"class":163,"line":768},[290,50356,771],{"class":295},[290,50358,50359,50361,50363,50365,50367,50369,50371],{"class":163,"line":774},[290,50360,26109],{"class":541},[290,50362,3595],{"class":295},[290,50364,26114],{"class":461},[290,50366,465],{"class":295},[290,50368,1786],{"class":461},[290,50370,801],{"class":541},[290,50372,646],{"class":295},[290,50374,50375],{"class":163,"line":779},[290,50376,50377],{"class":455},"    \u002F* Each element needs a rule in every band — the maintenance cost *\u002F\n",[290,50379,50380,50382,50384,50386,50388,50390,50392,50394,50396],{"class":163,"line":784},[290,50381,50319],{"class":303},[290,50383,39206],{"class":299},[290,50385,790],{"class":295},[290,50387,44978],{"class":461},[290,50389,465],{"class":295},[290,50391,1622],{"class":461},[290,50393,484],{"class":295},[290,50395,50077],{"class":1561},[290,50397,1122],{"class":295},[290,50399,50400,50402,50404,50406,50408,50410,50412,50414,50416],{"class":163,"line":812},[290,50401,50319],{"class":303},[290,50403,50258],{"class":299},[290,50405,790],{"class":295},[290,50407,44978],{"class":461},[290,50409,465],{"class":295},[290,50411,1622],{"class":461},[290,50413,484],{"class":295},[290,50415,50052],{"class":1561},[290,50417,1122],{"class":295},[290,50419,50420,50422,50424,50426,50428,50430,50432,50434,50436],{"class":163,"line":860},[290,50421,50319],{"class":303},[290,50423,39281],{"class":299},[290,50425,4612],{"class":295},[290,50427,44978],{"class":461},[290,50429,465],{"class":295},[290,50431,1622],{"class":461},[290,50433,484],{"class":295},[290,50435,50027],{"class":1561},[290,50437,1122],{"class":295},[290,50439,50440],{"class":163,"line":865},[290,50441,771],{"class":295},[290,50443,50444],{"class":163,"line":871},[290,50445,334],{"emptyLinePlaceholder":333},[290,50447,50448],{"class":163,"line":880},[290,50449,50450],{"class":455},"  \u002F* --- Approach 2: fluid clamp() typography --- *\u002F\n",[290,50452,50453],{"class":163,"line":896},[290,50454,50455],{"class":455},"  \u002F* clamp(min, preferred, max): the preferred term interpolates,\n",[290,50457,50458],{"class":163,"line":4734},[290,50459,50460],{"class":455},"     the rem bounds cap it. One declaration replaces the whole matrix. *\u002F\n",[290,50462,50463,50466,50468,50470,50472,50474,50476,50478,50480,50482,50484,50486,50488,50490,50492,50494,50496,50498,50500,50502,50504],{"class":163,"line":4742},[290,50464,50465],{"class":303},"  .fluid",[290,50467,39206],{"class":299},[290,50469,790],{"class":295},[290,50471,44978],{"class":461},[290,50473,465],{"class":295},[290,50475,11555],{"class":461},[290,50477,484],{"class":295},[290,50479,1622],{"class":461},[290,50481,484],{"class":295},[290,50483,50249],{"class":1561},[290,50485,24264],{"class":295},[290,50487,1789],{"class":461},[290,50489,801],{"class":541},[290,50491,3592],{"class":1561},[290,50493,12650],{"class":461},[290,50495,12094],{"class":541},[290,50497,569],{"class":295},[290,50499,1622],{"class":461},[290,50501,484],{"class":295},[290,50503,50077],{"class":1561},[290,50505,4894],{"class":295},[290,50507,50508,50510,50512,50514,50516,50518,50520,50522,50524,50526,50528,50530,50532,50534,50537,50540,50542,50544,50546,50548,50550],{"class":163,"line":4761},[290,50509,50465],{"class":303},[290,50511,50258],{"class":299},[290,50513,790],{"class":295},[290,50515,44978],{"class":461},[290,50517,465],{"class":295},[290,50519,11555],{"class":461},[290,50521,484],{"class":295},[290,50523,1622],{"class":461},[290,50525,484],{"class":295},[290,50527,50271],{"class":1561},[290,50529,24264],{"class":295},[290,50531,468],{"class":461},[290,50533,801],{"class":541},[290,50535,50536],{"class":1561},"  +",[290,50538,50539],{"class":461}," 1.6",[290,50541,12094],{"class":541},[290,50543,569],{"class":295},[290,50545,1622],{"class":461},[290,50547,484],{"class":295},[290,50549,50052],{"class":1561},[290,50551,4894],{"class":295},[290,50553,50554,50556,50558,50560,50562,50564,50566,50568,50570,50572,50574,50576,50578,50580,50582,50585,50587,50589,50591,50593,50595],{"class":163,"line":4766},[290,50555,50465],{"class":303},[290,50557,39281],{"class":299},[290,50559,4612],{"class":295},[290,50561,44978],{"class":461},[290,50563,465],{"class":295},[290,50565,11555],{"class":461},[290,50567,484],{"class":295},[290,50569,1622],{"class":461},[290,50571,484],{"class":295},[290,50573,50292],{"class":1561},[290,50575,24264],{"class":295},[290,50577,1119],{"class":461},[290,50579,801],{"class":541},[290,50581,3592],{"class":1561},[290,50583,50584],{"class":461}," 0.4",[290,50586,12094],{"class":541},[290,50588,569],{"class":295},[290,50590,1622],{"class":461},[290,50592,484],{"class":295},[290,50594,50027],{"class":1561},[290,50596,4894],{"class":295},[290,50598,50599],{"class":163,"line":4786},[290,50600,334],{"emptyLinePlaceholder":333},[290,50602,50603],{"class":163,"line":9635},[290,50604,50605],{"class":455},"  \u002F* Headings need tighter line-height as they grow; unitless keeps it proportional *\u002F\n",[290,50607,50608,50611,50613,50615,50617,50619,50621,50623,50625,50628,50630,50633,50635],{"class":163,"line":9641},[290,50609,50610],{"class":299},"  h2",[290,50612,569],{"class":295},[290,50614,2757],{"class":299},[290,50616,790],{"class":295},[290,50618,50124],{"class":461},[290,50620,465],{"class":295},[290,50622,39253],{"class":461},[290,50624,828],{"class":295},[290,50626,50627],{"class":461},"letter-spacing",[290,50629,465],{"class":295},[290,50631,50632],{"class":461},"-0.01",[290,50634,86],{"class":541},[290,50636,809],{"class":295},[290,50638,50639,50641,50643],{"class":163,"line":9647},[290,50640,431],{"class":295},[290,50642,1430],{"class":299},[290,50644,327],{"class":295},[290,50646,50647,50649,50651],{"class":163,"line":9665},[290,50648,431],{"class":295},[290,50650,9013],{"class":299},[290,50652,327],{"class":295},[290,50654,50655,50657,50659],{"class":163,"line":9683},[290,50656,296],{"class":295},[290,50658,9239],{"class":299},[290,50660,327],{"class":295},[290,50662,50663,50665,50667,50669,50671,50674],{"class":163,"line":9688},[290,50664,367],{"class":295},[290,50666,5944],{"class":299},[290,50668,314],{"class":303},[290,50670,307],{"class":295},[290,50672,50673],{"class":310},"\"panel stepped\"",[290,50675,327],{"class":295},[290,50677,50678,50680,50682,50684,50686,50689,50692,50694],{"class":163,"line":9694},[290,50679,4290],{"class":295},[290,50681,14],{"class":299},[290,50683,314],{"class":303},[290,50685,307],{"class":295},[290,50687,50688],{"class":310},"\"label\"",[290,50690,50691],{"class":295},">stepped @media (jumps at 30rem \u002F 60rem)\u003C\u002F",[290,50693,14],{"class":299},[290,50695,327],{"class":295},[290,50697,50698,50700,50702,50705,50707],{"class":163,"line":9700},[290,50699,4290],{"class":295},[290,50701,50],{"class":299},[290,50703,50704],{"class":295},">Display heading\u003C\u002F",[290,50706,50],{"class":299},[290,50708,327],{"class":295},[290,50710,50711,50713,50715,50717,50719],{"class":163,"line":9710},[290,50712,4290],{"class":295},[290,50714,2757],{"class":299},[290,50716,5962],{"class":295},[290,50718,2757],{"class":299},[290,50720,327],{"class":295},[290,50722,50723,50725,50727,50730,50732],{"class":163,"line":9716},[290,50724,4290],{"class":295},[290,50726,14],{"class":299},[290,50728,50729],{"class":295},">Body copy holds a fixed size inside each breakpoint band, then snaps.\u003C\u002F",[290,50731,14],{"class":299},[290,50733,327],{"class":295},[290,50735,50736,50738,50740],{"class":163,"line":9738},[290,50737,4315],{"class":295},[290,50739,5944],{"class":299},[290,50741,327],{"class":295},[290,50743,50744],{"class":163,"line":9748},[290,50745,334],{"emptyLinePlaceholder":333},[290,50747,50748,50750,50752,50754,50756,50759],{"class":163,"line":9754},[290,50749,367],{"class":295},[290,50751,5944],{"class":299},[290,50753,314],{"class":303},[290,50755,307],{"class":295},[290,50757,50758],{"class":310},"\"panel fluid\"",[290,50760,327],{"class":295},[290,50762,50763,50765,50767,50769,50771,50773,50776,50778],{"class":163,"line":9760},[290,50764,4290],{"class":295},[290,50766,14],{"class":299},[290,50768,314],{"class":303},[290,50770,307],{"class":295},[290,50772,50688],{"class":310},[290,50774,50775],{"class":295},">fluid clamp() (smooth)\u003C\u002F",[290,50777,14],{"class":299},[290,50779,327],{"class":295},[290,50781,50782,50784,50786,50788,50790],{"class":163,"line":9777},[290,50783,4290],{"class":295},[290,50785,50],{"class":299},[290,50787,50704],{"class":295},[290,50789,50],{"class":299},[290,50791,327],{"class":295},[290,50793,50794,50796,50798,50800,50802],{"class":163,"line":9787},[290,50795,4290],{"class":295},[290,50797,2757],{"class":299},[290,50799,5962],{"class":295},[290,50801,2757],{"class":299},[290,50803,327],{"class":295},[290,50805,50806,50808,50810,50813,50815],{"class":163,"line":9793},[290,50807,4290],{"class":295},[290,50809,14],{"class":299},[290,50811,50812],{"class":295},">Body copy interpolates continuously between the rem floor and ceiling.\u003C\u002F",[290,50814,14],{"class":299},[290,50816,327],{"class":295},[290,50818,50819,50821,50823],{"class":163,"line":9799},[290,50820,4315],{"class":295},[290,50822,5944],{"class":299},[290,50824,327],{"class":295},[290,50826,50827,50829,50831],{"class":163,"line":9805},[290,50828,431],{"class":295},[290,50830,9239],{"class":299},[290,50832,327],{"class":295},[290,50834,50835,50837,50839],{"class":163,"line":9811},[290,50836,431],{"class":295},[290,50838,285],{"class":299},[290,50840,327],{"class":295},[14,50842,50843,50844,50846],{},"Resize the window slowly: the stepped panel jumps at exactly 30rem and 60rem, while the fluid panel glides. Zoom to 200% on both — because every bound is in ",[18,50845,801],{},", neither drops below a readable floor.",[133,50848,140,50850,140,50853,140,50856,140,50859,140,50861,140,50863,140,50865,140,50868,140,50871,140,50874,140,50877,140,50880,140,50883,140,50887,140,50891],{"viewBox":2588,"role":136,"ariaLabel":50849,"xmlns":138,"style":139},"clamp value bands compared with a stepped media-query line across increasing viewport width",[142,50851,50852],{},"clamp() bands versus stepped media-query typography",[146,50854,50855],{},"A chart of font size against viewport width showing the clamp() minimum floor, the fluid sloped middle, and the maximum ceiling, beside a stepped line that jumps at breakpoints.",[150,50857,50858],{"x":152,"y":153,"style":1781},"font size vs viewport width",[163,50860],{"x1":5144,"y1":1786,"x2":5144,"y2":2601,"stroke":167,"strokeWidth":168},[163,50862],{"x1":5144,"y1":2601,"x2":25430,"y2":2601,"stroke":167,"strokeWidth":168},[150,50864,41019],{"x":4146,"y":5144,"style":10252},[150,50866,1748],{"x":10284,"y":40013,"style":50867},"text-anchor:end;fill:currentColor;font:11px sans-serif;opacity:0.7",[163,50869],{"x1":5144,"y1":165,"x2":25430,"y2":165,"stroke":177,"strokeWidth":468,"strokeDashArray":50870,"opacity":21064},[249,249],[163,50872],{"x1":5144,"y1":4190,"x2":25430,"y2":4190,"stroke":177,"strokeWidth":468,"strokeDashArray":50873,"opacity":21064},[249,249],[150,50875,50876],{"x":41988,"y":18845,"style":38882},"max ceiling",[150,50878,50879],{"x":41988,"y":19916,"style":38882},"min floor",[253,50881],{"d":50882,"fill":72,"stroke":177,"strokeWidth":1579},"M70 230 L240 230 L470 100 L680 100",[150,50884,50886],{"x":50885,"y":8912,"style":22565},"350","clamp() fluid slope",[253,50888],{"d":50889,"fill":72,"stroke":167,"strokeWidth":194,"strokeDashArray":50890,"opacity":572},"M70 250 L260 250 L260 175 L490 175 L490 120 L680 120",[5901,249],[150,50892,50894],{"x":5138,"y":1787,"style":50893},"text-anchor:start;fill:currentColor;font:12px sans-serif;opacity:0.75","stepped @media",[50,50896,50898],{"id":50897},"key-technique-the-min-preferred-max-anatomy","Key technique: the min \u002F preferred \u002F max anatomy",[14,50900,50901,50902,50905,50906,50909,50910,50913,50914,50917,50918,50920,50921,50923,50924,12091,50926,50928,50929,50917,50932,50934,50935,69,50938,50941,50942,50944],{},"Everything rests on how the browser resolves ",[18,50903,50904],{},"clamp(MIN, PREFERRED, MAX)",". The engine computes ",[18,50907,50908],{},"PREFERRED"," first, then returns the value clamped into the ",[18,50911,50912],{},"[MIN, MAX]"," range: it is exactly ",[18,50915,50916],{},"max(MIN, min(PREFERRED, MAX))",". The ",[18,50919,50908],{}," term is where interpolation happens, and it is the only place a viewport or container unit belongs — written as a ",[18,50922,801],{}," base plus a ",[18,50925,12094],{},[18,50927,11404],{},") slope, e.g. ",[18,50930,50931],{},"1rem + 1.6vw",[18,50933,801],{}," base sets the size at zero width; the unit term adds the per-width growth. The ",[18,50936,50937],{},"MIN",[18,50939,50940],{},"MAX"," are guardrails and must be ",[18,50943,801],{}," so zoom and OS font-size preferences always reach them. Read left to right it is: never smaller than this, grow like this, never larger than this.",[47,50946],{},[50,50948,50950],{"id":50949},"variation-a-hybrid-that-respects-reduced-motion-and-large-screens","Variation: a hybrid that respects reduced motion and large screens",[14,50952,50953],{},"Pure interpolation never stops growing while the viewport grows, so on ultra-wide displays a heading can overshoot the intended ceiling before the clamp catches it only at the very top. A hybrid pins the ceiling with a media query while keeping fluidity below it, and it also avoids any transition on the size so users who prefer reduced motion are unaffected.",[281,50955,50957],{"className":438,"code":50956,"language":440,"meta":286,"style":286},".fluid h2 {\n  font-size: clamp(1.75rem, 1.2rem + 4vw, 3rem);\n}\n\n\u002F* Past the design's max width, drop the fluid term entirely so the\n   heading is a stable 3rem and never tracks ultra-wide widths. *\u002F\n@media (min-width: 90rem) {\n  .fluid h2 { font-size: 3rem; }\n}\n\n\u002F* Font size itself is not animated; this guards any decorative transition\n   that might accompany a size change on resize. *\u002F\n@media (prefers-reduced-motion: reduce) {\n  .fluid h2 { transition: none; }\n}\n",[18,50958,50959,50968,51002,51006,51010,51015,51020,51036,51054,51058,51062,51067,51072,51078,51094],{"__ignoreMap":286},[290,50960,50961,50964,50966],{"class":163,"line":292},[290,50962,50963],{"class":303},".fluid",[290,50965,39206],{"class":299},[290,50967,450],{"class":295},[290,50969,50970,50972,50974,50976,50978,50980,50982,50984,50986,50988,50990,50992,50994,50996,50998,51000],{"class":163,"line":330},[290,50971,17980],{"class":461},[290,50973,465],{"class":295},[290,50975,11555],{"class":461},[290,50977,484],{"class":295},[290,50979,50057],{"class":461},[290,50981,801],{"class":541},[290,50983,569],{"class":295},[290,50985,1789],{"class":461},[290,50987,801],{"class":541},[290,50989,3592],{"class":1561},[290,50991,12650],{"class":461},[290,50993,12094],{"class":541},[290,50995,569],{"class":295},[290,50997,1579],{"class":461},[290,50999,801],{"class":541},[290,51001,500],{"class":295},[290,51003,51004],{"class":163,"line":337},[290,51005,620],{"class":295},[290,51007,51008],{"class":163,"line":364},[290,51009,334],{"emptyLinePlaceholder":333},[290,51011,51012],{"class":163,"line":386},[290,51013,51014],{"class":455},"\u002F* Past the design's max width, drop the fluid term entirely so the\n",[290,51016,51017],{"class":163,"line":408},[290,51018,51019],{"class":455},"   heading is a stable 3rem and never tracks ultra-wide widths. *\u002F\n",[290,51021,51022,51024,51026,51028,51030,51032,51034],{"class":163,"line":428},[290,51023,874],{"class":541},[290,51025,3595],{"class":295},[290,51027,26114],{"class":461},[290,51029,465],{"class":295},[290,51031,5115],{"class":461},[290,51033,801],{"class":541},[290,51035,646],{"class":295},[290,51037,51038,51040,51042,51044,51046,51048,51050,51052],{"class":163,"line":517},[290,51039,50465],{"class":303},[290,51041,39206],{"class":299},[290,51043,790],{"class":295},[290,51045,44978],{"class":461},[290,51047,465],{"class":295},[290,51049,1579],{"class":461},[290,51051,801],{"class":541},[290,51053,809],{"class":295},[290,51055,51056],{"class":163,"line":523},[290,51057,620],{"class":295},[290,51059,51060],{"class":163,"line":532},[290,51061,334],{"emptyLinePlaceholder":333},[290,51063,51064],{"class":163,"line":551},[290,51065,51066],{"class":455},"\u002F* Font size itself is not animated; this guards any decorative transition\n",[290,51068,51069],{"class":163,"line":586},[290,51070,51071],{"class":455},"   that might accompany a size change on resize. *\u002F\n",[290,51073,51074,51076],{"class":163,"line":602},[290,51075,874],{"class":541},[290,51077,877],{"class":295},[290,51079,51080,51082,51084,51086,51088,51090,51092],{"class":163,"line":617},[290,51081,50465],{"class":303},[290,51083,39206],{"class":299},[290,51085,790],{"class":295},[290,51087,887],{"class":461},[290,51089,465],{"class":295},[290,51091,72],{"class":461},[290,51093,809],{"class":295},[290,51095,51096],{"class":163,"line":623},[290,51097,620],{"class":295},[14,51099,51100],{},"This keeps a single fluid declaration for the common case and adds exactly one query, rather than reverting to a full stepped matrix.",[50,51102,1299],{"id":1298},[14,51104,51105,569,51107,8393,51109,51112,51113,51115,51116,51119,51120,51122],{},[18,51106,12276],{},[18,51108,32822],{},[18,51110,51111],{},"max()"," are supported in Chrome 79+, Edge 79+, Safari 13.1+, and Firefox 75+, so fluid typography needs no fallback on any engine shipped since 2020. Stepped ",[18,51114,874],{}," width queries work everywhere. If you must support pre-2020 engines, wrap the fluid rule in ",[18,51117,51118],{},"@supports (font-size: clamp(1rem, 1vw, 2rem))"," and provide a static ",[18,51121,801],{}," size before it as the baseline.",[50,51124,1316],{"id":1315},[14,51126,51127,51130,51131,2351,51133,51135,51136,51138],{},[62,51128,51129],{},"Does clamp() typography break browser zoom?","\nNot if you express the min and max bounds in ",[18,51132,801],{},[18,51134,86],{},". The viewport-unit term in the middle does not scale with zoom, but the ",[18,51137,801],{}," floor guarantees a readable minimum, so the result stays accessible.",[14,51140,51141,51144],{},[62,51142,51143],{},"When should I prefer stepped media-query typography over clamp()?","\nChoose stepped media queries when a design demands a fixed size within each breakpoint band, when you must support engines older than 2020, or when art direction requires distinct type at named widths rather than smooth interpolation.",[14,51146,51147,51150,51151,51154],{},[62,51148,51149],{},"What minimum font size satisfies accessibility for the clamp() floor?","\nUse a floor of at least ",[18,51152,51153],{},"1rem"," (16px) for body text. Smaller floors fail WCAG reflow and readability expectations once a user zooms, because the floor is the smallest size the text can ever reach.",[14,51156,51157,51160,51161,51163],{},[62,51158,51159],{},"Can I mix clamp() and media queries in the same type scale?","\nYes. A common hybrid uses ",[18,51162,12276],{}," for smooth interpolation within a band and a media query to reset the preferred term on very large viewports so headings stop growing past a hard ceiling.",[50,51165,1391],{"id":1390},[1393,51167,51168,51173,51178,51184,51189],{},[1396,51169,51170,51172],{},[27,51171,12361],{"href":11312}," — the parent guide framing fluid type systems.",[1396,51174,51175,51177],{},[27,51176,5002],{"href":5001}," — the interpolation math behind the preferred term.",[1396,51179,51180,51183],{},[27,51181,51182],{"href":11317},"Building a Fluid Spacing Scale"," — applying the same clamp() anatomy to margins and gaps.",[1396,51185,51186,51188],{},[27,51187,37796],{"href":37795}," — when component context beats viewport breakpoints.",[1396,51190,51191,51193],{},[27,51192,2511],{"href":1412}," — cross-area guidance on honoring prefers-reduced-motion.",[1430,51195,51196],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":286,"searchDepth":330,"depth":330,"links":51198},[51199,51200,51201,51202,51203,51204,51205],{"id":49868,"depth":330,"text":49869},{"id":4202,"depth":330,"text":4203},{"id":50897,"depth":330,"text":50898},{"id":50949,"depth":330,"text":50950},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"clamp() fluid type vs stepped media-query typography: the min\u002Fpreferred\u002Fmax anatomy, accessibility tradeoffs, zoom and minimum-size safety, and when to pick each.",{"seoTitle":51208,"datePublished":1447,"dateModified":1447,"faq":51209},"clamp() vs Media Query Type: Tradeoffs",[51210,51212,51214,51216],{"q":51129,"a":51211},"Not if you express the min and max bounds in rem or em. The viewport-unit term in the middle does not scale with zoom, but the rem floor guarantees a readable minimum, so the result stays accessible.",{"q":51143,"a":51213},"Choose stepped media queries when a design demands a fixed size within each breakpoint band, when you must support engines older than 2020, or when art direction requires distinct type at named widths rather than smooth interpolation.",{"q":51149,"a":51215},"Use a floor of at least 1rem (16px) for body text. Smaller floors fail WCAG reflow and readability expectations once a user zooms, because the floor is the smallest size the text can ever reach.",{"q":51159,"a":51217},"Yes. A common hybrid uses clamp() for smooth interpolation within a band and a media query to reset the preferred term on very large viewports so headings stop growing past a hard ceiling.","\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Fclamp-vs-media-query-typography",{"title":49845,"description":51206},"mastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Fclamp-vs-media-query-typography\u002Findex","wdY2CTT0AhX48mqjr0hJBPAMcJq3CISJsw5x1Pxq6zI",{"id":51223,"title":51224,"body":51225,"description":52709,"extension":1444,"meta":52710,"navigation":333,"path":52721,"seo":52722,"stem":52723,"__hash__":52724},"content\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-space-scale-with-clamp\u002Findex.md","Building a Fluid Spacing Scale with clamp() and Container Units",{"type":7,"value":51226,"toc":52700},[51227,51230,51251,51255,51267,51288,51301,51303,51305,51313,52336,52342,52398,52402,52421,52423,52427,52432,52602,52605,52607,52629,52631,52642,52654,52660,52666,52668,52698],[10,51228,51224],{"id":51229},"building-a-fluid-spacing-scale-with-clamp-and-container-units",[14,51231,51232,51233,51235,51236,51238,51239,51241,51242,51244,51245,51247,51248,51250],{},"Spacing is usually the last thing to go fluid. Teams adopt ",[18,51234,12276],{}," for headings, then leave margins, padding, and gaps frozen at fixed ",[18,51237,801],{}," values that look cramped on large layouts and bloated on small ones. This guide builds a complete fluid spacing scale as design tokens, using ",[18,51240,12276],{}," for the interpolation and ",[18,51243,11404],{}," container units so spacing tracks the component rather than the whole window. It belongs to ",[27,51246,12361],{"href":11312}," within ",[27,51249,11296],{"href":5777},", and it reuses the same min\u002Fpreferred\u002Fmax anatomy that drives fluid type, applied to space instead of size.",[50,51252,51254],{"id":51253},"why-container-units-beat-viewport-units-for-spacing","Why container units beat viewport units for spacing",[14,51256,51257,51258,51260,51261,51263,51264,51266],{},"A spacing scale exists to create consistent rhythm. If the rhythm is keyed to the viewport with ",[18,51259,12094],{},", every component inherits the page width as its reference, so a card in a narrow sidebar gets the same generous padding as a hero spanning the full page. That breaks the intent: the card looks padded out of proportion to its own size. Container units fix this. ",[18,51262,11404],{}," resolves against the inline size of the nearest ancestor that declares ",[18,51265,24401],{},", so a component's spacing grows with the component. Drop the same card into a 280px rail or a 900px main column and its internal gaps stay proportional in both, with no context-specific overrides.",[14,51268,51269,51270,69,51272,51275,51276,51278,51279,51281,51282,51284,51285,51287],{},"The accessibility rule from typography carries over unchanged: the ",[18,51271,32765],{},[18,51273,51274],{},"max"," bounds of each ",[18,51277,12276],{}," must be ",[18,51280,801],{},", never ",[18,51283,674],{}," or a bare container unit, so a user who zooms or raises their OS font size gets correspondingly larger spacing and never a collapsed gap. Only the preferred middle term should hold the ",[18,51286,11404],{}," slope. Because everything resolves in CSS during layout, there is no JavaScript and no resize listener — the spacing recalculates as part of the normal layout pass, the same way fluid type does.",[14,51289,51290,51291,51293,51294,51297,51298,51300],{},"There is a deliberate split worth naming. Page-level rhythm — the outer page gutters, the vertical spacing between major sections — can legitimately use ",[18,51292,12094],{},", because there the viewport ",[86,51295,51296],{},"is"," the relevant reference. Component-internal rhythm should use ",[18,51299,11404],{},". A well-built token set exposes both and lets each context pick.",[47,51302],{},[50,51304,4203],{"id":4202},[14,51306,51307,51308,51310,51311,42],{},"This block defines an eight-step token scale, wires a card to a sizing container, and lays the cards out in a grid whose gap is itself a fluid token. Every bound is ",[18,51309,801],{},"; every slope is ",[18,51312,11404],{},[281,51314,51316],{"className":283,"code":51315,"language":285,"meta":286,"style":286},"\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\u003Cstyle>\n  :root {\n    \u002F* Fluid spacing tokens. Each is clamp(rem floor, rem base + cqi slope, rem ceiling).\n       The cqi term means: grow with the component's own width. *\u002F\n    --space-3xs: clamp(0.25rem, 0.2rem + 0.4cqi, 0.5rem);\n    --space-2xs: clamp(0.5rem,  0.4rem + 0.6cqi, 0.75rem);\n    --space-xs:  clamp(0.75rem, 0.6rem + 0.8cqi, 1rem);\n    --space-s:   clamp(1rem,    0.8rem + 1cqi,   1.5rem);\n    --space-m:   clamp(1.5rem,  1.1rem + 1.6cqi, 2.25rem);\n    --space-l:   clamp(2rem,    1.4rem + 2.4cqi, 3.5rem);\n    --space-xl:  clamp(3rem,    2rem + 3.6cqi,   5rem);\n    --space-2xl: clamp(4rem,    2.6rem + 5cqi,   7rem);\n  }\n\n  body { margin: 0; font-family: system-ui, sans-serif; }\n\n  \u002F* The grid region establishes a container so its children's cqi resolves\n     against this region's width, and uses a fluid token for the gap. *\u002F\n  .grid {\n    container-type: inline-size;\n    display: grid;\n    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));\n    gap: var(--space-m);\n    padding: var(--space-l);\n  }\n\n  \u002F* Each card is also its own container, so its internal padding\u002Fgaps\n     scale to the card width, not the grid width. *\u002F\n  .card {\n    container-type: inline-size;\n    display: grid;\n    gap: var(--space-s);\n    padding: var(--space-m);\n    border: 1px solid #d0d0d0;\n    border-radius: var(--space-xs);\n  }\n\n  .card h3 { margin: 0; }\n  .card p  { margin: 0; line-height: 1.5; }\n\n  \u002F* A pill uses the smallest tokens; it spaces itself by its own width *\u002F\n  .tag {\n    container-type: inline-size;\n    display: inline-block;\n    padding: var(--space-3xs) var(--space-xs);\n    border-radius: 999px;\n    background: #7aa2ff33;\n    font-size: 0.875rem;\n  }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cmain class=\"grid\">\n    \u003Carticle class=\"card\">\n      \u003Cspan class=\"tag\">Tokens\u003C\u002Fspan>\n      \u003Ch3>Proportional padding\u003C\u002Fh3>\n      \u003Cp>This card's gaps grow with the card, not the page, because cqi resolves against the card.\u003C\u002Fp>\n    \u003C\u002Farticle>\n    \u003Carticle class=\"card\">\n      \u003Cspan class=\"tag\">Rhythm\u003C\u002Fspan>\n      \u003Ch3>Consistent scale\u003C\u002Fh3>\n      \u003Cp>Every gap, margin, and pad references one of eight named tokens.\u003C\u002Fp>\n    \u003C\u002Farticle>\n    \u003Carticle class=\"card\">\n      \u003Cspan class=\"tag\">Zoom-safe\u003C\u002Fspan>\n      \u003Ch3>rem bounds\u003C\u002Fh3>\n      \u003Cp>Floors and ceilings are in rem, so spacing respects browser zoom.\u003C\u002Fp>\n    \u003C\u002Farticle>\n  \u003C\u002Fmain>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,51317,51318,51328,51342,51350,51364,51384,51392,51398,51403,51408,51443,51479,51515,51553,51588,51625,51661,51698,51702,51706,51732,51736,51741,51746,51752,51758,51768,51798,51813,51828,51832,51836,51841,51846,51852,51858,51868,51883,51897,51914,51929,51933,51937,51953,51977,51981,51986,51993,51999,52009,52032,52044,52055,52068,52072,52080,52088,52096,52110,52124,52144,52157,52170,52178,52192,52211,52224,52237,52245,52259,52278,52291,52304,52312,52320,52328],{"__ignoreMap":286},[290,51319,51320,51322,51324,51326],{"class":163,"line":292},[290,51321,8982],{"class":295},[290,51323,35913],{"class":299},[290,51325,8988],{"class":303},[290,51327,327],{"class":295},[290,51329,51330,51332,51334,51336,51338,51340],{"class":163,"line":330},[290,51331,296],{"class":295},[290,51333,285],{"class":299},[290,51335,8999],{"class":303},[290,51337,307],{"class":295},[290,51339,9004],{"class":310},[290,51341,327],{"class":295},[290,51343,51344,51346,51348],{"class":163,"line":337},[290,51345,296],{"class":295},[290,51347,9013],{"class":299},[290,51349,327],{"class":295},[290,51351,51352,51354,51356,51358,51360,51362],{"class":163,"line":364},[290,51353,296],{"class":295},[290,51355,9022],{"class":299},[290,51357,9025],{"class":303},[290,51359,307],{"class":295},[290,51361,9030],{"class":310},[290,51363,327],{"class":295},[290,51365,51366,51368,51370,51372,51374,51376,51378,51380,51382],{"class":163,"line":386},[290,51367,296],{"class":295},[290,51369,9022],{"class":299},[290,51371,35962],{"class":303},[290,51373,307],{"class":295},[290,51375,35967],{"class":310},[290,51377,35970],{"class":303},[290,51379,307],{"class":295},[290,51381,35975],{"class":310},[290,51383,327],{"class":295},[290,51385,51386,51388,51390],{"class":163,"line":408},[290,51387,296],{"class":295},[290,51389,1430],{"class":299},[290,51391,327],{"class":295},[290,51393,51394,51396],{"class":163,"line":428},[290,51395,3430],{"class":303},[290,51397,450],{"class":295},[290,51399,51400],{"class":163,"line":517},[290,51401,51402],{"class":455},"    \u002F* Fluid spacing tokens. Each is clamp(rem floor, rem base + cqi slope, rem ceiling).\n",[290,51404,51405],{"class":163,"line":523},[290,51406,51407],{"class":455},"       The cqi term means: grow with the component's own width. *\u002F\n",[290,51409,51410,51413,51415,51417,51419,51421,51423,51425,51427,51429,51431,51433,51435,51437,51439,51441],{"class":163,"line":532},[290,51411,51412],{"class":1561},"    --space-3xs",[290,51414,465],{"class":295},[290,51416,11555],{"class":461},[290,51418,484],{"class":295},[290,51420,1668],{"class":461},[290,51422,801],{"class":541},[290,51424,569],{"class":295},[290,51426,566],{"class":461},[290,51428,801],{"class":541},[290,51430,3592],{"class":1561},[290,51432,50584],{"class":461},[290,51434,11404],{"class":541},[290,51436,569],{"class":295},[290,51438,798],{"class":461},[290,51440,801],{"class":541},[290,51442,500],{"class":295},[290,51444,51445,51448,51450,51452,51454,51456,51458,51461,51463,51465,51467,51469,51471,51473,51475,51477],{"class":163,"line":551},[290,51446,51447],{"class":1561},"    --space-2xs",[290,51449,465],{"class":295},[290,51451,11555],{"class":461},[290,51453,484],{"class":295},[290,51455,798],{"class":461},[290,51457,801],{"class":541},[290,51459,51460],{"class":295},",  ",[290,51462,169],{"class":461},[290,51464,801],{"class":541},[290,51466,3592],{"class":1561},[290,51468,39313],{"class":461},[290,51470,11404],{"class":541},[290,51472,569],{"class":295},[290,51474,823],{"class":461},[290,51476,801],{"class":541},[290,51478,500],{"class":295},[290,51480,51481,51484,51487,51489,51491,51493,51495,51497,51499,51501,51503,51505,51507,51509,51511,51513],{"class":163,"line":586},[290,51482,51483],{"class":1561},"    --space-xs",[290,51485,51486],{"class":295},":  ",[290,51488,11555],{"class":461},[290,51490,484],{"class":295},[290,51492,823],{"class":461},[290,51494,801],{"class":541},[290,51496,569],{"class":295},[290,51498,232],{"class":461},[290,51500,801],{"class":541},[290,51502,3592],{"class":1561},[290,51504,17938],{"class":461},[290,51506,11404],{"class":541},[290,51508,569],{"class":295},[290,51510,468],{"class":461},[290,51512,801],{"class":541},[290,51514,500],{"class":295},[290,51516,51517,51520,51523,51525,51527,51529,51531,51534,51536,51538,51540,51542,51544,51547,51549,51551],{"class":163,"line":602},[290,51518,51519],{"class":1561},"    --space-s",[290,51521,51522],{"class":295},":   ",[290,51524,11555],{"class":461},[290,51526,484],{"class":295},[290,51528,468],{"class":461},[290,51530,801],{"class":541},[290,51532,51533],{"class":295},",    ",[290,51535,572],{"class":461},[290,51537,801],{"class":541},[290,51539,3592],{"class":1561},[290,51541,804],{"class":461},[290,51543,11404],{"class":541},[290,51545,51546],{"class":295},",   ",[290,51548,168],{"class":461},[290,51550,801],{"class":541},[290,51552,500],{"class":295},[290,51554,51555,51558,51560,51562,51564,51566,51568,51570,51572,51574,51576,51578,51580,51582,51584,51586],{"class":163,"line":617},[290,51556,51557],{"class":1561},"    --space-m",[290,51559,51522],{"class":295},[290,51561,11555],{"class":461},[290,51563,484],{"class":295},[290,51565,168],{"class":461},[290,51567,801],{"class":541},[290,51569,51460],{"class":295},[290,51571,39227],{"class":461},[290,51573,801],{"class":541},[290,51575,3592],{"class":1561},[290,51577,50539],{"class":461},[290,51579,11404],{"class":541},[290,51581,569],{"class":295},[290,51583,50330],{"class":461},[290,51585,801],{"class":541},[290,51587,500],{"class":295},[290,51589,51590,51593,51595,51597,51599,51601,51603,51605,51607,51609,51611,51614,51616,51618,51621,51623],{"class":163,"line":623},[290,51591,51592],{"class":1561},"    --space-l",[290,51594,51522],{"class":295},[290,51596,11555],{"class":461},[290,51598,484],{"class":295},[290,51600,194],{"class":461},[290,51602,801],{"class":541},[290,51604,51533],{"class":295},[290,51606,1824],{"class":461},[290,51608,801],{"class":541},[290,51610,3592],{"class":1561},[290,51612,51613],{"class":461}," 2.4",[290,51615,11404],{"class":541},[290,51617,569],{"class":295},[290,51619,51620],{"class":461},"3.5",[290,51622,801],{"class":541},[290,51624,500],{"class":295},[290,51626,51627,51630,51632,51634,51636,51638,51640,51642,51644,51646,51648,51651,51653,51655,51657,51659],{"class":163,"line":628},[290,51628,51629],{"class":1561},"    --space-xl",[290,51631,51486],{"class":295},[290,51633,11555],{"class":461},[290,51635,484],{"class":295},[290,51637,1579],{"class":461},[290,51639,801],{"class":541},[290,51641,51533],{"class":295},[290,51643,194],{"class":461},[290,51645,801],{"class":541},[290,51647,3592],{"class":1561},[290,51649,51650],{"class":461}," 3.6",[290,51652,11404],{"class":541},[290,51654,51546],{"class":295},[290,51656,5911],{"class":461},[290,51658,801],{"class":541},[290,51660,500],{"class":295},[290,51662,51663,51666,51668,51670,51672,51674,51676,51678,51681,51683,51685,51688,51690,51692,51694,51696],{"class":163,"line":634},[290,51664,51665],{"class":1561},"    --space-2xl",[290,51667,465],{"class":295},[290,51669,11555],{"class":461},[290,51671,484],{"class":295},[290,51673,249],{"class":461},[290,51675,801],{"class":541},[290,51677,51533],{"class":295},[290,51679,51680],{"class":461},"2.6",[290,51682,801],{"class":541},[290,51684,3592],{"class":1561},[290,51686,51687],{"class":461}," 5",[290,51689,11404],{"class":541},[290,51691,51546],{"class":295},[290,51693,248],{"class":461},[290,51695,801],{"class":541},[290,51697,500],{"class":295},[290,51699,51700],{"class":163,"line":649},[290,51701,771],{"class":295},[290,51703,51704],{"class":163,"line":660},[290,51705,334],{"emptyLinePlaceholder":333},[290,51707,51708,51710,51712,51714,51716,51718,51720,51722,51724,51726,51728,51730],{"class":163,"line":688},[290,51709,44444],{"class":299},[290,51711,790],{"class":295},[290,51713,2725],{"class":461},[290,51715,465],{"class":295},[290,51717,487],{"class":461},[290,51719,828],{"class":295},[290,51721,50110],{"class":461},[290,51723,465],{"class":295},[290,51725,50115],{"class":461},[290,51727,569],{"class":295},[290,51729,44479],{"class":461},[290,51731,809],{"class":295},[290,51733,51734],{"class":163,"line":693},[290,51735,334],{"emptyLinePlaceholder":333},[290,51737,51738],{"class":163,"line":698},[290,51739,51740],{"class":455},"  \u002F* The grid region establishes a container so its children's cqi resolves\n",[290,51742,51743],{"class":163,"line":704},[290,51744,51745],{"class":455},"     against this region's width, and uses a fluid token for the gap. *\u002F\n",[290,51747,51748,51750],{"class":163,"line":710},[290,51749,9046],{"class":303},[290,51751,450],{"class":295},[290,51753,51754,51756],{"class":163,"line":717},[290,51755,37025],{"class":461},[290,51757,25565],{"class":295},[290,51759,51760,51762,51764,51766],{"class":163,"line":730},[290,51761,34590],{"class":461},[290,51763,465],{"class":295},[290,51765,9147],{"class":461},[290,51767,471],{"class":295},[290,51769,51770,51772,51774,51776,51778,51780,51782,51784,51786,51788,51790,51792,51794,51796],{"class":163,"line":742},[290,51771,36011],{"class":461},[290,51773,465],{"class":295},[290,51775,44542],{"class":461},[290,51777,484],{"class":295},[290,51779,44547],{"class":461},[290,51781,569],{"class":295},[290,51783,44552],{"class":461},[290,51785,484],{"class":295},[290,51787,9212],{"class":461},[290,51789,801],{"class":541},[290,51791,569],{"class":295},[290,51793,468],{"class":461},[290,51795,11964],{"class":541},[290,51797,11616],{"class":295},[290,51799,51800,51802,51804,51806,51808,51811],{"class":163,"line":768},[290,51801,36027],{"class":461},[290,51803,465],{"class":295},[290,51805,1622],{"class":461},[290,51807,484],{"class":295},[290,51809,51810],{"class":1561},"--space-m",[290,51812,500],{"class":295},[290,51814,51815,51817,51819,51821,51823,51826],{"class":163,"line":774},[290,51816,36040],{"class":461},[290,51818,465],{"class":295},[290,51820,1622],{"class":461},[290,51822,484],{"class":295},[290,51824,51825],{"class":1561},"--space-l",[290,51827,500],{"class":295},[290,51829,51830],{"class":163,"line":779},[290,51831,771],{"class":295},[290,51833,51834],{"class":163,"line":784},[290,51835,334],{"emptyLinePlaceholder":333},[290,51837,51838],{"class":163,"line":812},[290,51839,51840],{"class":455},"  \u002F* Each card is also its own container, so its internal padding\u002Fgaps\n",[290,51842,51843],{"class":163,"line":860},[290,51844,51845],{"class":455},"     scale to the card width, not the grid width. *\u002F\n",[290,51847,51848,51850],{"class":163,"line":865},[290,51849,9083],{"class":303},[290,51851,450],{"class":295},[290,51853,51854,51856],{"class":163,"line":871},[290,51855,37025],{"class":461},[290,51857,25565],{"class":295},[290,51859,51860,51862,51864,51866],{"class":163,"line":880},[290,51861,34590],{"class":461},[290,51863,465],{"class":295},[290,51865,9147],{"class":461},[290,51867,471],{"class":295},[290,51869,51870,51872,51874,51876,51878,51881],{"class":163,"line":896},[290,51871,36027],{"class":461},[290,51873,465],{"class":295},[290,51875,1622],{"class":461},[290,51877,484],{"class":295},[290,51879,51880],{"class":1561},"--space-s",[290,51882,500],{"class":295},[290,51884,51885,51887,51889,51891,51893,51895],{"class":163,"line":4734},[290,51886,36040],{"class":461},[290,51888,465],{"class":295},[290,51890,1622],{"class":461},[290,51892,484],{"class":295},[290,51894,51810],{"class":1561},[290,51896,500],{"class":295},[290,51898,51899,51901,51903,51905,51907,51909,51912],{"class":163,"line":4742},[290,51900,21285],{"class":461},[290,51902,465],{"class":295},[290,51904,468],{"class":461},[290,51906,674],{"class":541},[290,51908,852],{"class":461},[290,51910,51911],{"class":461}," #d0d0d0",[290,51913,471],{"class":295},[290,51915,51916,51918,51920,51922,51924,51927],{"class":163,"line":4761},[290,51917,12759],{"class":461},[290,51919,465],{"class":295},[290,51921,1622],{"class":461},[290,51923,484],{"class":295},[290,51925,51926],{"class":1561},"--space-xs",[290,51928,500],{"class":295},[290,51930,51931],{"class":163,"line":4766},[290,51932,771],{"class":295},[290,51934,51935],{"class":163,"line":4786},[290,51936,334],{"emptyLinePlaceholder":333},[290,51938,51939,51941,51943,51945,51947,51949,51951],{"class":163,"line":9635},[290,51940,9083],{"class":303},[290,51942,50258],{"class":299},[290,51944,790],{"class":295},[290,51946,2725],{"class":461},[290,51948,465],{"class":295},[290,51950,487],{"class":461},[290,51952,809],{"class":295},[290,51954,51955,51957,51959,51961,51963,51965,51967,51969,51971,51973,51975],{"class":163,"line":9641},[290,51956,9083],{"class":303},[290,51958,39281],{"class":299},[290,51960,4612],{"class":295},[290,51962,2725],{"class":461},[290,51964,465],{"class":295},[290,51966,487],{"class":461},[290,51968,828],{"class":295},[290,51970,50124],{"class":461},[290,51972,465],{"class":295},[290,51974,168],{"class":461},[290,51976,809],{"class":295},[290,51978,51979],{"class":163,"line":9647},[290,51980,334],{"emptyLinePlaceholder":333},[290,51982,51983],{"class":163,"line":9665},[290,51984,51985],{"class":455},"  \u002F* A pill uses the smallest tokens; it spaces itself by its own width *\u002F\n",[290,51987,51988,51991],{"class":163,"line":9683},[290,51989,51990],{"class":303},"  .tag",[290,51992,450],{"class":295},[290,51994,51995,51997],{"class":163,"line":9688},[290,51996,37025],{"class":461},[290,51998,25565],{"class":295},[290,52000,52001,52003,52005,52007],{"class":163,"line":9694},[290,52002,34590],{"class":461},[290,52004,465],{"class":295},[290,52006,17747],{"class":461},[290,52008,471],{"class":295},[290,52010,52011,52013,52015,52017,52019,52022,52024,52026,52028,52030],{"class":163,"line":9700},[290,52012,36040],{"class":461},[290,52014,465],{"class":295},[290,52016,1622],{"class":461},[290,52018,484],{"class":295},[290,52020,52021],{"class":1561},"--space-3xs",[290,52023,490],{"class":295},[290,52025,1622],{"class":461},[290,52027,484],{"class":295},[290,52029,51926],{"class":1561},[290,52031,500],{"class":295},[290,52033,52034,52036,52038,52040,52042],{"class":163,"line":9710},[290,52035,12759],{"class":461},[290,52037,465],{"class":295},[290,52039,10465],{"class":461},[290,52041,674],{"class":541},[290,52043,471],{"class":295},[290,52045,52046,52048,52050,52053],{"class":163,"line":9716},[290,52047,9124],{"class":461},[290,52049,465],{"class":295},[290,52051,52052],{"class":461},"#7aa2ff33",[290,52054,471],{"class":295},[290,52056,52057,52059,52061,52064,52066],{"class":163,"line":9738},[290,52058,39218],{"class":461},[290,52060,465],{"class":295},[290,52062,52063],{"class":461},"0.875",[290,52065,801],{"class":541},[290,52067,471],{"class":295},[290,52069,52070],{"class":163,"line":9748},[290,52071,771],{"class":295},[290,52073,52074,52076,52078],{"class":163,"line":9754},[290,52075,431],{"class":295},[290,52077,1430],{"class":299},[290,52079,327],{"class":295},[290,52081,52082,52084,52086],{"class":163,"line":9760},[290,52083,431],{"class":295},[290,52085,9013],{"class":299},[290,52087,327],{"class":295},[290,52089,52090,52092,52094],{"class":163,"line":9777},[290,52091,296],{"class":295},[290,52093,9239],{"class":299},[290,52095,327],{"class":295},[290,52097,52098,52100,52102,52104,52106,52108],{"class":163,"line":9787},[290,52099,367],{"class":295},[290,52101,45331],{"class":299},[290,52103,314],{"class":303},[290,52105,307],{"class":295},[290,52107,9274],{"class":310},[290,52109,327],{"class":295},[290,52111,52112,52114,52116,52118,52120,52122],{"class":163,"line":9793},[290,52113,4290],{"class":295},[290,52115,11445],{"class":299},[290,52117,314],{"class":303},[290,52119,307],{"class":295},[290,52121,9295],{"class":310},[290,52123,327],{"class":295},[290,52125,52126,52128,52130,52132,52134,52137,52140,52142],{"class":163,"line":9799},[290,52127,36430],{"class":295},[290,52129,290],{"class":299},[290,52131,314],{"class":303},[290,52133,307],{"class":295},[290,52135,52136],{"class":310},"\"tag\"",[290,52138,52139],{"class":295},">Tokens\u003C\u002F",[290,52141,290],{"class":299},[290,52143,327],{"class":295},[290,52145,52146,52148,52150,52153,52155],{"class":163,"line":9805},[290,52147,36430],{"class":295},[290,52149,2757],{"class":299},[290,52151,52152],{"class":295},">Proportional padding\u003C\u002F",[290,52154,2757],{"class":299},[290,52156,327],{"class":295},[290,52158,52159,52161,52163,52166,52168],{"class":163,"line":9811},[290,52160,36430],{"class":295},[290,52162,14],{"class":299},[290,52164,52165],{"class":295},">This card's gaps grow with the card, not the page, because cqi resolves against the card.\u003C\u002F",[290,52167,14],{"class":299},[290,52169,327],{"class":295},[290,52171,52172,52174,52176],{"class":163,"line":9820},[290,52173,36502],{"class":295},[290,52175,11445],{"class":299},[290,52177,327],{"class":295},[290,52179,52180,52182,52184,52186,52188,52190],{"class":163,"line":9829},[290,52181,4290],{"class":295},[290,52183,11445],{"class":299},[290,52185,314],{"class":303},[290,52187,307],{"class":295},[290,52189,9295],{"class":310},[290,52191,327],{"class":295},[290,52193,52194,52196,52198,52200,52202,52204,52207,52209],{"class":163,"line":27197},[290,52195,36430],{"class":295},[290,52197,290],{"class":299},[290,52199,314],{"class":303},[290,52201,307],{"class":295},[290,52203,52136],{"class":310},[290,52205,52206],{"class":295},">Rhythm\u003C\u002F",[290,52208,290],{"class":299},[290,52210,327],{"class":295},[290,52212,52213,52215,52217,52220,52222],{"class":163,"line":27202},[290,52214,36430],{"class":295},[290,52216,2757],{"class":299},[290,52218,52219],{"class":295},">Consistent scale\u003C\u002F",[290,52221,2757],{"class":299},[290,52223,327],{"class":295},[290,52225,52226,52228,52230,52233,52235],{"class":163,"line":27209},[290,52227,36430],{"class":295},[290,52229,14],{"class":299},[290,52231,52232],{"class":295},">Every gap, margin, and pad references one of eight named tokens.\u003C\u002F",[290,52234,14],{"class":299},[290,52236,327],{"class":295},[290,52238,52239,52241,52243],{"class":163,"line":27220},[290,52240,36502],{"class":295},[290,52242,11445],{"class":299},[290,52244,327],{"class":295},[290,52246,52247,52249,52251,52253,52255,52257],{"class":163,"line":27235},[290,52248,4290],{"class":295},[290,52250,11445],{"class":299},[290,52252,314],{"class":303},[290,52254,307],{"class":295},[290,52256,9295],{"class":310},[290,52258,327],{"class":295},[290,52260,52261,52263,52265,52267,52269,52271,52274,52276],{"class":163,"line":27240},[290,52262,36430],{"class":295},[290,52264,290],{"class":299},[290,52266,314],{"class":303},[290,52268,307],{"class":295},[290,52270,52136],{"class":310},[290,52272,52273],{"class":295},">Zoom-safe\u003C\u002F",[290,52275,290],{"class":299},[290,52277,327],{"class":295},[290,52279,52280,52282,52284,52287,52289],{"class":163,"line":43950},[290,52281,36430],{"class":295},[290,52283,2757],{"class":299},[290,52285,52286],{"class":295},">rem bounds\u003C\u002F",[290,52288,2757],{"class":299},[290,52290,327],{"class":295},[290,52292,52293,52295,52297,52300,52302],{"class":163,"line":43959},[290,52294,36430],{"class":295},[290,52296,14],{"class":299},[290,52298,52299],{"class":295},">Floors and ceilings are in rem, so spacing respects browser zoom.\u003C\u002F",[290,52301,14],{"class":299},[290,52303,327],{"class":295},[290,52305,52306,52308,52310],{"class":163,"line":43968},[290,52307,36502],{"class":295},[290,52309,11445],{"class":299},[290,52311,327],{"class":295},[290,52313,52314,52316,52318],{"class":163,"line":43977},[290,52315,4315],{"class":295},[290,52317,45331],{"class":299},[290,52319,327],{"class":295},[290,52321,52322,52324,52326],{"class":163,"line":43986},[290,52323,431],{"class":295},[290,52325,9239],{"class":299},[290,52327,327],{"class":295},[290,52329,52330,52332,52334],{"class":163,"line":43995},[290,52331,431],{"class":295},[290,52333,285],{"class":299},[290,52335,327],{"class":295},[14,52337,52338,52339,52341],{},"Narrow the window and watch the cards reflow; each card's internal padding shrinks toward its ",[18,52340,801],{}," floor while the grid gap shrinks independently, because they reference different containers.",[133,52343,140,52345,140,52348,140,52351,140,52354,140,52357,140,52362,140,52364,140,52367,140,52369,140,52373,140,52375,140,52377,140,52379,140,52382,140,52384,140,52387,140,52389,140,52393,140,52395],{"viewBox":4133,"role":136,"ariaLabel":52344,"xmlns":138,"style":139},"A fluid spacing scale ramp from smallest to largest token, each bar widening as the step increases",[142,52346,52347],{},"Fluid spacing scale ramp",[146,52349,52350],{},"Eight stacked bars, each labeled with a token name, growing in width from the smallest 3xs step to the largest 2xl step.",[150,52352,52353],{"x":152,"y":153,"style":1781},"spacing token ramp",[171,52355],{"x":1787,"y":1831,"width":4146,"height":17581,"rx":1579,"fill":177,"opacity":52356},"0.35",[150,52358,52361],{"x":4195,"y":52359,"style":52360},"66","text-anchor:end;fill:currentColor;font:12px ui-monospace,monospace","3xs",[171,52363],{"x":1787,"y":41988,"width":5144,"height":17581,"rx":1579,"fill":177,"opacity":52356},[150,52365,52366],{"x":4195,"y":18845,"style":52360},"2xs",[171,52368],{"x":1787,"y":6645,"width":5159,"height":17581,"rx":1579,"fill":177,"opacity":52356},[150,52370,52372],{"x":4195,"y":52371,"style":52360},"122","xs",[171,52374],{"x":1787,"y":17575,"width":1822,"height":17581,"rx":1579,"fill":177,"opacity":52356},[150,52376,1886],{"x":4195,"y":1787,"style":52360},[171,52378],{"x":1787,"y":43267,"width":3112,"height":17581,"rx":1579,"fill":177,"opacity":52356},[150,52380,52381],{"x":4195,"y":5154,"style":52360},"m",[171,52383],{"x":1787,"y":2615,"width":50885,"height":17581,"rx":1579,"fill":177,"opacity":52356},[150,52385,52386],{"x":4195,"y":11367,"style":52360},"l",[171,52388],{"x":1787,"y":34174,"width":2641,"height":17581,"rx":1579,"fill":177,"opacity":52356},[150,52390,52392],{"x":4195,"y":52391,"style":52360},"234","xl",[171,52394],{"x":1787,"y":19916,"width":5887,"height":17581,"rx":1579,"fill":177,"opacity":52356},[150,52396,52397],{"x":4195,"y":5914,"style":52360},"2xl",[50,52399,52401],{"id":52400},"key-technique-cqi-in-the-preferred-term-rem-in-the-bounds","Key technique: cqi in the preferred term, rem in the bounds",[14,52403,52404,52405,50917,52408,52410,52411,52414,52415,52417,52418,52420],{},"The whole scale follows one shape per token: ",[18,52406,52407],{},"clamp(rem-floor, rem-base + N cqi, rem-ceiling)",[18,52409,11404],{}," unit equals 1% of the container's inline size, so ",[18,52412,52413],{},"1cqi"," on a 600px-wide container is 6px and on a 300px container is 3px — the slope automatically halves when the component is half as wide. That is exactly the proportional behavior a spacing scale wants. Keeping the floor and ceiling in ",[18,52416,801],{}," means the token can never produce zero spacing on a tiny container nor a runaway gap on a huge one, and both bounds still answer to zoom. Adjust the single ",[18,52419,11404],{}," coefficient per step to tune how aggressively that step responds to size.",[47,52422],{},[50,52424,52426],{"id":52425},"variation-tokens-that-also-drive-transition-durations","Variation: tokens that also drive transition durations",[14,52428,52429,52430,42],{},"Because each token is a plain custom property, the spacing scale can do double duty and feed motion timing, so larger components animate slightly slower and motion stays proportional to space. This is the same idea explored cross-area in ",[27,52431,4901],{"href":4900},[281,52433,52435],{"className":438,"code":52434,"language":440,"meta":286,"style":286},":root {\n  \u002F* Derive a duration from a spacing magnitude: more space, more travel,\n     so allow a touch more time. Bounds keep it within sane motion limits. *\u002F\n  --motion-s: clamp(120ms, 80ms + 1cqi, 220ms);\n  --motion-m: clamp(180ms, 120ms + 1.6cqi, 320ms);\n}\n\n.card {\n  transition: transform var(--motion-m) ease, box-shadow var(--motion-s) ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .card { transition: none; }\n}\n",[18,52436,52437,52443,52448,52453,52488,52523,52527,52531,52537,52570,52574,52578,52584,52598],{"__ignoreMap":286},[290,52438,52439,52441],{"class":163,"line":292},[290,52440,1554],{"class":303},[290,52442,450],{"class":295},[290,52444,52445],{"class":163,"line":330},[290,52446,52447],{"class":455},"  \u002F* Derive a duration from a spacing magnitude: more space, more travel,\n",[290,52449,52450],{"class":163,"line":337},[290,52451,52452],{"class":455},"     so allow a touch more time. Bounds keep it within sane motion limits. *\u002F\n",[290,52454,52455,52458,52460,52462,52464,52466,52468,52470,52472,52474,52476,52478,52480,52482,52484,52486],{"class":163,"line":364},[290,52456,52457],{"class":1561},"  --motion-s",[290,52459,465],{"class":295},[290,52461,11555],{"class":461},[290,52463,484],{"class":295},[290,52465,4147],{"class":461},[290,52467,542],{"class":541},[290,52469,569],{"class":295},[290,52471,9105],{"class":461},[290,52473,542],{"class":541},[290,52475,3592],{"class":1561},[290,52477,804],{"class":461},[290,52479,11404],{"class":541},[290,52481,569],{"class":295},[290,52483,538],{"class":461},[290,52485,542],{"class":541},[290,52487,500],{"class":295},[290,52489,52490,52493,52495,52497,52499,52501,52503,52505,52507,52509,52511,52513,52515,52517,52519,52521],{"class":163,"line":386},[290,52491,52492],{"class":1561},"  --motion-m",[290,52494,465],{"class":295},[290,52496,11555],{"class":461},[290,52498,484],{"class":295},[290,52500,2602],{"class":461},[290,52502,542],{"class":541},[290,52504,569],{"class":295},[290,52506,4147],{"class":461},[290,52508,542],{"class":541},[290,52510,3592],{"class":1561},[290,52512,50539],{"class":461},[290,52514,11404],{"class":541},[290,52516,569],{"class":295},[290,52518,2613],{"class":461},[290,52520,542],{"class":541},[290,52522,500],{"class":295},[290,52524,52525],{"class":163,"line":408},[290,52526,620],{"class":295},[290,52528,52529],{"class":163,"line":428},[290,52530,334],{"emptyLinePlaceholder":333},[290,52532,52533,52535],{"class":163,"line":517},[290,52534,11528],{"class":303},[290,52536,450],{"class":295},[290,52538,52539,52541,52543,52545,52547,52550,52552,52554,52557,52559,52561,52564,52566,52568],{"class":163,"line":523},[290,52540,526],{"class":461},[290,52542,1880],{"class":295},[290,52544,1622],{"class":461},[290,52546,484],{"class":295},[290,52548,52549],{"class":1561},"--motion-m",[290,52551,490],{"class":295},[290,52553,6770],{"class":461},[290,52555,52556],{"class":295},", box-shadow ",[290,52558,1622],{"class":461},[290,52560,484],{"class":295},[290,52562,52563],{"class":1561},"--motion-s",[290,52565,490],{"class":295},[290,52567,6770],{"class":461},[290,52569,471],{"class":295},[290,52571,52572],{"class":163,"line":532},[290,52573,620],{"class":295},[290,52575,52576],{"class":163,"line":551},[290,52577,334],{"emptyLinePlaceholder":333},[290,52579,52580,52582],{"class":163,"line":586},[290,52581,874],{"class":541},[290,52583,877],{"class":295},[290,52585,52586,52588,52590,52592,52594,52596],{"class":163,"line":602},[290,52587,9083],{"class":303},[290,52589,790],{"class":295},[290,52591,887],{"class":461},[290,52593,465],{"class":295},[290,52595,72],{"class":461},[290,52597,809],{"class":295},[290,52599,52600],{"class":163,"line":617},[290,52601,620],{"class":295},[14,52603,52604],{},"The reduced-motion guard is mandatory: proportional duration is still motion, and users who opt out must get none.",[50,52606,1299],{"id":1298},[14,52608,52609,52611,52612,569,52614,8393,52616,52618,52619,52621,52622,52624,52625,52628],{},[18,52610,12276],{}," is supported in Chrome 79+, Edge 79+, Safari 13.1+, and Firefox 75+. Container query units ",[18,52613,11404],{},[18,52615,38792],{},[18,52617,12282],{}," require Chrome 105+, Edge 105+, Safari 16.0+, and Firefox 110+. Provide a static ",[18,52620,801],{}," fallback before the container-unit rule, or wrap the ",[18,52623,11404],{}," tokens in ",[18,52626,52627],{},"@supports (width: 1cqi)",", so engines without container units still get a sensible fixed scale.",[50,52630,1316],{"id":1315},[14,52632,52633,52636,52638,52639,52641],{},[62,52634,52635],{},"Why use cqi instead of vw for a spacing scale?",[18,52637,11404],{}," sizes against the nearest sizing container, so a component spaces itself by its own width. The same component then looks correct in a sidebar and a full-width region without per-context overrides, which ",[18,52640,12094],{}," cannot do.",[14,52643,52644,52647,52648,52650,52651,52653],{},[62,52645,52646],{},"Should spacing tokens use rem floors like typography tokens?","\nYes. Express the ",[18,52649,12276],{}," min and max in ",[18,52652,801],{}," so spacing respects browser zoom and never collapses to an unusably small gap when a user enlarges text.",[14,52655,52656,52659],{},[62,52657,52658],{},"How many steps should a fluid spacing scale have?","\nSix to eight named steps covers most interfaces. A common ratio is roughly 1.5 between adjacent steps, which keeps the rhythm visible without producing dozens of near-identical tokens.",[14,52661,52662,52665],{},[62,52663,52664],{},"Can the same tokens drive animation timing?","\nYes. Because the tokens are plain custom properties, the same scale that sizes gaps can feed transition durations, keeping motion proportional to spacing across the design system.",[50,52667,1391],{"id":1390},[1393,52669,52670,52675,52682,52688,52693],{},[1396,52671,52672,52674],{},[27,52673,12361],{"href":11312}," — the parent guide for clamp()-driven scales.",[1396,52676,52677,52681],{},[27,52678,52680],{"href":52679},"\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Fclamp-vs-media-query-typography\u002F","clamp() vs Media-Query Typography"," — the same anatomy applied to font sizing.",[1396,52683,52684,52687],{},[27,52685,52686],{"href":11248},"Container Query Units cqi\u002Fcqb Explained"," — how cqi resolves against a sizing container.",[1396,52689,52690,52692],{},[27,52691,4901],{"href":4900}," — cross-area: reusing space tokens for motion timing.",[1396,52694,52695,52697],{},[27,52696,3355],{"href":10147}," — cross-area patterns for token-driven systems.",[1430,52699,51196],{},{"title":286,"searchDepth":330,"depth":330,"links":52701},[52702,52703,52704,52705,52706,52707,52708],{"id":51253,"depth":330,"text":51254},{"id":4202,"depth":330,"text":4203},{"id":52400,"depth":330,"text":52401},{"id":52425,"depth":330,"text":52426},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Build a fluid spacing scale with CSS clamp() and cqi container units: design tokens for margins, padding, and gaps that grow with the layout, not the viewport.",{"seoTitle":52711,"datePublished":1447,"dateModified":1447,"faq":52712},"Fluid Space Scale with clamp() and cqi",[52713,52715,52717,52719],{"q":52635,"a":52714},"cqi sizes against the nearest sizing container, so a component spaces itself by its own width. The same component then looks correct in a sidebar and a full-width region without per-context overrides, which vw cannot do.",{"q":52646,"a":52716},"Yes. Express the clamp() min and max in rem so spacing respects browser zoom and never collapses to an unusably small gap when a user enlarges text.",{"q":52658,"a":52718},"Six to eight named steps covers most interfaces. A common ratio is roughly 1.5 between adjacent steps, which keeps the rhythm visible without producing dozens of near-identical tokens.",{"q":52664,"a":52720},"Yes. Because the tokens are plain custom properties, the same scale that sizes gaps can feed transition durations, keeping motion proportional to spacing across the design system.","\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-space-scale-with-clamp",{"title":51224,"description":52709},"mastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-space-scale-with-clamp\u002Findex","fi2OfoLt4VgkwqQ6dGn7cpxBMZQBMdvxirGY8iRU9kU",{"id":52726,"title":52727,"body":52728,"description":53674,"extension":1444,"meta":53675,"navigation":333,"path":53688,"seo":53689,"stem":53690,"__hash__":53691},"content\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-typography-without-javascript\u002Findex.md","Fluid Typography Without JavaScript: A Precision CSS Reference",{"type":7,"value":52729,"toc":53664},[52730,52733,52742,52747,52766,52806,52810,52820,52828,52841,52872,52917,52928,52932,52952,53087,53106,53110,53121,53208,53224,53228,53238,53354,53359,53393,53395,53473,53483,53485,53504,53530,53554,53572,53574,53596,53609,53626,53637,53639,53662],[10,52731,52727],{"id":52732},"fluid-typography-without-javascript-a-precision-css-reference",[14,52734,52735,52736,52738,52739,52741],{},"This guide provides a production-ready blueprint for implementing ",[27,52737,12361],{"href":11312}," and viewport-adaptive scaling using pure CSS. By leveraging modern math functions and intrinsic sizing, frontend teams can eliminate JavaScript overhead, prevent cumulative layout shift (CLS), and maintain typographic precision across all breakpoints. The methodology aligns with the broader architectural principles outlined in ",[27,52740,11296],{"href":5777},", ensuring isolated component behavior without global viewport dependencies.",[14,52743,52744],{},[62,52745,52746],{},"Implementation Targets:",[1393,52748,52749,52752,52760,52763],{},[1396,52750,52751],{},"Eliminates JS parsing overhead and layout shift",[1396,52753,52754,52755,69,52757,52759],{},"Uses CSS ",[18,52756,3693],{},[18,52758,12276],{}," for deterministic scaling",[1396,52761,52762],{},"Provides explicit fallbacks for legacy rendering engines",[1396,52764,52765],{},"Optimized for Core Web Vitals and accessibility zoom",[133,52767,140,52769,140,52772,140,52775,140,52777,140,52779,140,52781,140,52785,140,52787,140,52789,140,52791,140,52794,140,52797,140,52799,140,52803],{"viewBox":4133,"role":136,"ariaLabel":52768,"xmlns":138,"style":139},"Graph of clamp output staying flat at the minimum, scaling linearly, then flattening at the maximum",[142,52770,52771],{},"How clamp() bounds font size across viewport width",[146,52773,52774],{},"Font size holds at the minimum on narrow screens, rises linearly through the middle range, and caps at the maximum on wide screens.",[150,52776,39669],{"x":152,"y":153,"style":1781},[163,52778],{"x1":9105,"y1":3112,"x2":25430,"y2":3112,"stroke":167,"strokeWidth":168},[163,52780],{"x1":9105,"y1":1786,"x2":9105,"y2":3112,"stroke":167,"strokeWidth":168},[150,52782,52784],{"x":17554,"y":52783,"style":5915},"284","viewport width",[163,52786],{"x1":9105,"y1":2622,"x2":8947,"y2":2622,"stroke":177,"strokeWidth":1579},[163,52788],{"x1":8947,"y1":2622,"x2":18875,"y2":165,"stroke":177,"strokeWidth":1579},[163,52790],{"x1":18875,"y1":165,"x2":25430,"y2":165,"stroke":177,"strokeWidth":1579},[163,52792],{"x1":8947,"y1":1786,"x2":8947,"y2":3112,"stroke":167,"strokeWidth":468,"strokeDashArray":52793,"opacity":798},[249,249],[163,52795],{"x1":18875,"y1":1786,"x2":18875,"y2":3112,"stroke":167,"strokeWidth":468,"strokeDashArray":52796,"opacity":798},[249,249],[150,52798,50879],{"x":8912,"y":174,"style":10262},[150,52800,52802],{"x":52801,"y":17543,"style":10262},"370","preferred slope",[150,52804,50876],{"x":52805,"y":5115,"style":10262},"590",[50,52807,52809],{"id":52808},"viewport-interpolation-linear-scaling-math","Viewport Interpolation & Linear Scaling Math",[14,52811,52812,52813,52815,52816,52819],{},"Before adopting shorthand functions, establish the mathematical baseline. Fluid type relies on linear interpolation between two viewport bounds. The slope (",[18,52814,52381],{},") and intercept (",[18,52817,52818],{},"b",") are derived as follows:",[14,52821,52822,52825],{},[18,52823,52824],{},"m = (max_font - min_font) \u002F (max_vw - min_vw)",[18,52826,52827],{},"b = min_font - (m * min_vw)",[14,52829,52830,52831,52834,52835,52837,52838,52840],{},"The resulting formula ",[18,52832,52833],{},"font-size = b + (m * 100vw)"," guarantees exact pixel-to-rem conversion at defined breakpoints. To prevent typographic bloat on ultra-wide displays, cap the output using ",[18,52836,51111],{}," or wrap in a ",[18,52839,874],{}," query.",[14,52842,52843,52844,52846,52847,52850,52851,52853,52854,52856,52857,52860,52861,16198,52864,52867,52868,52871],{},"CSS ",[18,52845,3693],{}," cannot divide values with different units (e.g., ",[18,52848,52849],{},"rem \u002F px","), so the slope must be pre-computed in pixels and expressed as a ",[18,52852,12094],{}," percentage. For a range of ",[18,52855,51153],{}," (16px) → ",[18,52858,52859],{},"2rem"," (32px) across ",[18,52862,52863],{},"320px",[18,52865,52866],{},"1440px"," viewports, the slope is ",[18,52869,52870],{},"(32−16)\u002F(1440−320)×100 ≈ 1.43vw",". Write it directly:",[281,52873,52875],{"className":438,"code":52874,"language":440,"meta":286,"style":286},".fluid-text {\n  \u002F* Pre-computed: scales from 1rem at 320px to 2rem at 1440px *\u002F\n  font-size: calc(0.714rem + 1.43vw);\n}\n",[18,52876,52877,52884,52889,52913],{"__ignoreMap":286},[290,52878,52879,52882],{"class":163,"line":292},[290,52880,52881],{"class":303},".fluid-text",[290,52883,450],{"class":295},[290,52885,52886],{"class":163,"line":330},[290,52887,52888],{"class":455},"  \u002F* Pre-computed: scales from 1rem at 320px to 2rem at 1440px *\u002F\n",[290,52890,52891,52893,52895,52897,52899,52902,52904,52906,52909,52911],{"class":163,"line":337},[290,52892,17980],{"class":461},[290,52894,465],{"class":295},[290,52896,3556],{"class":461},[290,52898,484],{"class":295},[290,52900,52901],{"class":461},"0.714",[290,52903,801],{"class":541},[290,52905,3592],{"class":541},[290,52907,52908],{"class":461}," 1.43",[290,52910,12094],{"class":541},[290,52912,500],{"class":295},[290,52914,52915],{"class":163,"line":364},[290,52916,620],{"class":295},[14,52918,52919],{},[86,52920,52921,52922,52924,52925,52927],{},"Debugging Note: Requires manual clamping at boundaries for production use. The derivation is: slope (per px) = 16\u002F1120 ≈ 0.01429, so the ",[18,52923,12094],{}," coefficient is slope × 100 ≈ 1.43vw; intercept = 16px − (0.01429 × 320px) ≈ 11.43px (≈ 0.714rem). Prefer ",[18,52926,12276],{}," in production to cap the output automatically.",[50,52929,52931],{"id":52930},"production-clamp-implementation","Production clamp() Implementation",[14,52933,52934,52935,52937,52938,52941,52942,12087,52944,69,52946,52948,52949,52951],{},"Replace verbose interpolation with ",[18,52936,39669],{},". This single declaration handles boundary capping natively and removes the breakpoint stair-stepping you would otherwise stack up; the tradeoffs are covered in ",[27,52939,52940],{"href":52679},"clamp() vs media-query typography",". Always use ",[18,52943,801],{},[18,52945,32765],{},[18,52947,51274],{}," bounds to respect OS-level font preferences and WCAG 2.2 compliance. Avoid fractional pixel rounding artifacts in sub-16px scaling by aligning your ",[18,52950,12094],{}," delta to integer pixel increments.",[281,52953,52955],{"className":438,"code":52954,"language":440,"meta":286,"style":286},":root {\n  --fluid-min: 1rem;\n  --fluid-max: 2.5rem;\n  --fluid-preferred: clamp(var(--fluid-min), 1.5rem + 1.2vw, var(--fluid-max));\n}\n\nh1 {\n  font-size: var(--fluid-preferred);\n  line-height: 1.2;\n  letter-spacing: -0.02em;\n}\n",[18,52956,52957,52963,52976,52989,53030,53034,53038,53044,53059,53069,53083],{"__ignoreMap":286},[290,52958,52959,52961],{"class":163,"line":292},[290,52960,1554],{"class":303},[290,52962,450],{"class":295},[290,52964,52965,52968,52970,52972,52974],{"class":163,"line":330},[290,52966,52967],{"class":1561},"  --fluid-min",[290,52969,465],{"class":295},[290,52971,468],{"class":461},[290,52973,801],{"class":541},[290,52975,471],{"class":295},[290,52977,52978,52981,52983,52985,52987],{"class":163,"line":337},[290,52979,52980],{"class":1561},"  --fluid-max",[290,52982,465],{"class":295},[290,52984,10258],{"class":461},[290,52986,801],{"class":541},[290,52988,471],{"class":295},[290,52990,52991,52994,52996,52998,53000,53002,53004,53007,53009,53011,53013,53015,53017,53019,53021,53023,53025,53028],{"class":163,"line":364},[290,52992,52993],{"class":1561},"  --fluid-preferred",[290,52995,465],{"class":295},[290,52997,11555],{"class":461},[290,52999,484],{"class":295},[290,53001,1622],{"class":461},[290,53003,484],{"class":295},[290,53005,53006],{"class":1561},"--fluid-min",[290,53008,24264],{"class":295},[290,53010,168],{"class":461},[290,53012,801],{"class":541},[290,53014,3592],{"class":1561},[290,53016,12128],{"class":461},[290,53018,12094],{"class":541},[290,53020,569],{"class":295},[290,53022,1622],{"class":461},[290,53024,484],{"class":295},[290,53026,53027],{"class":1561},"--fluid-max",[290,53029,11616],{"class":295},[290,53031,53032],{"class":163,"line":386},[290,53033,620],{"class":295},[290,53035,53036],{"class":163,"line":408},[290,53037,334],{"emptyLinePlaceholder":333},[290,53039,53040,53042],{"class":163,"line":428},[290,53041,10],{"class":299},[290,53043,450],{"class":295},[290,53045,53046,53048,53050,53052,53054,53057],{"class":163,"line":517},[290,53047,17980],{"class":461},[290,53049,465],{"class":295},[290,53051,1622],{"class":461},[290,53053,484],{"class":295},[290,53055,53056],{"class":1561},"--fluid-preferred",[290,53058,500],{"class":295},[290,53060,53061,53063,53065,53067],{"class":163,"line":523},[290,53062,17994],{"class":461},[290,53064,465],{"class":295},[290,53066,1789],{"class":461},[290,53068,471],{"class":295},[290,53070,53071,53074,53076,53079,53081],{"class":163,"line":532},[290,53072,53073],{"class":461},"  letter-spacing",[290,53075,465],{"class":295},[290,53077,53078],{"class":461},"-0.02",[290,53080,86],{"class":541},[290,53082,471],{"class":295},[290,53084,53085],{"class":163,"line":551},[290,53086,620],{"class":295},[14,53088,53089,53090,53092,53093],{},"The same interpolation technique extends beyond type: a ",[27,53091,11318],{"href":11317}," applies identical bounds to margins, gaps, and padding so vertical rhythm scales in lockstep with headings.\n",[86,53094,53095,53096,53098,53099,53102,53103,53105],{},"Debugging Tip: If text appears blurry at specific breakpoints, verify that your ",[18,53097,12094],{}," multiplier doesn't produce fractional sub-pixel values. Apply ",[18,53100,53101],{},"-webkit-font-smoothing: antialiased;"," (macOS\u002FiOS) and ensure ",[18,53104,50124],{}," is unitless to prevent baseline drift.",[50,53107,53109],{"id":53108},"container-aware-typography-scoping","Container-Aware Typography Scoping",[14,53111,53112,53113,3041,53115,53117,53118,53120],{},"Viewport-relative scaling breaks down in modular layouts. Transition to container query units (",[18,53114,38792],{},[18,53116,11404],{},") to isolate typographic behavior within nested components. Define ",[18,53119,25409],{}," on the parent wrapper to establish a local coordinate system.",[281,53122,53124],{"className":438,"code":53123,"language":440,"meta":286,"style":286},".card-component {\n  container-type: inline-size;\n  container-name: card;\n}\n\n@container card (min-width: 200px) {\n  .card-title {\n    font-size: clamp(1rem, 1.5rem + 5cqw, 2rem);\n  }\n}\n",[18,53125,53126,53132,53138,53144,53148,53152,53159,53166,53200,53204],{"__ignoreMap":286},[290,53127,53128,53130],{"class":163,"line":292},[290,53129,41049],{"class":303},[290,53131,450],{"class":295},[290,53133,53134,53136],{"class":163,"line":330},[290,53135,11509],{"class":461},[290,53137,25565],{"class":295},[290,53139,53140,53142],{"class":163,"line":337},[290,53141,25575],{"class":461},[290,53143,37983],{"class":295},[290,53145,53146],{"class":163,"line":364},[290,53147,620],{"class":295},[290,53149,53150],{"class":163,"line":386},[290,53151,334],{"emptyLinePlaceholder":333},[290,53153,53154,53156],{"class":163,"line":408},[290,53155,12001],{"class":541},[290,53157,53158],{"class":295}," card (min-width: 200px) {\n",[290,53160,53161,53164],{"class":163,"line":428},[290,53162,53163],{"class":303},"  .card-title",[290,53165,450],{"class":295},[290,53167,53168,53170,53172,53174,53176,53178,53180,53182,53184,53186,53188,53190,53192,53194,53196,53198],{"class":163,"line":517},[290,53169,39218],{"class":461},[290,53171,465],{"class":295},[290,53173,11555],{"class":461},[290,53175,484],{"class":295},[290,53177,468],{"class":461},[290,53179,801],{"class":541},[290,53181,569],{"class":295},[290,53183,168],{"class":461},[290,53185,801],{"class":541},[290,53187,3592],{"class":1561},[290,53189,51687],{"class":461},[290,53191,38792],{"class":541},[290,53193,569],{"class":295},[290,53195,194],{"class":461},[290,53197,801],{"class":541},[290,53199,500],{"class":295},[290,53201,53202],{"class":163,"line":523},[290,53203,771],{"class":295},[290,53205,53206],{"class":163,"line":532},[290,53207,620],{"class":295},[14,53209,53210],{},[86,53211,53212,53213,53215,53216,53218,53219,2351,53221,53223],{},"Architecture Note: ",[18,53214,38792],{}," units calculate against the nearest ancestor with ",[18,53217,24401],{},". Avoid applying ",[18,53220,38658],{},[18,53222,17909],{}," to the container itself, as it disables intrinsic sizing and breaks unit resolution. Performance impact is negligible; modern engines batch container recalculations during layout passes.",[50,53225,53227],{"id":53226},"fallback-architecture-performance-tuning","Fallback Architecture & Performance Tuning",[14,53229,53230,53231,53233,53234,53237],{},"Implement progressive enhancement using ",[18,53232,2086],{}," to guarantee baseline readability on pre-2020 engines. Pair this with ",[18,53235,53236],{},"font-display: swap"," to eliminate FOIT and prevent CLS during web font loading.",[281,53239,53241],{"className":438,"code":53240,"language":440,"meta":286,"style":286},".fluid-heading {\n  font-size: 1.25rem; \u002F* Static baseline *\u002F\n}\n\n@supports (font-size: clamp(1rem, 1.5rem, 2rem)) {\n  .fluid-heading {\n    font-size: clamp(1rem, 1.5rem + 1vw, 2rem);\n  }\n}\n",[18,53242,53243,53250,53265,53269,53273,53305,53312,53346,53350],{"__ignoreMap":286},[290,53244,53245,53248],{"class":163,"line":292},[290,53246,53247],{"class":303},".fluid-heading",[290,53249,450],{"class":295},[290,53251,53252,53254,53256,53258,53260,53262],{"class":163,"line":330},[290,53253,17980],{"class":461},[290,53255,465],{"class":295},[290,53257,35120],{"class":461},[290,53259,801],{"class":541},[290,53261,828],{"class":295},[290,53263,53264],{"class":455},"\u002F* Static baseline *\u002F\n",[290,53266,53267],{"class":163,"line":337},[290,53268,620],{"class":295},[290,53270,53271],{"class":163,"line":364},[290,53272,334],{"emptyLinePlaceholder":333},[290,53274,53275,53277,53279,53281,53283,53285,53287,53289,53291,53293,53295,53297,53299,53301,53303],{"class":163,"line":386},[290,53276,2086],{"class":541},[290,53278,3595],{"class":295},[290,53280,44978],{"class":461},[290,53282,465],{"class":295},[290,53284,11555],{"class":461},[290,53286,484],{"class":295},[290,53288,468],{"class":461},[290,53290,801],{"class":541},[290,53292,569],{"class":295},[290,53294,168],{"class":461},[290,53296,801],{"class":541},[290,53298,569],{"class":295},[290,53300,194],{"class":461},[290,53302,801],{"class":541},[290,53304,14796],{"class":295},[290,53306,53307,53310],{"class":163,"line":408},[290,53308,53309],{"class":303},"  .fluid-heading",[290,53311,450],{"class":295},[290,53313,53314,53316,53318,53320,53322,53324,53326,53328,53330,53332,53334,53336,53338,53340,53342,53344],{"class":163,"line":428},[290,53315,39218],{"class":461},[290,53317,465],{"class":295},[290,53319,11555],{"class":461},[290,53321,484],{"class":295},[290,53323,468],{"class":461},[290,53325,801],{"class":541},[290,53327,569],{"class":295},[290,53329,168],{"class":461},[290,53331,801],{"class":541},[290,53333,3592],{"class":1561},[290,53335,804],{"class":461},[290,53337,12094],{"class":541},[290,53339,569],{"class":295},[290,53341,194],{"class":461},[290,53343,801],{"class":541},[290,53345,500],{"class":295},[290,53347,53348],{"class":163,"line":517},[290,53349,771],{"class":295},[290,53351,53352],{"class":163,"line":523},[290,53353,620],{"class":295},[14,53355,53356],{},[86,53357,53358],{},"Performance Checklist:",[1393,53360,53361,53373,53383],{},[1396,53362,53363,2318,53366,53369,53370,53372],{},[62,53364,53365],{},"Minimize Repaints:",[18,53367,53368],{},"content-visibility: auto"," on off-screen text blocks. Avoid ",[18,53371,3797],{}," on typography unless animating transforms.",[1396,53374,53375,53378,53379,53382],{},[62,53376,53377],{},"LCP Optimization:"," Preload critical font files (",[18,53380,53381],{},"\u003Clink rel=\"preload\" as=\"font\" crossorigin>","). Ensure the fallback font closely matches the web font's metrics.",[1396,53384,53385,53388,53389,53392],{},[62,53386,53387],{},"TTI Impact:"," CSS math functions execute at the compositor level, bypassing the main thread entirely. They outperform JS ",[18,53390,53391],{},"resize"," listeners by avoiding forced synchronous layouts.",[50,53394,34625],{"id":34624},[2250,53396,53397,53411],{},[2253,53398,53399],{},[2256,53400,53401,53403,53405,53407,53409],{},[2259,53402,3737],{},[2259,53404,2276],{},[2259,53406,2287],{},[2259,53408,2297],{},[2259,53410,2308],{},[2269,53412,53413,53427,53443,53459],{},[2256,53414,53415,53419,53421,53423,53425],{},[2274,53416,53417],{},[18,53418,12276],{},[2274,53420,3767],{},[2274,53422,8481],{},[2274,53424,8478],{},[2274,53426,3767],{},[2256,53428,53429,53433,53436,53439,53441],{},[2274,53430,53431],{},[18,53432,3693],{},[2274,53434,53435],{},"All (IE11 partial)",[2274,53437,53438],{},"All",[2274,53440,53438],{},[2274,53442,53438],{},[2256,53444,53445,53451,53453,53455,53457],{},[2274,53446,53447,3041,53449],{},[18,53448,38792],{},[18,53450,11404],{},[2274,53452,29200],{},[2274,53454,37592],{},[2274,53456,40677],{},[2274,53458,29200],{},[2256,53460,53461,53465,53467,53469,53471],{},[2274,53462,53463],{},[18,53464,12001],{},[2274,53466,29200],{},[2274,53468,37592],{},[2274,53470,40677],{},[2274,53472,29200],{},[14,53474,53475,2318,53477,53479,53480,53482],{},[86,53476,13313],{},[18,53478,2086],{}," with static ",[18,53481,801],{}," fallbacks. Polyfills are not recommended due to parsing overhead and layout thrashing risks.",[50,53484,40706],{"id":40705},[14,53486,53487,53490,53491,53493,53494,53496,53497,53500,53501,42],{},[62,53488,53489],{},"Issue:"," Extreme font scaling on ultrawide monitors (>2560px)\n",[62,53492,37664],{}," Cap scaling using ",[18,53495,51111],{}," on the preferred value: ",[18,53498,53499],{},"clamp(1rem, min(2.5rem, 1.5rem + 1.2vw), 2.5rem)",", or enforce a hard ceiling via ",[18,53502,53503],{},"@media (min-width: 1440px) { font-size: 2.5rem; }",[14,53505,53506,53508,53509,2318,53511,12087,53513,3041,53515,53517,53518,53520,53521,53523,53524,32863,53527,53529],{},[62,53507,53489],{}," Fractional pixel rounding causing blurry text rendering\n",[62,53510,37664],{},[18,53512,801],{},[18,53514,32765],{},[18,53516,51274],{}," bounds. Align ",[18,53519,12094],{}," deltas to integer increments. Apply ",[18,53522,53101],{}," (macOS\u002FiOS) for sub-pixel clarity. Note that ",[18,53525,53526],{},"font-smoothing",[18,53528,3834],{}," prefix is not a standard CSS property and has no effect.",[14,53531,53532,53534,53535,3041,53537,53539,53540,53542,53543,53545,53546,3041,53548,53550,53551,53553],{},[62,53533,53489],{}," Container query units (",[18,53536,38792],{},[18,53538,11404],{},") not scaling inside flex\u002Fgrid children\n",[62,53541,37664],{}," Verify the parent has explicit ",[18,53544,25409],{},". Ensure the container isn't constrained by ",[18,53547,38658],{},[18,53549,17909],{},". Check inheritance chains for conflicting ",[18,53552,26072],{}," declarations.",[14,53555,53556,53558,53559,53561,53562,12087,53564,53566,53567,2351,53569,53571],{},[62,53557,53489],{}," Accessibility zoom (200%+) breaking fluid calculations\n",[62,53560,37664],{}," Never use ",[18,53563,674],{},[18,53565,12276],{}," bounds. Always use ",[18,53568,801],{},[18,53570,86],{}," to respect browser zoom multipliers and satisfy WCAG 1.4.10 reflow requirements.",[50,53573,1316],{"id":1315},[14,53575,53576,53579,53580,3041,53582,53584,53585,69,53587,53589,53590,53592,53593,53595],{},[62,53577,53578],{},"Can fluid typography without JavaScript respect user accessibility zoom?","\nYes, provided you use relative units (",[18,53581,801],{},[18,53583,86],{},") for the ",[18,53586,32765],{},[18,53588,51274],{}," bounds in ",[18,53591,12276],{},". Avoiding ",[18,53594,674],{}," ensures the browser's native zoom multiplier applies correctly to the calculated values.",[14,53597,53598,41867,53601,69,53603,53605,53606,53608],{},[62,53599,53600],{},"How do I prevent layout shift (CLS) when using fluid type?",[18,53602,50124],{},[18,53604,37680],{}," constraints on text containers, use ",[18,53607,53236],{},", and preload critical web fonts. CSS-only fluid scaling eliminates JS execution delays that typically trigger CLS.",[14,53610,53611,5735,53620,53622,53623,53625],{},[62,53612,53613,53614,53616,53617,53619],{},"Should I use viewport units (",[18,53615,12094],{},") or container units (",[18,53618,38792],{},") for component typography?",[18,53621,12094],{}," for global page-level type (headings, hero text). Use ",[18,53624,38792],{}," for isolated UI components (cards, modals, sidebars) to ensure typography scales predictably regardless of viewport width.",[14,53627,53628,53636],{},[62,53629,53630,53631,69,53633,53635],{},"What is the performance impact of ",[18,53632,3693],{},[18,53634,12276],{}," on rendering?","\nNegligible. Modern browsers optimize CSS math functions at the compositor level. They execute faster than JS resize listeners and avoid forced synchronous layouts, improving both FPS and TTI.",[50,53638,1391],{"id":1390},[1393,53640,53641,53646,53652,53657],{},[1396,53642,53643,53645],{},[27,53644,52680],{"href":52679}," — when continuous scaling beats stacked breakpoints.",[1396,53647,53648,53651],{},[27,53649,53650],{"href":11317},"Fluid Space Scale with clamp()"," — extend the same math to spacing tokens.",[1396,53653,53654,53656],{},[27,53655,12361],{"href":11312}," — the parent guide for clamp-based sizing.",[1396,53658,53659,53661],{},[27,53660,8757],{"href":8756}," — keep compositor-level work fast across type and motion.",[1430,53663,25260],{},{"title":286,"searchDepth":330,"depth":330,"links":53665},[53666,53667,53668,53669,53670,53671,53672,53673],{"id":52808,"depth":330,"text":52809},{"id":52930,"depth":330,"text":52931},{"id":53108,"depth":330,"text":53109},{"id":53226,"depth":330,"text":53227},{"id":34624,"depth":330,"text":34625},{"id":40705,"depth":330,"text":40706},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Fluid typography without JavaScript: CSS clamp(), viewport units, and math functions that eliminate CLS and achieve precise scaling across all breakpoints.",{"seoTitle":53676,"datePublished":1447,"dateModified":1447,"faq":53677},"Fluid Typography Without JavaScript: CSS",[53678,53680,53682,53685],{"q":53578,"a":53679},"Yes, provided you use relative units (rem or em) for the min and max bounds in clamp(). Avoiding px ensures the browser's native zoom multiplier applies correctly to the calculated values.",{"q":53600,"a":53681},"Define explicit line-height and min-height constraints on text containers, use font-display: swap, and preload critical web fonts. CSS-only fluid scaling eliminates the JS execution delays that typically trigger CLS.",{"q":53683,"a":53684},"Should I use viewport units (vw) or container units (cqw) for component typography?","Use vw for global page-level type such as headings and hero text. Use cqw for isolated UI components like cards, modals, and sidebars so typography scales predictably regardless of viewport width.",{"q":53686,"a":53687},"What is the performance impact of calc() and clamp() on rendering?","Negligible. Modern browsers optimize CSS math functions at the compositor level. They execute faster than JS resize listeners and avoid forced synchronous layouts, improving both FPS and TTI.","\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-typography-without-javascript",{"title":52727,"description":53674},"mastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Ffluid-typography-without-javascript\u002Findex","GuDCfFRgYpe7Mu2Zx5LB1E-gXAlLgmR_Vu8mtdTveQE",{"id":53693,"title":53694,"body":53695,"description":54803,"extension":1444,"meta":54804,"navigation":333,"path":54819,"seo":54820,"stem":54821,"__hash__":54822},"content\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Findex.md","Fluid Typography with clamp(): A Practical Guide for Modern CSS",{"type":7,"value":53696,"toc":54792},[53697,53700,53709,53714,53728,53767,53769,53775,53795,53822,53941,53954,53956,53960,53969,53975,54202,54208,54210,54214,54226,54312,54317,54339,54345,54347,54351,54354,54398,54403,54454,54466,54468,54472,54552,54557,54608,54610,54612,54629,54655,54669,54702,54704,54708,54757,54759,54761,54789],[10,53698,53694],{"id":53699},"fluid-typography-with-clamp-a-practical-guide-for-modern-css",[14,53701,53702,53703,53705,53706,53708],{},"Fluid Typography with ",[18,53704,12276],{}," enables seamless, viewport-responsive font scaling without JavaScript. By leveraging native CSS math functions, developers can create accessible, performant type systems that adapt to any screen size. This guide bridges foundational responsive design principles with advanced implementation patterns, building upon architectural concepts from ",[27,53707,11296],{"href":5777}," to deliver production-ready CSS architectures.",[14,53710,53711],{},[62,53712,53713],{},"Key Implementation Takeaways:",[1393,53715,53716,53719,53722,53725],{},[1396,53717,53718],{},"Eliminates breakpoint-heavy media queries for type scaling",[1396,53720,53721],{},"Uses native CSS math functions for predictable, hardware-accelerated interpolation",[1396,53723,53724],{},"Maintains accessibility and readability across extreme viewports",[1396,53726,53727],{},"Integrates seamlessly with modern component architectures and design tokens",[133,53729,140,53731,140,53734,140,53737,140,53739,140,53741,140,53743,140,53746,140,53749,140,53752,140,53755,140,53758,140,53762,140,53765],{"viewBox":4133,"role":136,"ariaLabel":53730,"xmlns":138,"style":139},"How clamp() interpolates between a minimum and maximum font size as the viewport grows",[142,53732,53733],{},"clamp() interpolation curve",[146,53735,53736],{},"A line that is flat at the minimum, slopes up across the preferred range, then flattens at the maximum.",[150,53738,39669],{"x":152,"y":153,"style":1781},[163,53740],{"x1":9105,"y1":3112,"x2":10284,"y2":3112,"stroke":167,"strokeWidth":168,"opacity":798},[163,53742],{"x1":9105,"y1":3112,"x2":9105,"y2":1786,"stroke":167,"strokeWidth":168,"opacity":798},[10255,53744],{"points":53745,"fill":72,"stroke":177,"strokeWidth":1579},"80,210 260,210 480,110 660,110",[163,53747],{"x1":12480,"y1":1786,"x2":12480,"y2":3112,"stroke":167,"strokeWidth":468,"strokeDashArray":53748,"opacity":169},[249,249],[163,53750],{"x1":19660,"y1":1786,"x2":19660,"y2":3112,"stroke":167,"strokeWidth":468,"strokeDashArray":53751,"opacity":169},[249,249],[150,53753,53754],{"x":1822,"y":174,"style":4181},"min (clamped)",[150,53756,53757],{"x":52801,"y":1787,"style":12455},"preferred (vw slope)",[150,53759,53761],{"x":53760,"y":165,"style":4181},"570","max (clamped)",[150,53763,52784],{"x":52801,"y":53764,"style":1808},"278",[150,53766,41019],{"x":1831,"y":8912,"style":1808},[47,53768],{},[50,53770,5072,53772,53774],{"id":53771},"understanding-the-clamp-function",[18,53773,12276],{}," Function",[14,53776,1517,53777,53779,53780,53783,53784,53787,53788,2351,53791,53794],{},[18,53778,12276],{}," function accepts three parameters: ",[18,53781,53782],{},"clamp(minimum, preferred, maximum)",". The browser evaluates the ",[18,53785,53786],{},"preferred"," value first. If it falls outside the defined bounds, the engine automatically snaps to the nearest ",[18,53789,53790],{},"minimum",[18,53792,53793],{},"maximum"," value. This creates a mathematically precise interpolation curve that replaces traditional breakpoint stacks.",[14,53796,53797,53798,24264,53800,53802,53803,53805,53806,53809,53810,53812,53813,53815,53816,53818,53819,42],{},"When paired with viewport units (",[18,53799,12094],{},[18,53801,12276],{}," generates a fluid slope. The ",[18,53804,53786],{}," value acts as a linear equation (",[18,53807,53808],{},"y = mx + b","), where ",[18,53811,52381],{}," is the viewport percentage and ",[18,53814,52818],{}," is the base offset. This approach outperforms legacy ",[18,53817,3693],{}," + media query combinations because it requires zero layout thrashing, executes entirely in the rendering engine, and guarantees smooth scaling between defined thresholds. For a side-by-side breakdown of why the math function wins on maintainability, see ",[27,53820,53821],{"href":52679},"clamp() vs media query typography",[281,53823,53825],{"className":438,"code":53824,"language":440,"meta":286,"style":286},"\u002F* Basic Fluid Heading *\u002F\n:root {\n  --fluid-min: 1.5rem;\n  --fluid-max: 3rem;\n  \u002F* Scales smoothly between 24px and 48px based on 5% of viewport width *\u002F\n  --fluid-preferred: clamp(var(--fluid-min), 5vw, var(--fluid-max));\n}\n\nh1 {\n  font-size: var(--fluid-preferred);\n  line-height: 1.2;\n}\n",[18,53826,53827,53832,53838,53850,53862,53867,53899,53903,53907,53913,53927,53937],{"__ignoreMap":286},[290,53828,53829],{"class":163,"line":292},[290,53830,53831],{"class":455},"\u002F* Basic Fluid Heading *\u002F\n",[290,53833,53834,53836],{"class":163,"line":330},[290,53835,1554],{"class":303},[290,53837,450],{"class":295},[290,53839,53840,53842,53844,53846,53848],{"class":163,"line":337},[290,53841,52967],{"class":1561},[290,53843,465],{"class":295},[290,53845,168],{"class":461},[290,53847,801],{"class":541},[290,53849,471],{"class":295},[290,53851,53852,53854,53856,53858,53860],{"class":163,"line":364},[290,53853,52980],{"class":1561},[290,53855,465],{"class":295},[290,53857,1579],{"class":461},[290,53859,801],{"class":541},[290,53861,471],{"class":295},[290,53863,53864],{"class":163,"line":386},[290,53865,53866],{"class":455},"  \u002F* Scales smoothly between 24px and 48px based on 5% of viewport width *\u002F\n",[290,53868,53869,53871,53873,53875,53877,53879,53881,53883,53885,53887,53889,53891,53893,53895,53897],{"class":163,"line":408},[290,53870,52993],{"class":1561},[290,53872,465],{"class":295},[290,53874,11555],{"class":461},[290,53876,484],{"class":295},[290,53878,1622],{"class":461},[290,53880,484],{"class":295},[290,53882,53006],{"class":1561},[290,53884,24264],{"class":295},[290,53886,5911],{"class":461},[290,53888,12094],{"class":541},[290,53890,569],{"class":295},[290,53892,1622],{"class":461},[290,53894,484],{"class":295},[290,53896,53027],{"class":1561},[290,53898,11616],{"class":295},[290,53900,53901],{"class":163,"line":428},[290,53902,620],{"class":295},[290,53904,53905],{"class":163,"line":517},[290,53906,334],{"emptyLinePlaceholder":333},[290,53908,53909,53911],{"class":163,"line":523},[290,53910,10],{"class":299},[290,53912,450],{"class":295},[290,53914,53915,53917,53919,53921,53923,53925],{"class":163,"line":532},[290,53916,17980],{"class":461},[290,53918,465],{"class":295},[290,53920,1622],{"class":461},[290,53922,484],{"class":295},[290,53924,53056],{"class":1561},[290,53926,500],{"class":295},[290,53928,53929,53931,53933,53935],{"class":163,"line":551},[290,53930,17994],{"class":461},[290,53932,465],{"class":295},[290,53934,1789],{"class":461},[290,53936,471],{"class":295},[290,53938,53939],{"class":163,"line":586},[290,53940,620],{"class":295},[14,53942,53943,53945,53946,2351,53948,53950,53951,53953],{},[62,53944,12661],{}," Always define ",[18,53947,801],{},[18,53949,86],{}," for bounds to respect user agent defaults and OS-level font scaling. Hardcoding ",[18,53952,674],{}," in the min\u002Fmax values breaks accessibility compliance.",[47,53955],{},[50,53957,53959],{"id":53958},"implementing-fluid-type-scales","Implementing Fluid Type Scales",[14,53961,53962,53963,53965,53966],{},"A modular type scale requires systematic ratio calculations. Instead of guessing ",[18,53964,12094],{}," values, apply the slope-intercept formula:\n",[18,53967,53968],{},"slope = (max_size - min_size) \u002F (max_viewport - min_viewport) * 100",[14,53970,53971,53972,53974],{},"This yields the exact viewport percentage needed for linear interpolation between your target breakpoints (e.g., 320px to 1440px). Centralize these calculations using CSS custom properties to maintain a single source of truth across your design system. The same slope math drives spacing as well as type, so once your scale is in place you can extend it into a ",[27,53973,11318],{"href":11317}," for margins, gaps, and padding.",[281,53976,53978],{"className":438,"code":53977,"language":440,"meta":286,"style":286},"\u002F* Modular Type Scale with Custom Properties *\u002F\n:root {\n  --scale-ratio: 1.25;\n  \u002F* Pre-calculated clamp() steps for consistent typographic rhythm *\u002F\n  --step-0: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);\n  --step-1: clamp(1.25rem, 1.1rem + 0.75vw, 1.5625rem);\n  --step-2: clamp(1.5625rem, 1.3rem + 1.1vw, 1.953rem);\n}\n\n.text-sm {\n  font-size: var(--step-0);\n}\n.text-lg {\n  font-size: var(--step-1);\n}\n.text-xl {\n  font-size: var(--step-2);\n}\n",[18,53979,53980,53985,53991,54002,54007,54042,54078,54116,54120,54124,54131,54146,54150,54157,54172,54176,54183,54198],{"__ignoreMap":286},[290,53981,53982],{"class":163,"line":292},[290,53983,53984],{"class":455},"\u002F* Modular Type Scale with Custom Properties *\u002F\n",[290,53986,53987,53989],{"class":163,"line":330},[290,53988,1554],{"class":303},[290,53990,450],{"class":295},[290,53992,53993,53996,53998,54000],{"class":163,"line":337},[290,53994,53995],{"class":1561},"  --scale-ratio",[290,53997,465],{"class":295},[290,53999,35120],{"class":461},[290,54001,471],{"class":295},[290,54003,54004],{"class":163,"line":364},[290,54005,54006],{"class":455},"  \u002F* Pre-calculated clamp() steps for consistent typographic rhythm *\u002F\n",[290,54008,54009,54012,54014,54016,54018,54020,54022,54024,54026,54028,54030,54032,54034,54036,54038,54040],{"class":163,"line":386},[290,54010,54011],{"class":1561},"  --step-0",[290,54013,465],{"class":295},[290,54015,11555],{"class":461},[290,54017,484],{"class":295},[290,54019,468],{"class":461},[290,54021,801],{"class":541},[290,54023,569],{"class":295},[290,54025,3589],{"class":461},[290,54027,801],{"class":541},[290,54029,3592],{"class":1561},[290,54031,41234],{"class":461},[290,54033,12094],{"class":541},[290,54035,569],{"class":295},[290,54037,35120],{"class":461},[290,54039,801],{"class":541},[290,54041,500],{"class":295},[290,54043,54044,54047,54049,54051,54053,54055,54057,54059,54061,54063,54065,54067,54069,54071,54074,54076],{"class":163,"line":408},[290,54045,54046],{"class":1561},"  --step-1",[290,54048,465],{"class":295},[290,54050,11555],{"class":461},[290,54052,484],{"class":295},[290,54054,35120],{"class":461},[290,54056,801],{"class":541},[290,54058,569],{"class":295},[290,54060,39227],{"class":461},[290,54062,801],{"class":541},[290,54064,3592],{"class":1561},[290,54066,50200],{"class":461},[290,54068,12094],{"class":541},[290,54070,569],{"class":295},[290,54072,54073],{"class":461},"1.5625",[290,54075,801],{"class":541},[290,54077,500],{"class":295},[290,54079,54080,54083,54085,54087,54089,54091,54093,54095,54098,54100,54102,54105,54107,54109,54112,54114],{"class":163,"line":428},[290,54081,54082],{"class":1561},"  --step-2",[290,54084,465],{"class":295},[290,54086,11555],{"class":461},[290,54088,484],{"class":295},[290,54090,54073],{"class":461},[290,54092,801],{"class":541},[290,54094,569],{"class":295},[290,54096,54097],{"class":461},"1.3",[290,54099,801],{"class":541},[290,54101,3592],{"class":1561},[290,54103,54104],{"class":461}," 1.1",[290,54106,12094],{"class":541},[290,54108,569],{"class":295},[290,54110,54111],{"class":461},"1.953",[290,54113,801],{"class":541},[290,54115,500],{"class":295},[290,54117,54118],{"class":163,"line":517},[290,54119,620],{"class":295},[290,54121,54122],{"class":163,"line":523},[290,54123,334],{"emptyLinePlaceholder":333},[290,54125,54126,54129],{"class":163,"line":532},[290,54127,54128],{"class":303},".text-sm",[290,54130,450],{"class":295},[290,54132,54133,54135,54137,54139,54141,54144],{"class":163,"line":551},[290,54134,17980],{"class":461},[290,54136,465],{"class":295},[290,54138,1622],{"class":461},[290,54140,484],{"class":295},[290,54142,54143],{"class":1561},"--step-0",[290,54145,500],{"class":295},[290,54147,54148],{"class":163,"line":586},[290,54149,620],{"class":295},[290,54151,54152,54155],{"class":163,"line":602},[290,54153,54154],{"class":303},".text-lg",[290,54156,450],{"class":295},[290,54158,54159,54161,54163,54165,54167,54170],{"class":163,"line":617},[290,54160,17980],{"class":461},[290,54162,465],{"class":295},[290,54164,1622],{"class":461},[290,54166,484],{"class":295},[290,54168,54169],{"class":1561},"--step-1",[290,54171,500],{"class":295},[290,54173,54174],{"class":163,"line":623},[290,54175,620],{"class":295},[290,54177,54178,54181],{"class":163,"line":628},[290,54179,54180],{"class":303},".text-xl",[290,54182,450],{"class":295},[290,54184,54185,54187,54189,54191,54193,54196],{"class":163,"line":634},[290,54186,17980],{"class":461},[290,54188,465],{"class":295},[290,54190,1622],{"class":461},[290,54192,484],{"class":295},[290,54194,54195],{"class":1561},"--step-2",[290,54197,500],{"class":295},[290,54199,54200],{"class":163,"line":649},[290,54201,620],{"class":295},[14,54203,54204,54205,54207],{},"When integrating fluid scales into reusable UI elements, scope your typography to component boundaries. This prevents global viewport scaling from breaking isolated modules. For detailed implementation strategies on scoped styling, refer to ",[27,54206,38098],{"href":38097}," to ensure type scales adapt predictably within card layouts, modals, and sidebars.",[47,54209],{},[50,54211,54213],{"id":54212},"container-aware-typography","Container-Aware Typography",[14,54215,54216,54217,54219,54220,54222,54223,54225],{},"Viewport-based ",[18,54218,12276],{}," fails in isolated or deeply nested components. A sidebar widget or embedded dashboard panel should scale relative to its own container, not the global browser window. CSS Container Queries solve this by introducing ",[18,54221,38792],{}," (container query width) and ",[18,54224,11404],{}," (container query inline-size) units, which map directly to the parent's dimensions.",[281,54227,54229],{"className":438,"code":54228,"language":440,"meta":286,"style":286},"\u002F* Container-Scoped Fluid Type *\u002F\n.card {\n  container-type: inline-size;\n  container-name: card;\n}\n\n.card__title {\n  \u002F* Adapts to the .card parent width, not the viewport *\u002F\n  font-size: clamp(1rem, 2cqi + 0.5rem, 1.75rem);\n}\n",[18,54230,54231,54236,54242,54248,54254,54258,54262,54269,54274,54308],{"__ignoreMap":286},[290,54232,54233],{"class":163,"line":292},[290,54234,54235],{"class":455},"\u002F* Container-Scoped Fluid Type *\u002F\n",[290,54237,54238,54240],{"class":163,"line":330},[290,54239,11528],{"class":303},[290,54241,450],{"class":295},[290,54243,54244,54246],{"class":163,"line":337},[290,54245,11509],{"class":461},[290,54247,25565],{"class":295},[290,54249,54250,54252],{"class":163,"line":364},[290,54251,25575],{"class":461},[290,54253,37983],{"class":295},[290,54255,54256],{"class":163,"line":386},[290,54257,620],{"class":295},[290,54259,54260],{"class":163,"line":408},[290,54261,334],{"emptyLinePlaceholder":333},[290,54263,54264,54267],{"class":163,"line":428},[290,54265,54266],{"class":303},".card__title",[290,54268,450],{"class":295},[290,54270,54271],{"class":163,"line":517},[290,54272,54273],{"class":455},"  \u002F* Adapts to the .card parent width, not the viewport *\u002F\n",[290,54275,54276,54278,54280,54282,54284,54286,54288,54290,54292,54294,54296,54298,54300,54302,54304,54306],{"class":163,"line":523},[290,54277,17980],{"class":461},[290,54279,465],{"class":295},[290,54281,11555],{"class":461},[290,54283,484],{"class":295},[290,54285,468],{"class":461},[290,54287,801],{"class":541},[290,54289,569],{"class":295},[290,54291,194],{"class":461},[290,54293,11404],{"class":541},[290,54295,3592],{"class":1561},[290,54297,41234],{"class":461},[290,54299,801],{"class":541},[290,54301,569],{"class":295},[290,54303,50057],{"class":461},[290,54305,801],{"class":541},[290,54307,500],{"class":295},[290,54309,54310],{"class":163,"line":532},[290,54311,620],{"class":295},[14,54313,54314],{},[62,54315,54316],{},"Implementation Workflow:",[3017,54318,54319,54325,54333],{},[1396,54320,54321,54322,54324],{},"Declare ",[18,54323,25409],{}," on the parent wrapper.",[1396,54326,1499,54327,54329,54330,54332],{},[18,54328,11404],{}," for horizontal scaling and ",[18,54331,12282],{}," for vertical scaling.",[1396,54334,54335,54336,54338],{},"Combine with ",[18,54337,12276],{}," to enforce readable bounds within the component context.",[14,54340,54341,54342,54344],{},"For teams migrating from viewport-only systems, establishing proper containment contexts is critical. Review ",[27,54343,26284],{"href":25340}," to understand fallback strategies and polyfill requirements for environments lacking native containment support.",[47,54346],{},[50,54348,54350],{"id":54349},"performance-accessibility-considerations","Performance & Accessibility Considerations",[14,54352,54353],{},"Fluid typography introduces specific rendering and accessibility challenges that require proactive mitigation:",[1393,54355,54356,54375,54386],{},[1396,54357,54358,54361,54362,54364,54365,54367,54368,12512,54371,54374],{},[62,54359,54360],{},"Ultra-Wide Displays:"," Unbounded ",[18,54363,12094],{}," values cause text to balloon on 4K+ monitors. Always cap the ",[18,54366,53793],{}," parameter at a readable threshold (typically ",[18,54369,54370],{},"2.5rem",[18,54372,54373],{},"3.5rem"," for body text).",[1396,54376,54377,54380,54381,6941,54383,54385],{},[62,54378,54379],{},"User Font Preferences:"," Browsers allow users to override base font sizes. Using ",[18,54382,801],{},[18,54384,12276],{}," bounds ensures proportional scaling, maintaining WCAG 2.1 AA\u002FAAA compliance.",[1396,54387,54388,54391,54392,54394,54395,54397],{},[62,54389,54390],{},"Cumulative Layout Shift (CLS):"," If fluid type scales dynamically during resource loading, it can trigger layout shifts. Reserve space using ",[18,54393,37680],{}," on text containers or apply ",[18,54396,53368],{}," to defer rendering.",[14,54399,54400],{},[62,54401,54402],{},"DevTools Debugging Workflow:",[3017,54404,54405,54414,54431,54443],{},[1396,54406,54407,54408,1203,54411,3724],{},"Open Chrome\u002FEdge DevTools → Toggle Device Toolbar (",[18,54409,54410],{},"Ctrl+Shift+M",[18,54412,54413],{},"Cmd+Shift+M",[1396,54415,54416,54417,69,54419,54421,54422,54424,54425,54427,54428,54430],{},"Set responsive mode to ",[18,54418,52863],{},[18,54420,52866],{},". Inspect the ",[18,54423,13407],{}," tab to verify ",[18,54426,12276],{}," resolves to exact ",[18,54429,801],{}," values at both extremes.",[1396,54432,54433,54434,54437,54438,3041,54440,54442],{},"Enable the ",[62,54435,54436],{},"Container Query"," panel in DevTools to visualize ",[18,54439,11404],{},[18,54441,38792],{}," interpolation in real-time.",[1396,54444,54445,54446,54449,54450,54453],{},"Test accessibility by forcing ",[18,54447,54448],{},"200%"," zoom or overriding ",[18,54451,54452],{},":root { font-size: 125%; }"," in DevTools. Verify text remains legible and doesn't overflow its container.",[14,54455,54456,54457,54459,54460,54462,54463,54465],{},"For environments requiring strict legacy browser support or environments where CSS math functions are restricted, consult ",[27,54458,16320],{"href":5001}," for progressive enhancement fallbacks using ",[18,54461,874],{}," queries and ",[18,54464,3693],{}," polyfills.",[47,54467],{},[50,54469,54471],{"id":54470},"browser-support-common-issues","Browser Support & Common Issues",[2250,54473,54474,54490],{},[2253,54475,54476],{},[2256,54477,54478,54480,54482,54484,54486,54488],{},[2259,54479,3737],{},[2259,54481,2276],{},[2259,54483,2287],{},[2259,54485,2297],{},[2259,54487,2308],{},[2259,54489,2267],{},[2269,54491,54492,54509,54531],{},[2256,54493,54494,54498,54500,54502,54504,54506],{},[2274,54495,54496],{},[18,54497,12276],{},[2274,54499,3767],{},[2274,54501,8481],{},[2274,54503,8478],{},[2274,54505,3767],{},[2274,54507,54508],{},"Full support across evergreen browsers",[2256,54510,54511,54517,54519,54521,54523,54525],{},[2274,54512,54513,3041,54515],{},[18,54514,11404],{},[18,54516,38792],{},[2274,54518,29200],{},[2274,54520,37592],{},[2274,54522,8465],{},[2274,54524,29200],{},[2274,54526,54527,54528,54530],{},"Requires ",[18,54529,24401],{}," declaration",[2256,54532,54533,54536,54538,54540,54542,54544],{},[2274,54534,54535],{},"Legacy Fallback",[2274,54537,3821],{},[2274,54539,3821],{},[2274,54541,3821],{},[2274,54543,3821],{},[2274,54545,54546,54547,21541,54549,54551],{},"Use fixed ",[18,54548,801],{},[18,54550,874],{}," for IE11\u002Folder Safari",[14,54553,54554],{},[62,54555,54556],{},"Common Pitfalls & Resolutions:",[1393,54558,54559,54568,54583,54599],{},[1396,54560,54561,54564,54565,54567],{},[62,54562,54563],{},"Unreadable text on large screens:"," Caused by missing ",[18,54566,53793],{}," bounds. Always cap interpolation.",[1396,54569,54570,54573,54574,6941,54576,54578,54579,3041,54581,42],{},[62,54571,54572],{},"Broken accessibility scaling:"," Caused by ",[18,54575,674],{},[18,54577,12276],{}," bounds. Switch to ",[18,54580,801],{},[18,54582,86],{},[1396,54584,54585,54588,54589,569,54591,8393,54593,54595,54596,54598],{},[62,54586,54587],{},"Unpredictable curves:"," Caused by mixing ",[18,54590,12094],{},[18,54592,38774],{},[18,54594,11404],{}," in the same ",[18,54597,12276],{}," call. Stick to a single axis unit for the preferred value.",[1396,54600,54601,54604,54605,54607],{},[62,54602,54603],{},"Async layout shifts:"," Caused by fluid type recalculating after image\u002Fcontainer load. Apply explicit container sizing or ",[18,54606,38572],{}," to parent wrappers.",[47,54609],{},[50,54611,1316],{"id":1315},[14,54613,54614,37757,54619,54621,54622,54624,54625,54628],{},[62,54615,40789,54616,54618],{},[18,54617,12276],{}," for line-height and spacing alongside font-size?",[18,54620,12276],{}," accepts any CSS length unit, making it ideal for fluid line-height, padding, and margin. However, line-height should typically remain unitless or use smaller ",[18,54623,12094],{}," values (e.g., ",[18,54626,54627],{},"clamp(1.2, 1.15 + 0.25vw, 1.4)",") to maintain vertical rhythm and prevent excessive leading on wide screens.",[14,54630,54631,54636,54637,69,54639,54641,54642,2351,54644,569,54646,54648,54649,54651,54652,54654],{},[62,54632,5714,54633,54635],{},[18,54634,12276],{}," respect browser zoom and accessibility settings?","\nAbsolutely. When ",[18,54638,32765],{},[18,54640,51274],{}," values are defined in ",[18,54643,801],{},[18,54645,86],{},[18,54647,12276],{}," scales proportionally with the user's base font size, ensuring compliance with WCAG 2.1 guidelines. The browser's zoom engine recalculates the ",[18,54650,801],{}," root before evaluating the ",[18,54653,12276],{}," function.",[14,54656,54657,54662,54663,54665,54666,54668],{},[62,54658,54659,54660,5734],{},"When should I prefer container queries over viewport-based ",[18,54661,12276],{},"\nUse container queries when typography must adapt to the width of a specific component (e.g., a sidebar card or nested widget) rather than the global viewport. Viewport ",[18,54664,12276],{}," is best for page-level headings and body text. Container-aware ",[18,54667,12276],{}," isolates scaling logic to the component's layout context.",[14,54670,54671,54680,54681,54684,54685,54687,54688,54690,54691,80,54693,54690,54695,54698,54699,42],{},[62,54672,54673,54674,54676,54677,54679],{},"How do I calculate the exact ",[18,54675,12094],{}," value for my ",[18,54678,12276],{}," preferred size?","\nUse the slope-intercept formula: ",[18,54682,54683],{},"slope = (max - min) \u002F (max_viewport - min_viewport) * 100",". This yields the precise ",[18,54686,12094],{}," percentage needed for linear interpolation between your breakpoints. For example, scaling from ",[18,54689,51153],{}," at ",[18,54692,52863],{},[18,54694,52859],{},[18,54696,54697],{},"1200px"," requires ",[18,54700,54701],{},"slope = (2-1)\u002F(1200-320)*100 ≈ 0.1136vw",[47,54703],{},[50,54705,54707],{"id":54706},"specification-references-implementation-notes","Specification References & Implementation Notes",[1393,54709,54710,54723,54733,54747],{},[1396,54711,54712,54715,54716,569,54718,8393,54720,54722],{},[62,54713,54714],{},"CSS Values and Units Module Level 4:"," Defines the mathematical evaluation order for ",[18,54717,12276],{},[18,54719,32822],{},[18,54721,51111],{}," functions. The browser resolves nested math functions left-to-right before applying unit conversion.",[1396,54724,54725,54727,54728,69,54730,54732],{},[62,54726,43069],{}," Governs ",[18,54729,24401],{},[18,54731,26072],{}," properties. Inline-size containment triggers layout recalculation only when the parent's inline dimension changes, optimizing paint performance.",[1396,54734,54735,54738,54739,6941,54741,54743,54744,54746],{},[62,54736,54737],{},"Progressive Enhancement Strategy:"," Always ship a baseline ",[18,54740,44978],{},[18,54742,801],{}," before applying ",[18,54745,12276],{},". Modern browsers will override it; legacy engines will gracefully degrade to the static value.",[1396,54748,54749,54752,54753,54756],{},[62,54750,54751],{},"Cross-Browser Testing:"," Validate interpolation curves using the ",[18,54754,54755],{},"@supports (font-size: clamp(1rem, 2vw, 3rem))"," feature query to conditionally apply fluid scales while serving static fallbacks to unsupported agents.",[47,54758],{},[50,54760,1391],{"id":1390},[1393,54762,54763,54768,54773,54779,54784],{},[1396,54764,54765,54767],{},[27,54766,11296],{"href":5777}," — the parent guide framing fluid sizing within responsive architecture.",[1396,54769,54770,54772],{},[27,54771,53821],{"href":52679}," — when the math function beats stacked breakpoints, and when it does not.",[1396,54774,54775,54778],{},[27,54776,54777],{"href":11317},"Fluid space scale with clamp()"," — extend the slope formula to margins, gaps, and padding.",[1396,54780,54781,54783],{},[27,54782,16320],{"href":5001}," — progressive-enhancement fallbacks for engines lacking math functions.",[1396,54785,54786,54788],{},[27,54787,11232],{"href":4900}," — cross-area: reuse the same fluid tokens to scale animation timing.",[1430,54790,54791],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":286,"searchDepth":330,"depth":330,"links":54793},[54794,54796,54797,54798,54799,54800,54801,54802],{"id":53771,"depth":330,"text":54795},"Understanding the clamp() Function",{"id":53958,"depth":330,"text":53959},{"id":54212,"depth":330,"text":54213},{"id":54349,"depth":330,"text":54350},{"id":54470,"depth":330,"text":54471},{"id":1315,"depth":330,"text":1316},{"id":54706,"depth":330,"text":54707},{"id":1390,"depth":330,"text":1391},"Fluid typography with CSS clamp(): viewport-responsive font scaling, accessible type systems, and production-ready math function patterns without JavaScript.",{"seoTitle":54805,"datePublished":1447,"dateModified":1447,"faq":54806},"Fluid Typography with clamp(): Modern CSS",[54807,54810,54813,54816],{"q":54808,"a":54809},"Can I use clamp() for line-height and spacing alongside font-size?","Yes. clamp() accepts any CSS length unit, making it ideal for fluid line-height, padding, and margin. Keep line-height unitless or use small vw values to maintain vertical rhythm on wide screens.",{"q":54811,"a":54812},"Does clamp() respect browser zoom and accessibility settings?","Yes. When min and max are defined in rem or em, clamp() scales proportionally with the user's base font size, ensuring WCAG 2.1 compliance because the zoom engine recalculates the rem root before evaluating clamp().",{"q":54814,"a":54815},"When should I prefer container queries over viewport-based clamp()?","Use container queries when typography must adapt to a specific component's width, such as a sidebar card or nested widget. Viewport clamp() is best for page-level headings and body text.",{"q":54817,"a":54818},"How do I calculate the exact vw value for my clamp() preferred size?","Use the slope-intercept formula: slope = (max - min) \u002F (max_viewport - min_viewport) * 100. This yields the precise vw percentage for linear interpolation between your breakpoints.","\u002Fmastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp",{"title":53694,"description":54803},"mastering-container-queries-responsive-layouts\u002Ffluid-typography-with-clamp\u002Findex","wGMnO0Fd0jDxc_zvmujzS9PpPqkq7facYdYMA-a2s78",{"id":54824,"title":11296,"body":54825,"description":56659,"extension":1444,"meta":56660,"navigation":333,"path":56671,"seo":56672,"stem":56673,"__hash__":56674},"content\u002Fmastering-container-queries-responsive-layouts\u002Findex.md",{"type":7,"value":54826,"toc":56626},[54827,54830,54839,54842,54857,54926,54928,54932,54938,54941,54945,54960,55065,55074,55086,55091,55093,55097,55100,55108,55119,55123,55141,55239,55244,55246,55250,55260,55267,55270,55370,55377,55381,55392,55451,55458,55460,55464,55467,55471,55474,55555,55559,55565,55622,55628,55630,55634,55641,55645,55651,55818,55821,55823,55827,55830,55835,55838,56023,56028,56030,56034,56037,56041,56047,56207,56211,56214,56324,56335,56337,56339,56342,56398,56413,56415,56417,56520,56522,56524,56530,56542,56554,56564,56566,56568,56624],[10,54828,11296],{"id":54829},"mastering-container-queries-responsive-layouts",[14,54831,54832,54833,54835,54836,54838],{},"The evolution of responsive web design has fundamentally shifted from viewport-centric breakpoints to component-scoped adaptability. ",[62,54834,11296],{}," requires a deep understanding of CSS containment, intrinsic sizing, and modern architectural patterns that decouple UI modules from global viewport constraints. This guide bridges the gap between legacy ",[18,54837,874],{}," workflows and production-ready, component-driven responsive design, emphasizing performance optimization, accessibility compliance, and progressive enhancement.",[14,54840,54841],{},"By the end of this technical deep-dive, you will understand:",[1393,54843,54844,54847,54850],{},[1396,54845,54846],{},"How to shift from global viewport breakpoints to local component context",[1396,54848,54849],{},"The performance implications of layout containment and query evaluation",[1396,54851,54852,54853,69,54855],{},"How to integrate container queries with modern CSS features like ",[18,54854,44299],{},[18,54856,12276],{},[133,54858,140,54860,140,54863,140,54866,140,54869,140,54871,140,54874,140,54876,140,54878,140,54880,140,54883,140,54885,140,54887,140,54891,140,54893,140,54896,140,54898,140,54900,140,54903,140,54907,140,54909,140,54913,140,54916,140,54920,140,54923],{"viewBox":135,"role":136,"ariaLabel":54859,"xmlns":138,"style":139},"Comparison of viewport-based media queries and container-based queries",[142,54861,54862],{},"Viewport queries versus container queries",[146,54864,54865],{},"Media queries evaluate the global browser viewport, while container queries evaluate the nearest ancestor that establishes a containment context.",[150,54867,54868],{"x":152,"y":2598,"style":154},"Two evaluation contexts",[171,54870],{"x":158,"y":4172,"width":6740,"height":2601,"rx":5894,"fill":72,"stroke":167,"strokeWidth":168,"opacity":6041},[150,54872,54873],{"x":2602,"y":1823,"style":10244},"@media — global viewport",[171,54875],{"x":1788,"y":173,"width":2637,"height":8879,"rx":5901,"fill":177,"opacity":178},[150,54877,38243],{"x":2602,"y":5909,"style":12455},[171,54879],{"x":1788,"y":26456,"width":2637,"height":4195,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},[150,54881,54882],{"x":2602,"y":6721,"style":1808},"browser window width",[171,54884],{"x":1793,"y":6725,"width":159,"height":32042,"rx":5901,"fill":177,"opacity":199},[171,54886],{"x":2615,"y":6725,"width":159,"height":32042,"rx":5901,"fill":177,"opacity":199},[150,54888,54890],{"x":2602,"y":54889,"style":22612},"328","same component, viewport-driven",[171,54892],{"x":22584,"y":4172,"width":6740,"height":2601,"rx":5894,"fill":72,"stroke":177,"strokeWidth":194},[150,54894,54895],{"x":5887,"y":1823,"style":10244},"@container — parent context",[171,54897],{"x":22590,"y":173,"width":2637,"height":8879,"rx":5901,"fill":177,"opacity":178},[150,54899,43194],{"x":5887,"y":5909,"style":12455},[171,54901],{"x":22590,"y":26456,"width":4158,"height":4195,"rx":176,"fill":72,"stroke":177,"strokeWidth":168,"strokeDashArray":54902},[5911,249],[150,54904,54906],{"x":54905,"y":27969,"style":10252},"475","narrow parent",[171,54908],{"x":26459,"y":11367,"width":5115,"height":4196,"rx":5901,"fill":177,"opacity":199},[171,54910],{"x":54911,"y":26456,"width":4158,"height":4195,"rx":176,"fill":72,"stroke":177,"strokeWidth":168,"strokeDashArray":54912},"552",[5911,249],[150,54914,54915],{"x":27962,"y":27969,"style":10252},"wide parent",[171,54917],{"x":54918,"y":11367,"width":54919,"height":4196,"rx":249,"fill":177,"opacity":199},"566","42",[171,54921],{"x":54922,"y":11367,"width":54919,"height":4196,"rx":249,"fill":177,"opacity":199},"614",[150,54924,54925],{"x":5887,"y":54889,"style":22612},"same component, parent-driven",[47,54927],{},[50,54929,54931],{"id":54930},"the-shift-from-viewport-to-container-context","The Shift from Viewport to Container Context",[14,54933,54934,54935,54937],{},"Traditional responsive architecture relies on ",[18,54936,874],{}," queries that evaluate the browser viewport. While effective for page-level layout shifts, viewport queries break down when reusable UI components are deployed across disparate contexts (e.g., a card component rendered in a narrow sidebar, a hero section, or a multi-column dashboard). The component remains unaware of its immediate parent, forcing developers to write brittle, context-specific overrides.",[14,54939,54940],{},"Container queries solve this by allowing elements to query their nearest ancestor with an established containment context. This isolation enables true component-driven responsive design, where modules adapt to their available space rather than the global viewport.",[2757,54942,54944],{"id":54943},"performance-containment-principles","Performance & Containment Principles",[14,54946,54947,54948,2351,54950,54952,54953,54955,54956,54959],{},"When you declare a container, the browser establishes a containment boundary. This prevents layout, paint, and style calculations from leaking into unrelated DOM branches, significantly reducing main-thread work during resize events. However, containment must be applied strategically. Overusing ",[18,54949,40219],{},[18,54951,40216],{}," on deeply nested elements can trigger excessive layout recalculations. The browser's layout engine optimizes for ",[18,54954,25409],{},", which implicitly applies ",[18,54957,54958],{},"contain: layout size style"," to the queried ancestor.",[281,54961,54963],{"className":438,"code":54962,"language":440,"meta":286,"style":286},"\u002F* Establish a containment context for the parent *\u002F\n.card-wrapper {\n  container-type: inline-size;\n  container-name: card;\n  \u002F* Optional: isolate paint\u002Fstyle for micro-interactions *\u002F\n  contain: layout style;\n}\n\n\u002F* Query the container's inline-size *\u002F\n@container card (min-width: 400px) {\n  .card__media {\n    aspect-ratio: 16 \u002F 9;\n    grid-column: 1 \u002F -1;\n  }\n}\n",[18,54964,54965,54970,54976,54982,54988,54993,55003,55007,55011,55016,55022,55028,55042,55057,55061],{"__ignoreMap":286},[290,54966,54967],{"class":163,"line":292},[290,54968,54969],{"class":455},"\u002F* Establish a containment context for the parent *\u002F\n",[290,54971,54972,54974],{"class":163,"line":330},[290,54973,40135],{"class":303},[290,54975,450],{"class":295},[290,54977,54978,54980],{"class":163,"line":337},[290,54979,11509],{"class":461},[290,54981,25565],{"class":295},[290,54983,54984,54986],{"class":163,"line":364},[290,54985,25575],{"class":461},[290,54987,37983],{"class":295},[290,54989,54990],{"class":163,"line":386},[290,54991,54992],{"class":455},"  \u002F* Optional: isolate paint\u002Fstyle for micro-interactions *\u002F\n",[290,54994,54995,54997,54999,55001],{"class":163,"line":408},[290,54996,40253],{"class":461},[290,54998,41070],{"class":295},[290,55000,1430],{"class":461},[290,55002,471],{"class":295},[290,55004,55005],{"class":163,"line":428},[290,55006,620],{"class":295},[290,55008,55009],{"class":163,"line":517},[290,55010,334],{"emptyLinePlaceholder":333},[290,55012,55013],{"class":163,"line":523},[290,55014,55015],{"class":455},"\u002F* Query the container's inline-size *\u002F\n",[290,55017,55018,55020],{"class":163,"line":532},[290,55019,12001],{"class":541},[290,55021,37998],{"class":295},[290,55023,55024,55026],{"class":163,"line":551},[290,55025,43567],{"class":303},[290,55027,450],{"class":295},[290,55029,55030,55032,55034,55036,55038,55040],{"class":163,"line":586},[290,55031,36346],{"class":461},[290,55033,465],{"class":295},[290,55035,9212],{"class":461},[290,55037,1203],{"class":295},[290,55039,247],{"class":461},[290,55041,471],{"class":295},[290,55043,55044,55047,55049,55051,55053,55055],{"class":163,"line":602},[290,55045,55046],{"class":461},"    grid-column",[290,55048,465],{"class":295},[290,55050,468],{"class":461},[290,55052,1203],{"class":295},[290,55054,11840],{"class":461},[290,55056,471],{"class":295},[290,55058,55059],{"class":163,"line":617},[290,55060,771],{"class":295},[290,55062,55063],{"class":163,"line":623},[290,55064,620],{"class":295},[14,55066,55067],{},[62,55068,55069,55070,1529,55072,723],{},"When to use ",[18,55071,874],{},[18,55073,12001],{},[1393,55075,55076,55081],{},[1396,55077,1499,55078,55080],{},[18,55079,874],{}," for global page structure, navigation breakpoints, and viewport-dependent typography scaling.",[1396,55082,1499,55083,55085],{},[18,55084,12001],{}," for reusable UI modules, data tables, cards, and sidebars that must adapt to their immediate parent's dimensions.",[14,55087,55088,55089,42],{},"For a deeper dive into modular UI composition, explore ",[27,55090,38098],{"href":38097},[47,55092],{},[50,55094,55096],{"id":55095},"core-syntax-declaration-architecture","Core Syntax & Declaration Architecture",[14,55098,55099],{},"Container queries operate through a two-step declaration process: establishing the container context and writing the conditional rules. The syntax is intentionally aligned with media query conventions but introduces container-specific scoping.",[2757,55101,55103,55104,69,55106],{"id":55102},"defining-container-type-and-container-name","Defining ",[18,55105,24401],{},[18,55107,26072],{},[14,55109,1517,55110,55112,55113,55115,55116,55118],{},[18,55111,24401],{}," property dictates which dimensions the browser tracks. ",[18,55114,26044],{}," is the most performant and widely supported, tracking the width in horizontal writing modes. ",[18,55117,26072],{}," is optional but highly recommended for disambiguation when multiple containers exist in the DOM tree.",[2757,55120,55122],{"id":55121},"size-query-syntax-logical-operators","Size Query Syntax & Logical Operators",[14,55124,55125,55126,569,55128,569,55130,569,55132,55134,55135,569,55137,8393,55139,42],{},"Container size queries accept standard comparison operators (",[18,55127,26114],{},[18,55129,41191],{},[18,55131,1748],{},[18,55133,2722],{},") and support logical grouping with ",[18,55136,11985],{},[18,55138,18318],{},[18,55140,10198],{},[281,55142,55144],{"className":438,"code":55143,"language":440,"meta":286,"style":286},"\u002F* Multi-condition query with named container *\u002F\n@container dashboard-panel (min-width: 600px) and (max-width: 900px) {\n  .panel__grid {\n    grid-template-columns: repeat(2, 1fr);\n  }\n}\n\n\u002F* Fallback to unnamed container (nearest ancestor with container-type) *\u002F\n@container (min-width: 320px) {\n  .widget__title {\n    font-size: var(--text-lg);\n  }\n}\n",[18,55145,55146,55151,55158,55165,55185,55189,55193,55197,55202,55209,55216,55231,55235],{"__ignoreMap":286},[290,55147,55148],{"class":163,"line":292},[290,55149,55150],{"class":455},"\u002F* Multi-condition query with named container *\u002F\n",[290,55152,55153,55155],{"class":163,"line":330},[290,55154,12001],{"class":541},[290,55156,55157],{"class":295}," dashboard-panel (min-width: 600px) and (max-width: 900px) {\n",[290,55159,55160,55163],{"class":163,"line":337},[290,55161,55162],{"class":303},"  .panel__grid",[290,55164,450],{"class":295},[290,55166,55167,55169,55171,55173,55175,55177,55179,55181,55183],{"class":163,"line":364},[290,55168,36011],{"class":461},[290,55170,465],{"class":295},[290,55172,44542],{"class":461},[290,55174,484],{"class":295},[290,55176,194],{"class":461},[290,55178,569],{"class":295},[290,55180,468],{"class":461},[290,55182,11964],{"class":541},[290,55184,500],{"class":295},[290,55186,55187],{"class":163,"line":386},[290,55188,771],{"class":295},[290,55190,55191],{"class":163,"line":408},[290,55192,620],{"class":295},[290,55194,55195],{"class":163,"line":428},[290,55196,334],{"emptyLinePlaceholder":333},[290,55198,55199],{"class":163,"line":517},[290,55200,55201],{"class":455},"\u002F* Fallback to unnamed container (nearest ancestor with container-type) *\u002F\n",[290,55203,55204,55206],{"class":163,"line":523},[290,55205,12001],{"class":541},[290,55207,55208],{"class":295}," (min-width: 320px) {\n",[290,55210,55211,55214],{"class":163,"line":532},[290,55212,55213],{"class":303},"  .widget__title",[290,55215,450],{"class":295},[290,55217,55218,55220,55222,55224,55226,55229],{"class":163,"line":551},[290,55219,39218],{"class":461},[290,55221,465],{"class":295},[290,55223,1622],{"class":461},[290,55225,484],{"class":295},[290,55227,55228],{"class":1561},"--text-lg",[290,55230,500],{"class":295},[290,55232,55233],{"class":163,"line":586},[290,55234,771],{"class":295},[290,55236,55237],{"class":163,"line":602},[290,55238,620],{"class":295},[14,55240,55241,55242,42],{},"Understanding the cascade and specificity rules is critical when combining multiple query blocks. For foundational syntax rules and cascade behavior, refer to ",[27,55243,26284],{"href":25340},[47,55245],{},[50,55247,55249],{"id":55248},"fluid-sizing-intrinsic-layout-techniques","Fluid Sizing & Intrinsic Layout Techniques",[14,55251,55252,55253,569,55255,8393,55257,55259],{},"Container queries achieve maximum flexibility when paired with modern CSS sizing functions. Instead of hardcoding pixel values, leverage ",[18,55254,12276],{},[18,55256,32822],{},[18,55258,51111],{}," to create fluid layouts that scale proportionally within the container's bounds.",[2757,55261,55263,55264,55266],{"id":55262},"combining-clamp-with-container-queries","Combining ",[18,55265,12276],{}," with Container Queries",[14,55268,55269],{},"Fluid typography and spacing eliminate the need for granular breakpoint steps. By defining minimum, preferred, and maximum values, components scale smoothly as the container resizes.",[281,55271,55273],{"className":438,"code":55272,"language":440,"meta":286,"style":286},"@container (min-width: 450px) {\n  .card__content {\n    \u002F* Fluid typography: scales between 1rem and 1.5rem based on container width *\u002F\n    font-size: clamp(1rem, 0.8rem + 1.5cqi, 1.5rem);\n    padding: clamp(1rem, 0.5rem + 2cqi, 2rem);\n  }\n}\n",[18,55274,55275,55282,55289,55294,55328,55362,55366],{"__ignoreMap":286},[290,55276,55277,55279],{"class":163,"line":292},[290,55278,12001],{"class":541},[290,55280,55281],{"class":295}," (min-width: 450px) {\n",[290,55283,55284,55287],{"class":163,"line":330},[290,55285,55286],{"class":303},"  .card__content",[290,55288,450],{"class":295},[290,55290,55291],{"class":163,"line":337},[290,55292,55293],{"class":455},"    \u002F* Fluid typography: scales between 1rem and 1.5rem based on container width *\u002F\n",[290,55295,55296,55298,55300,55302,55304,55306,55308,55310,55312,55314,55316,55318,55320,55322,55324,55326],{"class":163,"line":364},[290,55297,39218],{"class":461},[290,55299,465],{"class":295},[290,55301,11555],{"class":461},[290,55303,484],{"class":295},[290,55305,468],{"class":461},[290,55307,801],{"class":541},[290,55309,569],{"class":295},[290,55311,572],{"class":461},[290,55313,801],{"class":541},[290,55315,3592],{"class":1561},[290,55317,10442],{"class":461},[290,55319,11404],{"class":541},[290,55321,569],{"class":295},[290,55323,168],{"class":461},[290,55325,801],{"class":541},[290,55327,500],{"class":295},[290,55329,55330,55332,55334,55336,55338,55340,55342,55344,55346,55348,55350,55352,55354,55356,55358,55360],{"class":163,"line":386},[290,55331,36040],{"class":461},[290,55333,465],{"class":295},[290,55335,11555],{"class":461},[290,55337,484],{"class":295},[290,55339,468],{"class":461},[290,55341,801],{"class":541},[290,55343,569],{"class":295},[290,55345,798],{"class":461},[290,55347,801],{"class":541},[290,55349,3592],{"class":1561},[290,55351,3290],{"class":461},[290,55353,11404],{"class":541},[290,55355,569],{"class":295},[290,55357,194],{"class":461},[290,55359,801],{"class":541},[290,55361,500],{"class":295},[290,55363,55364],{"class":163,"line":408},[290,55365,771],{"class":295},[290,55367,55368],{"class":163,"line":428},[290,55369,620],{"class":295},[14,55371,55372],{},[86,55373,34290,55374,55376],{},[18,55375,11404],{}," (container query inline unit) represents 1% of the container's inline size, enabling mathematically precise responsive scaling.",[2757,55378,55380],{"id":55379},"intrinsic-sizing-for-layout-stability","Intrinsic Sizing for Layout Stability",[14,55382,55383,55384,569,55386,8393,55388,55391],{},"Leveraging ",[18,55385,38658],{},[18,55387,17909],{},[18,55389,55390],{},"fit-content()"," prevents layout thrashing by allowing the browser to calculate optimal dimensions based on content rather than arbitrary constraints. This is particularly effective for text-heavy components and image galleries.",[281,55393,55395],{"className":438,"code":55394,"language":440,"meta":286,"style":286},"@container (min-width: 500px) {\n  .article__meta {\n    \u002F* Prevents overflow while respecting natural content width *\u002F\n    width: fit-content(400px);\n    margin-inline: auto;\n  }\n}\n",[18,55396,55397,55404,55411,55416,55433,55443,55447],{"__ignoreMap":286},[290,55398,55399,55401],{"class":163,"line":292},[290,55400,12001],{"class":541},[290,55402,55403],{"class":295}," (min-width: 500px) {\n",[290,55405,55406,55409],{"class":163,"line":330},[290,55407,55408],{"class":303},"  .article__meta",[290,55410,450],{"class":295},[290,55412,55413],{"class":163,"line":337},[290,55414,55415],{"class":455},"    \u002F* Prevents overflow while respecting natural content width *\u002F\n",[290,55417,55418,55420,55422,55425,55427,55429,55431],{"class":163,"line":364},[290,55419,9090],{"class":461},[290,55421,465],{"class":295},[290,55423,55424],{"class":461},"fit-content",[290,55426,484],{"class":295},[290,55428,2618],{"class":461},[290,55430,674],{"class":541},[290,55432,500],{"class":295},[290,55434,55435,55437,55439,55441],{"class":163,"line":386},[290,55436,44635],{"class":461},[290,55438,465],{"class":295},[290,55440,250],{"class":461},[290,55442,471],{"class":295},[290,55444,55445],{"class":163,"line":408},[290,55446,771],{"class":295},[290,55448,55449],{"class":163,"line":428},[290,55450,620],{"class":295},[14,55452,55453,55454,69,55456,42],{},"To master fluid scaling methodologies, review ",[27,55455,12361],{"href":11312},[27,55457,47441],{"href":47440},[47,55459],{},[50,55461,55463],{"id":55462},"advanced-grid-architectures-nested-layouts","Advanced Grid Architectures & Nested Layouts",[14,55465,55466],{},"CSS Grid and container queries form a powerful architectural duo. Grid handles two-dimensional layout distribution, while container queries dictate when and how that distribution should reflow.",[2757,55468,55470],{"id":55469},"responsive-grid-reflow-without-media-queries","Responsive Grid Reflow Without Media Queries",[14,55472,55473],{},"By querying the container's inline size, you can trigger grid reflows that maintain alignment and spacing consistency across varying parent widths.",[281,55475,55477],{"className":438,"code":55476,"language":440,"meta":286,"style":286},"@container (min-width: 650px) {\n  .feature-grid {\n    display: grid;\n    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n    gap: var(--space-md);\n  }\n}\n",[18,55478,55479,55486,55493,55503,55533,55547,55551],{"__ignoreMap":286},[290,55480,55481,55483],{"class":163,"line":292},[290,55482,12001],{"class":541},[290,55484,55485],{"class":295}," (min-width: 650px) {\n",[290,55487,55488,55491],{"class":163,"line":330},[290,55489,55490],{"class":303},"  .feature-grid",[290,55492,450],{"class":295},[290,55494,55495,55497,55499,55501],{"class":163,"line":337},[290,55496,34590],{"class":461},[290,55498,465],{"class":295},[290,55500,9147],{"class":461},[290,55502,471],{"class":295},[290,55504,55505,55507,55509,55511,55513,55515,55517,55519,55521,55523,55525,55527,55529,55531],{"class":163,"line":364},[290,55506,36011],{"class":461},[290,55508,465],{"class":295},[290,55510,44542],{"class":461},[290,55512,484],{"class":295},[290,55514,44547],{"class":461},[290,55516,569],{"class":295},[290,55518,44552],{"class":461},[290,55520,484],{"class":295},[290,55522,3112],{"class":461},[290,55524,674],{"class":541},[290,55526,569],{"class":295},[290,55528,468],{"class":461},[290,55530,11964],{"class":541},[290,55532,11616],{"class":295},[290,55534,55535,55537,55539,55541,55543,55545],{"class":163,"line":386},[290,55536,36027],{"class":461},[290,55538,465],{"class":295},[290,55540,1622],{"class":461},[290,55542,484],{"class":295},[290,55544,38041],{"class":1561},[290,55546,500],{"class":295},[290,55548,55549],{"class":163,"line":408},[290,55550,771],{"class":295},[290,55552,55553],{"class":163,"line":428},[290,55554,620],{"class":295},[2757,55556,55558],{"id":55557},"subgrid-alignment-across-nested-components","Subgrid Alignment Across Nested Components",[14,55560,55561,55562,55564],{},"When child components inherit track sizing from a parent grid, ",[18,55563,44299],{}," ensures visual harmony. Container queries can conditionally enable subgrid alignment when space permits, preventing awkward gaps or misaligned baselines.",[281,55566,55568],{"className":438,"code":55567,"language":440,"meta":286,"style":286},"@container (min-width: 800px) {\n  .nested-card {\n    display: grid;\n    grid-template-columns: subgrid;\n    grid-column: span 3;\n  }\n}\n",[18,55569,55570,55577,55584,55594,55604,55614,55618],{"__ignoreMap":286},[290,55571,55572,55574],{"class":163,"line":292},[290,55573,12001],{"class":541},[290,55575,55576],{"class":295}," (min-width: 800px) {\n",[290,55578,55579,55582],{"class":163,"line":330},[290,55580,55581],{"class":303},"  .nested-card",[290,55583,450],{"class":295},[290,55585,55586,55588,55590,55592],{"class":163,"line":337},[290,55587,34590],{"class":461},[290,55589,465],{"class":295},[290,55591,9147],{"class":461},[290,55593,471],{"class":295},[290,55595,55596,55598,55600,55602],{"class":163,"line":364},[290,55597,36011],{"class":461},[290,55599,465],{"class":295},[290,55601,44299],{"class":461},[290,55603,471],{"class":295},[290,55605,55606,55608,55610,55612],{"class":163,"line":386},[290,55607,55046],{"class":461},[290,55609,44678],{"class":295},[290,55611,1579],{"class":461},[290,55613,471],{"class":295},[290,55615,55616],{"class":163,"line":408},[290,55617,771],{"class":295},[290,55619,55620],{"class":163,"line":428},[290,55621,620],{"class":295},[14,55623,55624,55625,42],{},"Subgrid alignment unlocks precise typographic and visual harmony across deeply nested component boundaries. For dedicated two-dimensional layout techniques, see the guide to ",[27,55626,55627],{"href":44306},"CSS Grid and subgrid layouts",[47,55629],{},[50,55631,55633],{"id":55632},"component-patterns-style-queries","Component Patterns & Style Queries",[14,55635,55636,55637,55640],{},"Beyond dimensional queries, CSS Container Queries Level 3 introduces ",[62,55638,55639],{},"style queries",", enabling components to react to computed CSS custom properties and inherited states. This unlocks dynamic theming and micro-interaction triggers without JavaScript.",[2757,55642,55644],{"id":55643},"querying-computed-styles","Querying Computed Styles",[14,55646,55647,55648,42],{},"Style queries evaluate the computed value of a CSS property on the container. The syntax uses ",[18,55649,55650],{},"@container style(\u003Cproperty>: \u003Cvalue>)",[281,55652,55654],{"className":438,"code":55653,"language":440,"meta":286,"style":286},"\u002F* Theme-driven layout shift *\u002F\n@container style(--theme: dark) {\n  .card {\n    background: var(--surface-dark);\n    border: 1px solid var(--border-subtle);\n    color: var(--text-primary);\n  }\n}\n\n\u002F* Interactive state adaptation *\u002F\n@container style(--is-expanded: true) {\n  .accordion__panel {\n    max-height: 1000px;\n    opacity: 1;\n    transition:\n      max-height 0.3s ease,\n      opacity 0.3s ease;\n  }\n}\n",[18,55655,55656,55661,55667,55673,55688,55709,55724,55728,55732,55736,55741,55748,55755,55769,55779,55785,55798,55810,55814],{"__ignoreMap":286},[290,55657,55658],{"class":163,"line":292},[290,55659,55660],{"class":455},"\u002F* Theme-driven layout shift *\u002F\n",[290,55662,55663,55665],{"class":163,"line":330},[290,55664,12001],{"class":541},[290,55666,42277],{"class":295},[290,55668,55669,55671],{"class":163,"line":337},[290,55670,9083],{"class":303},[290,55672,450],{"class":295},[290,55674,55675,55677,55679,55681,55683,55686],{"class":163,"line":364},[290,55676,9124],{"class":461},[290,55678,465],{"class":295},[290,55680,1622],{"class":461},[290,55682,484],{"class":295},[290,55684,55685],{"class":1561},"--surface-dark",[290,55687,500],{"class":295},[290,55689,55690,55692,55694,55696,55698,55700,55702,55704,55707],{"class":163,"line":386},[290,55691,21285],{"class":461},[290,55693,465],{"class":295},[290,55695,468],{"class":461},[290,55697,674],{"class":541},[290,55699,852],{"class":461},[290,55701,1635],{"class":461},[290,55703,484],{"class":295},[290,55705,55706],{"class":1561},"--border-subtle",[290,55708,500],{"class":295},[290,55710,55711,55713,55715,55717,55719,55722],{"class":163,"line":408},[290,55712,36064],{"class":461},[290,55714,465],{"class":295},[290,55716,1622],{"class":461},[290,55718,484],{"class":295},[290,55720,55721],{"class":1561},"--text-primary",[290,55723,500],{"class":295},[290,55725,55726],{"class":163,"line":428},[290,55727,771],{"class":295},[290,55729,55730],{"class":163,"line":517},[290,55731,620],{"class":295},[290,55733,55734],{"class":163,"line":523},[290,55735,334],{"emptyLinePlaceholder":333},[290,55737,55738],{"class":163,"line":532},[290,55739,55740],{"class":455},"\u002F* Interactive state adaptation *\u002F\n",[290,55742,55743,55745],{"class":163,"line":551},[290,55744,12001],{"class":541},[290,55746,55747],{"class":295}," style(--is-expanded: true) {\n",[290,55749,55750,55753],{"class":163,"line":586},[290,55751,55752],{"class":303},"  .accordion__panel",[290,55754,450],{"class":295},[290,55756,55757,55760,55762,55765,55767],{"class":163,"line":602},[290,55758,55759],{"class":461},"    max-height",[290,55761,465],{"class":295},[290,55763,55764],{"class":461},"1000",[290,55766,674],{"class":541},[290,55768,471],{"class":295},[290,55770,55771,55773,55775,55777],{"class":163,"line":617},[290,55772,733],{"class":461},[290,55774,465],{"class":295},[290,55776,468],{"class":461},[290,55778,471],{"class":295},[290,55780,55781,55783],{"class":163,"line":623},[290,55782,4745],{"class":461},[290,55784,529],{"class":295},[290,55786,55787,55790,55792,55794,55796],{"class":163,"line":628},[290,55788,55789],{"class":295},"      max-height ",[290,55791,199],{"class":461},[290,55793,1886],{"class":541},[290,55795,545],{"class":461},[290,55797,548],{"class":295},[290,55799,55800,55802,55804,55806,55808],{"class":163,"line":634},[290,55801,24619],{"class":295},[290,55803,199],{"class":461},[290,55805,1886],{"class":541},[290,55807,545],{"class":461},[290,55809,471],{"class":295},[290,55811,55812],{"class":163,"line":649},[290,55813,771],{"class":295},[290,55815,55816],{"class":163,"line":660},[290,55817,620],{"class":295},[14,55819,55820],{},"Style queries integrate seamlessly with modern pseudo-classes and state-driven selectors. They evaluate the computed value of custom properties on the container, enabling component-level theme switching and state-driven layout without JavaScript.",[47,55822],{},[50,55824,55826],{"id":55825},"progressive-enhancement-production-fallbacks","Progressive Enhancement & Production Fallbacks",[14,55828,55829],{},"Production environments demand graceful degradation. While container queries enjoy robust native support, legacy browsers require viewport-based fallbacks to maintain usability and accessibility.",[2757,55831,55832,55833],{"id":37920},"Feature Detection with ",[18,55834,2086],{},[14,55836,55837],{},"Wrap container-specific styles in a feature query. Browsers that lack support will skip the block and fall back to standard viewport media queries.",[281,55839,55841],{"className":438,"code":55840,"language":440,"meta":286,"style":286},"\u002F* Base mobile-first layout *\u002F\n.component {\n  padding: var(--space-sm);\n}\n\n\u002F* Viewport fallback for legacy browsers *\u002F\n@supports not (container-type: inline-size) {\n  @media (min-width: 768px) {\n    .component {\n      padding: var(--space-lg);\n    }\n  }\n}\n\n\u002F* Modern container query implementation *\u002F\n@supports (container-type: inline-size) {\n  .component-wrapper {\n    container-type: inline-size;\n  }\n\n  @container (min-width: 768px) {\n    .component {\n      padding: var(--space-lg);\n    }\n  }\n}\n",[18,55842,55843,55848,55855,55870,55874,55878,55883,55895,55911,55918,55933,55937,55941,55945,55949,55953,55963,55970,55976,55980,55984,55991,55997,56011,56015,56019],{"__ignoreMap":286},[290,55844,55845],{"class":163,"line":292},[290,55846,55847],{"class":455},"\u002F* Base mobile-first layout *\u002F\n",[290,55849,55850,55853],{"class":163,"line":330},[290,55851,55852],{"class":303},".component",[290,55854,450],{"class":295},[290,55856,55857,55859,55861,55863,55865,55868],{"class":163,"line":337},[290,55858,10433],{"class":461},[290,55860,465],{"class":295},[290,55862,1622],{"class":461},[290,55864,484],{"class":295},[290,55866,55867],{"class":1561},"--space-sm",[290,55869,500],{"class":295},[290,55871,55872],{"class":163,"line":364},[290,55873,620],{"class":295},[290,55875,55876],{"class":163,"line":386},[290,55877,334],{"emptyLinePlaceholder":333},[290,55879,55880],{"class":163,"line":408},[290,55881,55882],{"class":455},"\u002F* Viewport fallback for legacy browsers *\u002F\n",[290,55884,55885,55887,55889,55891,55893],{"class":163,"line":428},[290,55886,2086],{"class":541},[290,55888,2116],{"class":541},[290,55890,3595],{"class":295},[290,55892,24401],{"class":461},[290,55894,26104],{"class":295},[290,55896,55897,55899,55901,55903,55905,55907,55909],{"class":163,"line":517},[290,55898,26109],{"class":541},[290,55900,3595],{"class":295},[290,55902,26114],{"class":461},[290,55904,465],{"class":295},[290,55906,38134],{"class":461},[290,55908,674],{"class":541},[290,55910,646],{"class":295},[290,55912,55913,55916],{"class":163,"line":523},[290,55914,55915],{"class":303},"    .component",[290,55917,450],{"class":295},[290,55919,55920,55922,55924,55926,55928,55931],{"class":163,"line":532},[290,55921,42626],{"class":461},[290,55923,465],{"class":295},[290,55925,1622],{"class":461},[290,55927,484],{"class":295},[290,55929,55930],{"class":1561},"--space-lg",[290,55932,500],{"class":295},[290,55934,55935],{"class":163,"line":551},[290,55936,8200],{"class":295},[290,55938,55939],{"class":163,"line":586},[290,55940,771],{"class":295},[290,55942,55943],{"class":163,"line":602},[290,55944,620],{"class":295},[290,55946,55947],{"class":163,"line":617},[290,55948,334],{"emptyLinePlaceholder":333},[290,55950,55951],{"class":163,"line":623},[290,55952,37003],{"class":455},[290,55954,55955,55957,55959,55961],{"class":163,"line":628},[290,55956,2086],{"class":541},[290,55958,3595],{"class":295},[290,55960,24401],{"class":461},[290,55962,26104],{"class":295},[290,55964,55965,55968],{"class":163,"line":634},[290,55966,55967],{"class":303},"  .component-wrapper",[290,55969,450],{"class":295},[290,55971,55972,55974],{"class":163,"line":649},[290,55973,37025],{"class":461},[290,55975,25565],{"class":295},[290,55977,55978],{"class":163,"line":660},[290,55979,771],{"class":295},[290,55981,55982],{"class":163,"line":688},[290,55983,334],{"emptyLinePlaceholder":333},[290,55985,55986,55988],{"class":163,"line":693},[290,55987,37040],{"class":541},[290,55989,55990],{"class":295}," (min-width: 768px) {\n",[290,55992,55993,55995],{"class":163,"line":698},[290,55994,55915],{"class":303},[290,55996,450],{"class":295},[290,55998,55999,56001,56003,56005,56007,56009],{"class":163,"line":704},[290,56000,42626],{"class":461},[290,56002,465],{"class":295},[290,56004,1622],{"class":461},[290,56006,484],{"class":295},[290,56008,55930],{"class":1561},[290,56010,500],{"class":295},[290,56012,56013],{"class":163,"line":710},[290,56014,8200],{"class":295},[290,56016,56017],{"class":163,"line":717},[290,56018,771],{"class":295},[290,56020,56021],{"class":163,"line":730},[290,56022,620],{"class":295},[14,56024,56025,56026,42],{},"Always maintain a strict performance budget when implementing fallbacks. Avoid duplicating complex layout logic; instead, isolate container-specific enhancements. For cross-browser reliability strategies, review ",[27,56027,35802],{"href":35801},[47,56029],{},[50,56031,56033],{"id":56032},"css-architecture-reset-strategies","CSS Architecture & Reset Strategies",[14,56035,56036],{},"A scalable container-driven design system requires a normalized foundation. Default browser styles and inherited box models often conflict with containment boundaries, leading to unpredictable overflow and spacing issues.",[2757,56038,56040],{"id":56039},"modern-reset-for-container-isolation","Modern Reset for Container Isolation",[14,56042,56043,56044,56046],{},"Apply a scoped reset that normalizes ",[18,56045,46435],{},", resets margins, and establishes custom property defaults within the container context.",[281,56048,56050],{"className":438,"code":56049,"language":440,"meta":286,"style":286},"\u002F* Scoped reset for contained components *\u002F\n@layer component-base {\n  .component-wrapper {\n    container-type: inline-size;\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n    font-family: var(--font-sans);\n    line-height: var(--leading-normal);\n    color: var(--text-default);\n  }\n\n  .component-wrapper *,\n  .component-wrapper *::before,\n  .component-wrapper *::after {\n    box-sizing: inherit;\n  }\n}\n",[18,56051,56052,56057,56064,56070,56076,56087,56097,56107,56123,56138,56153,56157,56161,56169,56179,56189,56199,56203],{"__ignoreMap":286},[290,56053,56054],{"class":163,"line":292},[290,56055,56056],{"class":455},"\u002F* Scoped reset for contained components *\u002F\n",[290,56058,56059,56061],{"class":163,"line":330},[290,56060,12681],{"class":541},[290,56062,56063],{"class":295}," component-base {\n",[290,56065,56066,56068],{"class":163,"line":337},[290,56067,55967],{"class":303},[290,56069,450],{"class":295},[290,56071,56072,56074],{"class":163,"line":364},[290,56073,37025],{"class":461},[290,56075,25565],{"class":295},[290,56077,56078,56081,56083,56085],{"class":163,"line":386},[290,56079,56080],{"class":461},"    box-sizing",[290,56082,465],{"class":295},[290,56084,46440],{"class":461},[290,56086,471],{"class":295},[290,56088,56089,56091,56093,56095],{"class":163,"line":408},[290,56090,39260],{"class":461},[290,56092,465],{"class":295},[290,56094,487],{"class":461},[290,56096,471],{"class":295},[290,56098,56099,56101,56103,56105],{"class":163,"line":428},[290,56100,36040],{"class":461},[290,56102,465],{"class":295},[290,56104,487],{"class":461},[290,56106,471],{"class":295},[290,56108,56109,56112,56114,56116,56118,56121],{"class":163,"line":517},[290,56110,56111],{"class":461},"    font-family",[290,56113,465],{"class":295},[290,56115,1622],{"class":461},[290,56117,484],{"class":295},[290,56119,56120],{"class":1561},"--font-sans",[290,56122,500],{"class":295},[290,56124,56125,56127,56129,56131,56133,56136],{"class":163,"line":523},[290,56126,39248],{"class":461},[290,56128,465],{"class":295},[290,56130,1622],{"class":461},[290,56132,484],{"class":295},[290,56134,56135],{"class":1561},"--leading-normal",[290,56137,500],{"class":295},[290,56139,56140,56142,56144,56146,56148,56151],{"class":163,"line":532},[290,56141,36064],{"class":461},[290,56143,465],{"class":295},[290,56145,1622],{"class":461},[290,56147,484],{"class":295},[290,56149,56150],{"class":1561},"--text-default",[290,56152,500],{"class":295},[290,56154,56155],{"class":163,"line":551},[290,56156,771],{"class":295},[290,56158,56159],{"class":163,"line":586},[290,56160,334],{"emptyLinePlaceholder":333},[290,56162,56163,56165,56167],{"class":163,"line":602},[290,56164,55967],{"class":303},[290,56166,3566],{"class":299},[290,56168,548],{"class":295},[290,56170,56171,56173,56175,56177],{"class":163,"line":617},[290,56172,55967],{"class":303},[290,56174,3566],{"class":299},[290,56176,2811],{"class":303},[290,56178,548],{"class":295},[290,56180,56181,56183,56185,56187],{"class":163,"line":623},[290,56182,55967],{"class":303},[290,56184,3566],{"class":299},[290,56186,2412],{"class":303},[290,56188,450],{"class":295},[290,56190,56191,56193,56195,56197],{"class":163,"line":628},[290,56192,56080],{"class":461},[290,56194,465],{"class":295},[290,56196,1970],{"class":461},[290,56198,471],{"class":295},[290,56200,56201],{"class":163,"line":634},[290,56202,771],{"class":295},[290,56204,56205],{"class":163,"line":649},[290,56206,620],{"class":295},[2757,56208,56210],{"id":56209},"custom-property-scoping-design-tokens","Custom Property Scoping & Design Tokens",[14,56212,56213],{},"Scope design tokens directly to the container to prevent cascade leakage and enable theme switching at the component level.",[281,56215,56217],{"className":438,"code":56216,"language":440,"meta":286,"style":286},".component-wrapper {\n  --card-bg: var(--surface-elevated);\n  --card-radius: var(--radius-md);\n  --card-padding: var(--space-md);\n\n  background: var(--card-bg);\n  border-radius: var(--card-radius);\n  padding: var(--card-padding);\n}\n",[18,56218,56219,56226,56241,56257,56272,56276,56290,56305,56320],{"__ignoreMap":286},[290,56220,56221,56224],{"class":163,"line":292},[290,56222,56223],{"class":303},".component-wrapper",[290,56225,450],{"class":295},[290,56227,56228,56230,56232,56234,56236,56239],{"class":163,"line":330},[290,56229,19729],{"class":1561},[290,56231,465],{"class":295},[290,56233,1622],{"class":461},[290,56235,484],{"class":295},[290,56237,56238],{"class":1561},"--surface-elevated",[290,56240,500],{"class":295},[290,56242,56243,56246,56248,56250,56252,56255],{"class":163,"line":337},[290,56244,56245],{"class":1561},"  --card-radius",[290,56247,465],{"class":295},[290,56249,1622],{"class":461},[290,56251,484],{"class":295},[290,56253,56254],{"class":1561},"--radius-md",[290,56256,500],{"class":295},[290,56258,56259,56262,56264,56266,56268,56270],{"class":163,"line":364},[290,56260,56261],{"class":1561},"  --card-padding",[290,56263,465],{"class":295},[290,56265,1622],{"class":461},[290,56267,484],{"class":295},[290,56269,38041],{"class":1561},[290,56271,500],{"class":295},[290,56273,56274],{"class":163,"line":386},[290,56275,334],{"emptyLinePlaceholder":333},[290,56277,56278,56280,56282,56284,56286,56288],{"class":163,"line":408},[290,56279,1186],{"class":461},[290,56281,465],{"class":295},[290,56283,1622],{"class":461},[290,56285,484],{"class":295},[290,56287,19952],{"class":1561},[290,56289,500],{"class":295},[290,56291,56292,56294,56296,56298,56300,56303],{"class":163,"line":428},[290,56293,1663],{"class":461},[290,56295,465],{"class":295},[290,56297,1622],{"class":461},[290,56299,484],{"class":295},[290,56301,56302],{"class":1561},"--card-radius",[290,56304,500],{"class":295},[290,56306,56307,56309,56311,56313,56315,56318],{"class":163,"line":517},[290,56308,10433],{"class":461},[290,56310,465],{"class":295},[290,56312,1622],{"class":461},[290,56314,484],{"class":295},[290,56316,56317],{"class":1561},"--card-padding",[290,56319,500],{"class":295},[290,56321,56322],{"class":163,"line":523},[290,56323,620],{"class":295},[14,56325,56326,56327,56331,56332,56334],{},"For foundational normalization techniques, see ",[27,56328,56330],{"href":56329},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002F","Modern CSS Reset Strategies",". Scope design tokens to the container element rather than ",[18,56333,1554],{}," when components must be independently themeable.",[47,56336],{},[50,56338,24807],{"id":24806},[14,56340,56341],{},"Container queries are widely supported in modern browsers. Style queries require newer engine versions. Always verify support before shipping to production.",[2250,56343,56344,56356],{},[2253,56345,56346],{},[2256,56347,56348,56350,56352,56354],{},[2259,56349,3737],{},[2259,56351,24818],{},[2259,56353,2297],{},[2259,56355,2287],{},[2269,56357,56358,56371,56383],{},[2256,56359,56360,56365,56367,56369],{},[2274,56361,56362,56364],{},[18,56363,24401],{}," & Size Queries",[2274,56366,29200],{},[2274,56368,8465],{},[2274,56370,37592],{},[2256,56372,56373,56377,56379,56381],{},[2274,56374,56375],{},[18,56376,24933],{},[2274,56378,24936],{},[2274,56380,24939],{},[2274,56382,24942],{},[2256,56384,56385,56392,56394,56396],{},[2274,56386,56387,1203,56389,56391],{},[18,56388,11404],{},[18,56390,12282],{}," Units",[2274,56393,29200],{},[2274,56395,8465],{},[2274,56397,37592],{},[14,56399,56400,56406,56407,56412],{},[62,56401,56402,56403,56405],{},"Progressive enhancement via ",[18,56404,2086],{}," is mandatory for production environments."," Monitor ",[27,56408,56411],{"href":56409,"rel":56410},"https:\u002F\u002Fcaniuse.com\u002Fcss-container-queries",[13489],"caniuse.com\u002Fcss-container-queries"," for real-time engine updates.",[47,56414],{},[50,56416,24968],{"id":24967},[2250,56418,56419,56429],{},[2253,56420,56421],{},[2256,56422,56423,56425,56427],{},[2259,56424,2338],{},[2259,56426,8560],{},[2259,56428,24981],{},[2269,56430,56431,56447,56465,56489,56505],{},[2256,56432,56433,56438,56441],{},[2274,56434,56435],{},[62,56436,56437],{},"Circular containment loops",[2274,56439,56440],{},"A container queries itself or a child that modifies the container's size.",[2274,56442,56443,56444,56446],{},"Ensure containment boundaries are strictly hierarchical. Never apply ",[18,56445,24401],{}," to an element that is also a direct child of another queried container without explicit isolation.",[2256,56448,56449,56454,56460],{},[2274,56450,56451],{},[62,56452,56453],{},"Style query specificity conflicts",[2274,56455,56456,56457,56459],{},"Cascade layers or inline styles override ",[18,56458,24933],{}," rules.",[2274,56461,1499,56462,56464],{},[18,56463,12681],{}," to manage specificity. Style queries evaluate computed values, so ensure tokens are applied before the query evaluates.",[2256,56466,56467,56472,56475],{},[2274,56468,56469],{},[62,56470,56471],{},"Unexpected layout shifts (CLS)",[2274,56473,56474],{},"Rapid container resize triggers synchronous reflow.",[2274,56476,1499,56477,56479,56480,3041,56482,56484,56485,2351,56487,42],{},[18,56478,40216],{}," on static wrappers. Avoid animating ",[18,56481,1748],{},[18,56483,2722],{}," on containers; prefer ",[18,56486,103],{},[18,56488,493],{},[2256,56490,56491,56496,56499],{},[2274,56492,56493],{},[62,56494,56495],{},"Over-nesting performance degradation",[2274,56497,56498],{},"Deeply nested containers force the browser to recalculate multiple containment trees.",[2274,56500,56501,56502,56504],{},"Flatten container hierarchies. Use a single ",[18,56503,24401],{}," per logical UI module.",[2256,56506,56507,56512,56515],{},[2274,56508,56509],{},[62,56510,56511],{},"Fallback media queries overriding container styles",[2274,56513,56514],{},"Higher specificity or later cascade order in fallback blocks.",[2274,56516,40776,56517,56519],{},[18,56518,38210],{}," and ensure container queries appear later in the stylesheet or use higher specificity selectors.",[47,56521],{},[50,56523,1316],{"id":1315},[14,56525,56526,56529],{},[62,56527,56528],{},"When should I use container queries instead of media queries?","\nUse container queries when a component's layout depends on its parent container's dimensions rather than the viewport. This is ideal for reusable UI modules, cards, and sidebars that appear in multiple contexts.",[14,56531,56532,56535,56536,69,56538,56541],{},[62,56533,56534],{},"Do container queries impact performance?","\nContainer queries are highly optimized by modern browsers. However, excessive use of layout containment or deeply nested containers can trigger frequent recalculations. Use ",[18,56537,40216],{},[18,56539,56540],{},"contain: style"," strategically, and avoid querying rapidly animating dimensions.",[14,56543,56544,56546,56547,56550,56551,56553],{},[62,56545,42983],{},"\nWrap container-specific CSS in ",[18,56548,56549],{},"@supports (container-type: inline-size) { ... }",". Provide a viewport-based ",[18,56552,874],{}," fallback outside the block to ensure graceful degradation.",[14,56555,56556,56559,56560,56563],{},[62,56557,56558],{},"Can I query CSS custom properties inside a container?","\nYes, using ",[18,56561,56562],{},"@container style(--property: value)",". This enables state-driven styling, such as switching layouts based on theme tokens or user preferences without JavaScript.",[47,56565],{},[50,56567,1391],{"id":1390},[1393,56569,56570,56579,56584,56589,56598,56603,56608,56613,56618],{},[1396,56571,56572,19562,56574,41968,56576,56578],{},[27,56573,26284],{"href":25340},[18,56575,12001],{},[18,56577,24401],{},", and relative units.",[1396,56580,56581,56583],{},[27,56582,35802],{"href":35801}," — progressive enhancement and feature detection for legacy engines.",[1396,56585,56586,56588],{},[27,56587,12361],{"href":11312}," — breakpoint-free type scaling that pairs with container units.",[1396,56590,56591,56593,56594,69,56596,42],{},[27,56592,47441],{"href":47440}," — content-driven dimensions with ",[18,56595,38658],{},[18,56597,55390],{},[1396,56599,56600,56602],{},[27,56601,56330],{"href":56329}," — a normalized foundation for contained components.",[1396,56604,56605,56607],{},[27,56606,38098],{"href":38097}," — copy-paste cards, navigation, and tables that adapt to their parent.",[1396,56609,56610,56612],{},[27,56611,55627],{"href":44306}," — two-dimensional layout and subgrid track inheritance.",[1396,56614,56615,56617],{},[27,56616,33331],{"href":8756}," — compositor-friendly motion for the components you build here.",[1396,56619,56620,56623],{},[27,56621,56622],{"href":1480},"CSS-only micro-interactions and animations"," — the companion guide to transitions, keyframes, and accessible motion.",[1430,56625,16324],{},{"title":286,"searchDepth":330,"depth":330,"links":56627},[56628,56631,56636,56641,56645,56648,56651,56655,56656,56657,56658],{"id":54930,"depth":330,"text":54931,"children":56629},[56630],{"id":54943,"depth":337,"text":54944},{"id":55095,"depth":330,"text":55096,"children":56632},[56633,56635],{"id":55102,"depth":337,"text":56634},"Defining container-type and container-name",{"id":55121,"depth":337,"text":55122},{"id":55248,"depth":330,"text":55249,"children":56637},[56638,56640],{"id":55262,"depth":337,"text":56639},"Combining clamp() with Container Queries",{"id":55379,"depth":337,"text":55380},{"id":55462,"depth":330,"text":55463,"children":56642},[56643,56644],{"id":55469,"depth":337,"text":55470},{"id":55557,"depth":337,"text":55558},{"id":55632,"depth":330,"text":55633,"children":56646},[56647],{"id":55643,"depth":337,"text":55644},{"id":55825,"depth":330,"text":55826,"children":56649},[56650],{"id":37920,"depth":337,"text":37784},{"id":56032,"depth":330,"text":56033,"children":56652},[56653,56654],{"id":56039,"depth":337,"text":56040},{"id":56209,"depth":337,"text":56210},{"id":24806,"depth":330,"text":24807},{"id":24967,"depth":330,"text":24968},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Master container queries and responsive layouts: CSS containment, intrinsic sizing, component-scoped breakpoints, and production-ready architecture.",{"seoTitle":56661,"datePublished":1447,"dateModified":1447,"faq":56662},"Mastering Container Queries & Layouts",[56663,56665,56667,56669],{"q":56528,"a":56664},"Use container queries when a component's layout depends on its parent container's dimensions rather than the viewport. This is ideal for reusable UI modules, cards, and sidebars that appear in multiple contexts.",{"q":56534,"a":56666},"Container queries are highly optimized by modern browsers. However, excessive use of layout containment or deeply nested containers can trigger frequent recalculations. Use contain: layout and contain: style strategically, and avoid querying rapidly animating dimensions.",{"q":42983,"a":56668},"Wrap container-specific CSS in @supports (container-type: inline-size). Provide a viewport-based @media fallback outside the block to ensure graceful degradation.",{"q":56558,"a":56670},"Yes, using @container style(--property: value). This enables state-driven styling, such as switching layouts based on theme tokens or user preferences without JavaScript.","\u002Fmastering-container-queries-responsive-layouts",{"title":11296,"description":56659},"mastering-container-queries-responsive-layouts\u002Findex","-L5rqGAUgmN5KyEFW1g3umXW0yRXigC1lmkKzLqo4ug",{"id":56676,"title":56677,"body":56678,"description":57722,"extension":1444,"meta":57723,"navigation":333,"path":57734,"seo":57735,"stem":57736,"__hash__":57737},"content\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Faspect-ratio-for-responsive-media\u002Findex.md","Using aspect-ratio for Responsive Media and Preventing Layout Shift",{"type":7,"value":56679,"toc":57713},[56680,56683,56700,56704,56710,56718,56721,56723,56725,56731,57403,57417,57457,57461,57494,57496,57500,57506,57605,57608,57610,57630,57632,57638,57655,57667,57680,57682,57711],[10,56681,56677],{"id":56682},"using-aspect-ratio-for-responsive-media-and-preventing-layout-shift",[14,56684,56685,56686,56688,56689,56691,56692,56695,56696,51247,56698,42],{},"Media that loads after the page renders is the classic cause of cumulative layout shift: the browser does not know how tall an image or video will be until its dimensions arrive, so content below it jumps when the box suddenly grows. The CSS ",[18,56687,38572],{}," property fixes this by reserving the correct height from a known width and a declared ratio, before a single byte of the media loads. This guide covers ",[18,56690,38572],{}," for responsive images, video, and embeds, and the ",[18,56693,56694],{},"padding-top"," hack fallback for older engines. It is part of ",[27,56697,47441],{"href":47440},[27,56699,11296],{"href":5777},[50,56701,56703],{"id":56702},"why-a-css-ratio-instead-of-fixed-heights-or-javascript","Why a CSS ratio instead of fixed heights or JavaScript",[14,56705,56706,56707,56709],{},"The problem is precisely timing: layout happens before media dimensions are known. A fixed pixel height appears to solve it but breaks responsiveness — the media is locked to one height regardless of width, so it letterboxes or distorts as the column changes size. Reading the image's natural size in JavaScript and setting a height after load is worse, because that runs ",[86,56708,956],{}," the first layout, which is the exact moment the shift you wanted to prevent has already happened.",[14,56711,56712,56714,56715,56717],{},[18,56713,38572],{}," resolves the timing problem inside CSS. Given a width — from a column, a grid track, or ",[18,56716,25907],{}," of a parent — and a ratio, the engine computes the height during the first layout pass, so the box is the right size before the media arrives. When the media loads it pours into a box that already fits, and nothing below it moves. This is both a Core Web Vitals win (CLS approaches zero) and a correctness win, and it costs no script and no measurement.",[14,56719,56720],{},"There is an accessibility dimension as well. Reserving space keeps the page geometrically stable while assistive technology and keyboard focus traverse it; a user tabbing through a page does not have content reflow out from under them as images stream in. The ratio also respects zoom, because it is a pure proportion applied to whatever the zoomed width resolves to.",[47,56722],{},[50,56724,4203],{"id":4202},[14,56726,56727,56728,56730],{},"The block below handles the three media cases — an image, a video element, and an iframe embed — each kept at a stable ratio with no layout shift, plus a ",[18,56729,2086],{}," fallback to the padding hack for engines without the property.",[281,56732,56734],{"className":283,"code":56733,"language":285,"meta":286,"style":286},"\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\u003Cstyle>\n  body { font-family: system-ui, sans-serif; margin: 2rem; max-width: 40rem; }\n\n  \u002F* Reserve a 16:9 box from the width before media loads.\n     object-fit: cover crops to fill without distortion. *\u002F\n  .media-16x9 {\n    aspect-ratio: 16 \u002F 9;\n    width: 100%;\n    object-fit: cover;\n    display: block;\n    background: #7aa2ff14; \u002F* visible placeholder while loading *\u002F\n    border-radius: 0.5rem;\n  }\n\n  \u002F* iframes have no object-fit; the ratio alone shapes the box *\u002F\n  iframe.media-16x9 { border: 0; }\n\n  \u002F* Fallback for engines without aspect-ratio: padding-hack wrapper.\n     padding-top: 56.25% == 9\u002F16, reserving height as a % of width. *\u002F\n  @supports not (aspect-ratio: 16 \u002F 9) {\n    .ratio-fallback {\n      position: relative;\n      width: 100%;\n      padding-top: 56.25%;\n    }\n    .ratio-fallback > * {\n      position: absolute;\n      inset: 0;\n      width: 100%;\n      height: 100%;\n    }\n  }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Ch2>Image\u003C\u002Fh2>\n  \u003C!-- width\u002Fheight attributes give browsers a default ratio too -->\n  \u003Cimg class=\"media-16x9\" width=\"1600\" height=\"900\" alt=\"Scenic ridge at dawn\"\n       src=\"https:\u002F\u002Fexample.com\u002Fridge.jpg\">\n\n  \u003Ch2>Video\u003C\u002Fh2>\n  \u003Cdiv class=\"ratio-fallback\">\n    \u003Cvideo class=\"media-16x9\" controls poster=\"https:\u002F\u002Fexample.com\u002Fposter.jpg\">\n      \u003Csource src=\"https:\u002F\u002Fexample.com\u002Fclip.mp4\" type=\"video\u002Fmp4\">\n    \u003C\u002Fvideo>\n  \u003C\u002Fdiv>\n\n  \u003Ch2>Embed\u003C\u002Fh2>\n  \u003Cdiv class=\"ratio-fallback\">\n    \u003Ciframe class=\"media-16x9\" title=\"Location map\"\n            src=\"https:\u002F\u002Fexample.com\u002Fmap\" loading=\"lazy\">\u003C\u002Fiframe>\n  \u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,56735,56736,56746,56760,56768,56782,56802,56810,56848,56852,56857,56862,56869,56883,56895,56905,56915,56929,56941,56945,56949,56954,56972,56976,56981,56986,57006,57013,57024,57037,57051,57055,57065,57075,57086,57098,57111,57115,57119,57127,57135,57143,57156,57161,57197,57209,57213,57226,57241,57265,57287,57295,57303,57307,57320,57334,57355,57379,57387,57395],{"__ignoreMap":286},[290,56737,56738,56740,56742,56744],{"class":163,"line":292},[290,56739,8982],{"class":295},[290,56741,35913],{"class":299},[290,56743,8988],{"class":303},[290,56745,327],{"class":295},[290,56747,56748,56750,56752,56754,56756,56758],{"class":163,"line":330},[290,56749,296],{"class":295},[290,56751,285],{"class":299},[290,56753,8999],{"class":303},[290,56755,307],{"class":295},[290,56757,9004],{"class":310},[290,56759,327],{"class":295},[290,56761,56762,56764,56766],{"class":163,"line":337},[290,56763,296],{"class":295},[290,56765,9013],{"class":299},[290,56767,327],{"class":295},[290,56769,56770,56772,56774,56776,56778,56780],{"class":163,"line":364},[290,56771,296],{"class":295},[290,56773,9022],{"class":299},[290,56775,9025],{"class":303},[290,56777,307],{"class":295},[290,56779,9030],{"class":310},[290,56781,327],{"class":295},[290,56783,56784,56786,56788,56790,56792,56794,56796,56798,56800],{"class":163,"line":386},[290,56785,296],{"class":295},[290,56787,9022],{"class":299},[290,56789,35962],{"class":303},[290,56791,307],{"class":295},[290,56793,35967],{"class":310},[290,56795,35970],{"class":303},[290,56797,307],{"class":295},[290,56799,35975],{"class":310},[290,56801,327],{"class":295},[290,56803,56804,56806,56808],{"class":163,"line":408},[290,56805,296],{"class":295},[290,56807,1430],{"class":299},[290,56809,327],{"class":295},[290,56811,56812,56814,56816,56818,56820,56822,56824,56826,56828,56830,56832,56834,56836,56838,56840,56842,56844,56846],{"class":163,"line":428},[290,56813,44444],{"class":299},[290,56815,790],{"class":295},[290,56817,50110],{"class":461},[290,56819,465],{"class":295},[290,56821,50115],{"class":461},[290,56823,569],{"class":295},[290,56825,44479],{"class":461},[290,56827,828],{"class":295},[290,56829,2725],{"class":461},[290,56831,465],{"class":295},[290,56833,194],{"class":461},[290,56835,801],{"class":541},[290,56837,828],{"class":295},[290,56839,41191],{"class":461},[290,56841,465],{"class":295},[290,56843,4146],{"class":461},[290,56845,801],{"class":541},[290,56847,809],{"class":295},[290,56849,56850],{"class":163,"line":517},[290,56851,334],{"emptyLinePlaceholder":333},[290,56853,56854],{"class":163,"line":523},[290,56855,56856],{"class":455},"  \u002F* Reserve a 16:9 box from the width before media loads.\n",[290,56858,56859],{"class":163,"line":532},[290,56860,56861],{"class":455},"     object-fit: cover crops to fill without distortion. *\u002F\n",[290,56863,56864,56867],{"class":163,"line":551},[290,56865,56866],{"class":303},"  .media-16x9",[290,56868,450],{"class":295},[290,56870,56871,56873,56875,56877,56879,56881],{"class":163,"line":586},[290,56872,36346],{"class":461},[290,56874,465],{"class":295},[290,56876,9212],{"class":461},[290,56878,1203],{"class":295},[290,56880,247],{"class":461},[290,56882,471],{"class":295},[290,56884,56885,56887,56889,56891,56893],{"class":163,"line":602},[290,56886,9090],{"class":461},[290,56888,465],{"class":295},[290,56890,165],{"class":461},[290,56892,11018],{"class":541},[290,56894,471],{"class":295},[290,56896,56897,56899,56901,56903],{"class":163,"line":617},[290,56898,36361],{"class":461},[290,56900,465],{"class":295},[290,56902,36366],{"class":461},[290,56904,471],{"class":295},[290,56906,56907,56909,56911,56913],{"class":163,"line":623},[290,56908,34590],{"class":461},[290,56910,465],{"class":295},[290,56912,68],{"class":461},[290,56914,471],{"class":295},[290,56916,56917,56919,56921,56924,56926],{"class":163,"line":628},[290,56918,9124],{"class":461},[290,56920,465],{"class":295},[290,56922,56923],{"class":461},"#7aa2ff14",[290,56925,828],{"class":295},[290,56927,56928],{"class":455},"\u002F* visible placeholder while loading *\u002F\n",[290,56930,56931,56933,56935,56937,56939],{"class":163,"line":634},[290,56932,12759],{"class":461},[290,56934,465],{"class":295},[290,56936,798],{"class":461},[290,56938,801],{"class":541},[290,56940,471],{"class":295},[290,56942,56943],{"class":163,"line":649},[290,56944,771],{"class":295},[290,56946,56947],{"class":163,"line":660},[290,56948,334],{"emptyLinePlaceholder":333},[290,56950,56951],{"class":163,"line":688},[290,56952,56953],{"class":455},"  \u002F* iframes have no object-fit; the ratio alone shapes the box *\u002F\n",[290,56955,56956,56959,56962,56964,56966,56968,56970],{"class":163,"line":693},[290,56957,56958],{"class":299},"  iframe",[290,56960,56961],{"class":303},".media-16x9",[290,56963,790],{"class":295},[290,56965,843],{"class":461},[290,56967,465],{"class":295},[290,56969,487],{"class":461},[290,56971,809],{"class":295},[290,56973,56974],{"class":163,"line":698},[290,56975,334],{"emptyLinePlaceholder":333},[290,56977,56978],{"class":163,"line":704},[290,56979,56980],{"class":455},"  \u002F* Fallback for engines without aspect-ratio: padding-hack wrapper.\n",[290,56982,56983],{"class":163,"line":710},[290,56984,56985],{"class":455},"     padding-top: 56.25% == 9\u002F16, reserving height as a % of width. *\u002F\n",[290,56987,56988,56990,56992,56994,56996,56998,57000,57002,57004],{"class":163,"line":717},[290,56989,36183],{"class":541},[290,56991,2116],{"class":541},[290,56993,3595],{"class":295},[290,56995,38572],{"class":461},[290,56997,465],{"class":295},[290,56999,9212],{"class":461},[290,57001,1203],{"class":295},[290,57003,247],{"class":461},[290,57005,646],{"class":295},[290,57007,57008,57011],{"class":163,"line":730},[290,57009,57010],{"class":303},"    .ratio-fallback",[290,57012,450],{"class":295},[290,57014,57015,57018,57020,57022],{"class":163,"line":742},[290,57016,57017],{"class":461},"      position",[290,57019,465],{"class":295},[290,57021,1871],{"class":461},[290,57023,471],{"class":295},[290,57025,57026,57029,57031,57033,57035],{"class":163,"line":768},[290,57027,57028],{"class":461},"      width",[290,57030,465],{"class":295},[290,57032,165],{"class":461},[290,57034,11018],{"class":541},[290,57036,471],{"class":295},[290,57038,57039,57042,57044,57047,57049],{"class":163,"line":774},[290,57040,57041],{"class":461},"      padding-top",[290,57043,465],{"class":295},[290,57045,57046],{"class":461},"56.25",[290,57048,11018],{"class":541},[290,57050,471],{"class":295},[290,57052,57053],{"class":163,"line":779},[290,57054,8200],{"class":295},[290,57056,57057,57059,57061,57063],{"class":163,"line":784},[290,57058,57010],{"class":303},[290,57060,24017],{"class":541},[290,57062,3566],{"class":299},[290,57064,450],{"class":295},[290,57066,57067,57069,57071,57073],{"class":163,"line":812},[290,57068,57017],{"class":461},[290,57070,465],{"class":295},[290,57072,1927],{"class":461},[290,57074,471],{"class":295},[290,57076,57077,57080,57082,57084],{"class":163,"line":860},[290,57078,57079],{"class":461},"      inset",[290,57081,465],{"class":295},[290,57083,487],{"class":461},[290,57085,471],{"class":295},[290,57087,57088,57090,57092,57094,57096],{"class":163,"line":865},[290,57089,57028],{"class":461},[290,57091,465],{"class":295},[290,57093,165],{"class":461},[290,57095,11018],{"class":541},[290,57097,471],{"class":295},[290,57099,57100,57103,57105,57107,57109],{"class":163,"line":871},[290,57101,57102],{"class":461},"      height",[290,57104,465],{"class":295},[290,57106,165],{"class":461},[290,57108,11018],{"class":541},[290,57110,471],{"class":295},[290,57112,57113],{"class":163,"line":880},[290,57114,8200],{"class":295},[290,57116,57117],{"class":163,"line":896},[290,57118,771],{"class":295},[290,57120,57121,57123,57125],{"class":163,"line":4734},[290,57122,431],{"class":295},[290,57124,1430],{"class":299},[290,57126,327],{"class":295},[290,57128,57129,57131,57133],{"class":163,"line":4742},[290,57130,431],{"class":295},[290,57132,9013],{"class":299},[290,57134,327],{"class":295},[290,57136,57137,57139,57141],{"class":163,"line":4761},[290,57138,296],{"class":295},[290,57140,9239],{"class":299},[290,57142,327],{"class":295},[290,57144,57145,57147,57149,57152,57154],{"class":163,"line":4766},[290,57146,367],{"class":295},[290,57148,50],{"class":299},[290,57150,57151],{"class":295},">Image\u003C\u002F",[290,57153,50],{"class":299},[290,57155,327],{"class":295},[290,57157,57158],{"class":163,"line":4786},[290,57159,57160],{"class":455},"  \u003C!-- width\u002Fheight attributes give browsers a default ratio too -->\n",[290,57162,57163,57165,57167,57169,57171,57174,57177,57179,57182,57185,57187,57190,57192,57194],{"class":163,"line":9635},[290,57164,367],{"class":295},[290,57166,136],{"class":299},[290,57168,314],{"class":303},[290,57170,307],{"class":295},[290,57172,57173],{"class":310},"\"media-16x9\"",[290,57175,57176],{"class":303}," width",[290,57178,307],{"class":295},[290,57180,57181],{"class":310},"\"1600\"",[290,57183,57184],{"class":303}," height",[290,57186,307],{"class":295},[290,57188,57189],{"class":310},"\"900\"",[290,57191,4341],{"class":303},[290,57193,307],{"class":295},[290,57195,57196],{"class":310},"\"Scenic ridge at dawn\"\n",[290,57198,57199,57202,57204,57207],{"class":163,"line":9641},[290,57200,57201],{"class":303},"       src",[290,57203,307],{"class":295},[290,57205,57206],{"class":310},"\"https:\u002F\u002Fexample.com\u002Fridge.jpg\"",[290,57208,327],{"class":295},[290,57210,57211],{"class":163,"line":9647},[290,57212,334],{"emptyLinePlaceholder":333},[290,57214,57215,57217,57219,57222,57224],{"class":163,"line":9665},[290,57216,367],{"class":295},[290,57218,50],{"class":299},[290,57220,57221],{"class":295},">Video\u003C\u002F",[290,57223,50],{"class":299},[290,57225,327],{"class":295},[290,57227,57228,57230,57232,57234,57236,57239],{"class":163,"line":9683},[290,57229,367],{"class":295},[290,57231,342],{"class":299},[290,57233,314],{"class":303},[290,57235,307],{"class":295},[290,57237,57238],{"class":310},"\"ratio-fallback\"",[290,57240,327],{"class":295},[290,57242,57243,57245,57247,57249,57251,57253,57256,57258,57260,57263],{"class":163,"line":9688},[290,57244,4290],{"class":295},[290,57246,4259],{"class":299},[290,57248,314],{"class":303},[290,57250,307],{"class":295},[290,57252,57173],{"class":310},[290,57254,57255],{"class":303}," controls",[290,57257,4278],{"class":303},[290,57259,307],{"class":295},[290,57261,57262],{"class":310},"\"https:\u002F\u002Fexample.com\u002Fposter.jpg\"",[290,57264,327],{"class":295},[290,57266,57267,57269,57271,57273,57275,57278,57280,57282,57285],{"class":163,"line":9694},[290,57268,36430],{"class":295},[290,57270,4293],{"class":299},[290,57272,4296],{"class":303},[290,57274,307],{"class":295},[290,57276,57277],{"class":310},"\"https:\u002F\u002Fexample.com\u002Fclip.mp4\"",[290,57279,393],{"class":303},[290,57281,307],{"class":295},[290,57283,57284],{"class":310},"\"video\u002Fmp4\"",[290,57286,327],{"class":295},[290,57288,57289,57291,57293],{"class":163,"line":9700},[290,57290,36502],{"class":295},[290,57292,4259],{"class":299},[290,57294,327],{"class":295},[290,57296,57297,57299,57301],{"class":163,"line":9710},[290,57298,4315],{"class":295},[290,57300,342],{"class":299},[290,57302,327],{"class":295},[290,57304,57305],{"class":163,"line":9716},[290,57306,334],{"emptyLinePlaceholder":333},[290,57308,57309,57311,57313,57316,57318],{"class":163,"line":9738},[290,57310,367],{"class":295},[290,57312,50],{"class":299},[290,57314,57315],{"class":295},">Embed\u003C\u002F",[290,57317,50],{"class":299},[290,57319,327],{"class":295},[290,57321,57322,57324,57326,57328,57330,57332],{"class":163,"line":9748},[290,57323,367],{"class":295},[290,57325,342],{"class":299},[290,57327,314],{"class":303},[290,57329,307],{"class":295},[290,57331,57238],{"class":310},[290,57333,327],{"class":295},[290,57335,57336,57338,57341,57343,57345,57347,57350,57352],{"class":163,"line":9754},[290,57337,4290],{"class":295},[290,57339,57340],{"class":299},"iframe",[290,57342,314],{"class":303},[290,57344,307],{"class":295},[290,57346,57173],{"class":310},[290,57348,57349],{"class":303}," title",[290,57351,307],{"class":295},[290,57353,57354],{"class":310},"\"Location map\"\n",[290,57356,57357,57360,57362,57365,57368,57370,57373,57375,57377],{"class":163,"line":9760},[290,57358,57359],{"class":303},"            src",[290,57361,307],{"class":295},[290,57363,57364],{"class":310},"\"https:\u002F\u002Fexample.com\u002Fmap\"",[290,57366,57367],{"class":303}," loading",[290,57369,307],{"class":295},[290,57371,57372],{"class":310},"\"lazy\"",[290,57374,11472],{"class":295},[290,57376,57340],{"class":299},[290,57378,327],{"class":295},[290,57380,57381,57383,57385],{"class":163,"line":9777},[290,57382,4315],{"class":295},[290,57384,342],{"class":299},[290,57386,327],{"class":295},[290,57388,57389,57391,57393],{"class":163,"line":9787},[290,57390,431],{"class":295},[290,57392,9239],{"class":299},[290,57394,327],{"class":295},[290,57396,57397,57399,57401],{"class":163,"line":9793},[290,57398,431],{"class":295},[290,57400,285],{"class":299},[290,57402,327],{"class":295},[14,57404,57405,57406,57409,57410,57412,57413,57416],{},"In the modern path the wrapper ",[18,57407,57408],{},"\u003Cdiv class=\"ratio-fallback\">"," is inert — the inner element sizes itself via ",[18,57411,38572],{},". Only when the property is unsupported does the ",[18,57414,57415],{},"@supports not"," rule activate the absolute-positioned fill.",[133,57418,140,57420,140,57423,140,57426,140,57429,140,57432,140,57435,140,57438,140,57440,140,57442,140,57445,140,57447,140,57450,140,57452,140,57454],{"viewBox":2588,"role":136,"ariaLabel":57419,"xmlns":138,"style":139},"An aspect-ratio box reserving its full height before media loads, then the media filling the already-sized box",[142,57421,57422],{},"aspect-ratio reserves space before media loads",[146,57424,57425],{},"Left: an empty placeholder box already sized to 16 by 9 with content below it. Right: the same box now filled by the loaded image, with the content below unmoved.",[150,57427,57428],{"x":152,"y":153,"style":1781},"space reserved before load, no shift after",[150,57430,57431],{"x":1822,"y":4172,"style":4181},"before load",[171,57433],{"x":1831,"y":5144,"width":8947,"height":5909,"rx":5901,"fill":177,"opacity":1883,"stroke":177,"strokeWidth":168,"strokeDashArray":57434},[5901,249],[150,57436,57437],{"x":1822,"y":40954,"style":1799},"aspect-ratio 16\u002F9",[171,57439],{"x":1831,"y":34174,"width":8947,"height":9212,"rx":1579,"fill":167,"opacity":199},[171,57441],{"x":1831,"y":45904,"width":2602,"height":9212,"rx":1579,"fill":167,"opacity":199},[150,57443,57444],{"x":1812,"y":4172,"style":4181},"after load",[171,57446],{"x":26459,"y":5144,"width":8947,"height":5909,"rx":5901,"fill":177,"opacity":169,"stroke":177,"strokeWidth":168},[150,57448,57449],{"x":1812,"y":40954,"style":22565},"image fills box",[171,57451],{"x":26459,"y":34174,"width":8947,"height":9212,"rx":1579,"fill":167,"opacity":199},[171,57453],{"x":26459,"y":45904,"width":2602,"height":9212,"rx":1579,"fill":167,"opacity":199},[150,57455,57456],{"x":152,"y":19657,"style":10252},"content below stays put — CLS near zero",[50,57458,57460],{"id":57459},"key-technique-ratio-plus-a-definite-width","Key technique: ratio plus a definite width",[14,57462,57463,57465,57466,57469,57470,57473,57474,569,57477,8393,57480,57483,57484,57486,57487,57489,57490,57493],{},[18,57464,38572],{}," only reserves space when the box has a definite size on one axis to compute the other from. For block media that means giving the element ",[18,57467,57468],{},"width: 100%"," (or a track width) so the height resolves from the ratio. The property is ",[18,57471,57472],{},"aspect-ratio: \u003Cwidth> \u002F \u003Cheight>",", an unitless proportion, so ",[18,57475,57476],{},"16 \u002F 9",[18,57478,57479],{},"4 \u002F 3",[18,57481,57482],{},"1 \u002F 1"," all work. If the content has a natural size that conflicts, ",[18,57485,38572],{}," yields to it unless you also constrain the conflicting axis — which is why ",[18,57488,57468],{}," on the media (or ",[18,57491,57492],{},"object-fit: cover"," for raster images) is part of the recipe rather than optional.",[47,57495],{},[50,57497,57499],{"id":57498},"variation-a-dark-mode-placeholder-that-shifts-nothing","Variation: a dark-mode placeholder that shifts nothing",[14,57501,57502,57503,42],{},"While media streams in, the reserved box can show a theme-aware placeholder so the layout reads as intentional rather than blank, and it still never shifts. This pairs naturally with ",[18,57504,57505],{},"prefers-color-scheme",[281,57507,57509],{"className":438,"code":57508,"language":440,"meta":286,"style":286},".media-16x9 {\n  aspect-ratio: 16 \u002F 9;\n  width: 100%;\n  object-fit: cover;\n  background: #eef1f8; \u002F* light placeholder *\u002F\n}\n\n@media (prefers-color-scheme: dark) {\n  .media-16x9 { background: #1c2230; } \u002F* dark placeholder, same box *\u002F\n}\n",[18,57510,57511,57517,57532,57544,57555,57569,57573,57577,57583,57601],{"__ignoreMap":286},[290,57512,57513,57515],{"class":163,"line":292},[290,57514,56961],{"class":303},[290,57516,450],{"class":295},[290,57518,57519,57522,57524,57526,57528,57530],{"class":163,"line":330},[290,57520,57521],{"class":461},"  aspect-ratio",[290,57523,465],{"class":295},[290,57525,9212],{"class":461},[290,57527,1203],{"class":295},[290,57529,247],{"class":461},[290,57531,471],{"class":295},[290,57533,57534,57536,57538,57540,57542],{"class":163,"line":337},[290,57535,17904],{"class":461},[290,57537,465],{"class":295},[290,57539,165],{"class":461},[290,57541,11018],{"class":541},[290,57543,471],{"class":295},[290,57545,57546,57549,57551,57553],{"class":163,"line":364},[290,57547,57548],{"class":461},"  object-fit",[290,57550,465],{"class":295},[290,57552,36366],{"class":461},[290,57554,471],{"class":295},[290,57556,57557,57559,57561,57564,57566],{"class":163,"line":386},[290,57558,1186],{"class":461},[290,57560,465],{"class":295},[290,57562,57563],{"class":461},"#eef1f8",[290,57565,828],{"class":295},[290,57567,57568],{"class":455},"\u002F* light placeholder *\u002F\n",[290,57570,57571],{"class":163,"line":408},[290,57572,620],{"class":295},[290,57574,57575],{"class":163,"line":428},[290,57576,334],{"emptyLinePlaceholder":333},[290,57578,57579,57581],{"class":163,"line":517},[290,57580,874],{"class":541},[290,57582,12872],{"class":295},[290,57584,57585,57587,57589,57591,57593,57596,57598],{"class":163,"line":523},[290,57586,56866],{"class":303},[290,57588,790],{"class":295},[290,57590,1217],{"class":461},[290,57592,465],{"class":295},[290,57594,57595],{"class":461},"#1c2230",[290,57597,6446],{"class":295},[290,57599,57600],{"class":455},"\u002F* dark placeholder, same box *\u002F\n",[290,57602,57603],{"class":163,"line":532},[290,57604,620],{"class":295},[14,57606,57607],{},"Because the box dimensions come from the ratio, changing the placeholder color is purely cosmetic and cannot reintroduce layout shift.",[50,57609,1299],{"id":1298},[14,57611,57612,57614,57615,57617,57618,69,57620,57622,57623,57626,57627,57629],{},[18,57613,38572],{}," is supported in Chrome 88+, Edge 88+, Safari 15+, and Firefox 89+, so it is reliable on every engine shipped since 2021. Browsers also derive a default ratio from an ",[18,57616,33949],{}," element's ",[18,57619,1748],{},[18,57621,2722],{}," attributes (Chrome 79+, Firefox 71+, Safari 14+), which reserves space even with no CSS. For anything older, the ",[18,57624,57625],{},"@supports not (aspect-ratio: 16 \u002F 9)"," block falls back to the ",[18,57628,56694],{}," percentage hack.",[50,57631,1316],{"id":1315},[14,57633,57634,57637],{},[62,57635,57636],{},"How does aspect-ratio prevent cumulative layout shift?","\nIt reserves the correct box height from the ratio and the known width before the media loads, so when the image or video arrives it fills the existing box instead of pushing surrounding content down.",[14,57639,57640,57643,57644,57646,57647,69,57649,57651,57652,57654],{},[62,57641,57642],{},"Do I still need width and height attributes on images?","\nKeep them. Modern browsers derive a default ",[18,57645,38572],{}," from the ",[18,57648,1748],{},[18,57650,2722],{}," attributes automatically, which reserves space even without a CSS rule. The CSS ",[18,57653,38572],{}," property is for elements without those attributes, like videos and iframes.",[14,57656,57657,57660,57661,57663,57664,57666],{},[62,57658,57659],{},"What is the padding-hack fallback for aspect-ratio?","\nWrap the media in a container with ",[18,57662,56694],{}," set to the ratio as a percentage of width, and position the media absolutely to fill it. It reserves height the same way, for engines that predate the ",[18,57665,38572],{}," property.",[14,57668,57669,37757,57672,57674,57675,69,57677,57679],{},[62,57670,57671],{},"Does aspect-ratio work on iframes and video embeds?",[18,57673,38572],{}," applies to any element with a definite width, including ",[18,57676,57340],{},[18,57678,4259],{},". It is the cleanest way to make a YouTube or map embed responsive without the padding wrapper.",[50,57681,1391],{"id":1390},[1393,57683,57684,57689,57696,57701,57706],{},[1396,57685,57686,57688],{},[27,57687,47441],{"href":47440}," — the parent guide for content-driven box sizing.",[1396,57690,57691,57695],{},[27,57692,57694],{"href":57693},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fmin-max-fit-content-explained\u002F","min-content, max-content, fit-content() Explained"," — sizing boxes from their content rather than a ratio.",[1396,57697,57698,57700],{},[27,57699,5002],{"href":5001}," — another technique for eliminating layout shift.",[1396,57702,57703,57705],{},[27,57704,10100],{"href":1426}," — cards whose media keeps a stable ratio across sizes.",[1396,57707,57708,57710],{},[27,57709,8757],{"href":8756}," — cross-area: keeping rendering stable and shift-free under motion.",[1430,57712,47384],{},{"title":286,"searchDepth":330,"depth":330,"links":57714},[57715,57716,57717,57718,57719,57720,57721],{"id":56702,"depth":330,"text":56703},{"id":4202,"depth":330,"text":4203},{"id":57459,"depth":330,"text":57460},{"id":57498,"depth":330,"text":57499},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Use CSS aspect-ratio for responsive images, video, and embeds to reserve space and prevent layout shift, with the padding-hack fallback for older browsers.",{"seoTitle":57724,"datePublished":1447,"dateModified":1447,"faq":57725},"aspect-ratio for Responsive Media & CLS",[57726,57728,57730,57732],{"q":57636,"a":57727},"It reserves the correct box height from the ratio and the known width before the media loads, so when the image or video arrives it fills the existing box instead of pushing surrounding content down.",{"q":57642,"a":57729},"Keep them. Modern browsers derive a default aspect-ratio from the width and height attributes automatically, which reserves space even without a CSS rule. The CSS aspect-ratio property is for elements without those attributes, like videos and iframes.",{"q":57659,"a":57731},"Wrap the media in a container with padding-top set to the ratio as a percentage of width, position the media absolutely to fill it. It reserves height the same way, for engines that predate the aspect-ratio property.",{"q":57671,"a":57733},"Yes. aspect-ratio applies to any element with a definite width, including iframe and video. It is the cleanest way to make a YouTube or map embed responsive without the padding wrapper.","\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Faspect-ratio-for-responsive-media",{"title":56677,"description":57722},"mastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Faspect-ratio-for-responsive-media\u002Findex","bXVum6D73TwjOR_ZIPYUcBSejwvt5ScADL9-0ogYaRE",{"id":57739,"title":57740,"body":57741,"description":59168,"extension":1444,"meta":59169,"navigation":333,"path":59179,"seo":59180,"stem":59181,"__hash__":59182},"content\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Findex.md","Intrinsic Sizing Techniques: Modern CSS Layouts for Responsive UI",{"type":7,"value":57742,"toc":59144},[57743,57746,57752,57756,57779,57815,57817,57821,57832,57838,57908,57917,57919,57923,57929,57985,57989,58005,58148,58150,58154,58160,58164,58179,58307,58311,58316,58444,58448,58453,58569,58571,58575,58578,58582,58588,58654,58658,58675,58759,58763,58769,58780,58782,58784,58789,58873,58879,58881,58885,58965,58967,59022,59024,59026,59073,59075,59077,59083,59101,59110,59112,59114,59141],[10,57744,57740],{"id":57745},"intrinsic-sizing-techniques-modern-css-layouts-for-responsive-ui",[14,57747,57748,57749,57751],{},"Intrinsic sizing allows elements to calculate their own dimensions based on content rather than external constraints, forming the architectural backbone of modern responsive interfaces. When paired with ",[27,57750,11296],{"href":5777},", developers can build truly adaptive components that scale gracefully across viewports, container boundaries, and dynamic data states. This guide breaks down core CSS keywords, provides copy-paste-ready implementation patterns, and demonstrates how to integrate intrinsic values into scalable, spec-compliant architectures.",[2757,57753,57755],{"id":57754},"key-takeaways","Key Takeaways",[1393,57757,57758,57761,57773,57776],{},[1396,57759,57760],{},"Shift from extrinsic (viewport-driven) to intrinsic (content-driven) sizing models for predictable component behavior.",[1396,57762,57763,57764,569,57766,569,57768,8393,57770,57772],{},"Master ",[18,57765,38658],{},[18,57767,17909],{},[18,57769,55390],{},[18,57771,250],{}," for precise dimension control.",[1396,57774,57775],{},"Apply intrinsic values to eliminate layout shifts, optimize micro-interactions, and reduce breakpoint dependency.",[1396,57777,57778],{},"Combine intrinsic keywords with Flexbox, Grid, and CSS containment for performant, container-aware layouts.",[133,57780,140,57782,140,57785,140,57788,140,57791,140,57794,140,57798,140,57800,140,57803,140,57806,140,57809,140,57812],{"viewBox":32969,"role":136,"ariaLabel":57781,"xmlns":138,"style":139},"How min-content, fit-content and max-content resize the same text box against its available space",[142,57783,57784],{},"Intrinsic sizing keywords compared",[146,57786,57787],{},"The same content rendered at min-content, fit-content and max-content widths within a fixed available track.",[150,57789,57790],{"x":152,"y":153,"style":1781},"Same content, three sizing keywords",[163,57792],{"x1":4146,"y1":1788,"x2":25430,"y2":1788,"stroke":167,"strokeWidth":468,"strokeDashArray":57793,"opacity":169},[249,249],[150,57795,57797],{"x":25430,"y":1831,"style":57796},"text-anchor:end;fill:currentColor;font:11px sans-serif;opacity:0.6","available width",[171,57799],{"x":4146,"y":41988,"width":4147,"height":8879,"rx":5901,"fill":177,"opacity":178},[150,57801,38658],{"x":165,"y":57802,"style":183},"105",[171,57804],{"x":4146,"y":40954,"width":152,"height":8879,"rx":5901,"fill":177,"opacity":57805},"0.24",[150,57807,55390],{"x":538,"y":57808,"style":183},"169",[171,57810],{"x":4146,"y":11367,"width":57811,"height":8879,"rx":5901,"fill":177,"opacity":5906},"620",[150,57813,17909],{"x":50885,"y":57814,"style":183},"233",[47,57816],{},[50,57818,57820],{"id":57819},"the-fundamentals-of-intrinsic-vs-extrinsic-sizing","The Fundamentals of Intrinsic vs. Extrinsic Sizing",[14,57822,57823,57824,57826,57827,3041,57829,57831],{},"Extrinsic sizing relies on external references: ",[18,57825,57468],{},", fixed ",[18,57828,674],{},[18,57830,801],{}," values, or viewport units. While predictable in static designs, extrinsic constraints break down when content length varies, typography scales, or components are reused across different contexts. Intrinsic sizing flips this paradigm by calculating dimensions from the inside out, using the natural minimum and maximum widths of child elements as the baseline.",[14,57833,57834,57835,57837],{},"The CSS Box Sizing Module Level 3 specification formalizes this behavior, allowing the rendering engine to compute intrinsic dimensions before applying external constraints. This calculation happens early in the layout pipeline, reducing reflow costs when combined with modern layout models. Understanding this baseline is essential before diving into ",[27,57836,26284],{"href":25340},", where intrinsic dimensions often dictate when and how components adapt.",[281,57839,57841],{"className":438,"code":57840,"language":440,"meta":286,"style":286},"\u002F* Extrinsic vs Intrinsic Comparison *\u002F\n.container-extrinsic {\n  width: 100%; \u002F* Forces element to parent width, ignores content *\u002F\n}\n\n.container-intrinsic {\n  width: fit-content(100%); \u002F* Respects content, caps at parent *\u002F\n}\n",[18,57842,57843,57848,57855,57870,57874,57878,57885,57904],{"__ignoreMap":286},[290,57844,57845],{"class":163,"line":292},[290,57846,57847],{"class":455},"\u002F* Extrinsic vs Intrinsic Comparison *\u002F\n",[290,57849,57850,57853],{"class":163,"line":330},[290,57851,57852],{"class":303},".container-extrinsic",[290,57854,450],{"class":295},[290,57856,57857,57859,57861,57863,57865,57867],{"class":163,"line":337},[290,57858,17904],{"class":461},[290,57860,465],{"class":295},[290,57862,165],{"class":461},[290,57864,11018],{"class":541},[290,57866,828],{"class":295},[290,57868,57869],{"class":455},"\u002F* Forces element to parent width, ignores content *\u002F\n",[290,57871,57872],{"class":163,"line":364},[290,57873,620],{"class":295},[290,57875,57876],{"class":163,"line":386},[290,57877,334],{"emptyLinePlaceholder":333},[290,57879,57880,57883],{"class":163,"line":408},[290,57881,57882],{"class":303},".container-intrinsic",[290,57884,450],{"class":295},[290,57886,57887,57889,57891,57893,57895,57897,57899,57901],{"class":163,"line":428},[290,57888,17904],{"class":461},[290,57890,465],{"class":295},[290,57892,55424],{"class":461},[290,57894,484],{"class":295},[290,57896,165],{"class":461},[290,57898,11018],{"class":541},[290,57900,14061],{"class":295},[290,57902,57903],{"class":455},"\u002F* Respects content, caps at parent *\u002F\n",[290,57905,57906],{"class":163,"line":517},[290,57907,620],{"class":295},[14,57909,57910,57912,57913,57916],{},[62,57911,20503],{}," Always pair intrinsic sizing with ",[18,57914,57915],{},"box-sizing: border-box"," in your reset strategy. Padding and borders are excluded from intrinsic width calculations by default, which can cause unexpected overflow if not normalized.",[47,57918],{},[50,57920,57922],{"id":57921},"core-css-keywords-min-content-max-content-and-fit-content","Core CSS Keywords: min-content, max-content, and fit-content()",[14,57924,57925,57926,42],{},"These three keywords form the intrinsic sizing vocabulary. Their mathematical behavior differs across block and inline axes, and understanding their resolution order prevents layout thrashing. For a focused walkthrough of each keyword's resolution algorithm with edge cases, see ",[27,57927,57928],{"href":57693},"min, max, and fit-content explained",[2250,57930,57931,57944],{},[2253,57932,57933],{},[2256,57934,57935,57938,57941],{},[2259,57936,57937],{},"Keyword",[2259,57939,57940],{},"Behavior",[2259,57942,57943],{},"Use Case",[2269,57945,57946,57958,57970],{},[2256,57947,57948,57952,57955],{},[2274,57949,57950],{},[18,57951,38658],{},[2274,57953,57954],{},"Shrinks to the narrowest possible width without overflow (typically the longest unbreakable word or image).",[2274,57956,57957],{},"Data tables, tag clouds, narrow sidebar widgets.",[2256,57959,57960,57964,57967],{},[2274,57961,57962],{},[18,57963,17909],{},[2274,57965,57966],{},"Expands to the widest possible width assuming no line breaks.",[2274,57968,57969],{},"Hero text, inline navigation, badge containers.",[2256,57971,57972,57976,57982],{},[2274,57973,57974],{},[18,57975,55390],{},[2274,57977,57978,57979,42],{},"Caps expansion at a specified limit while respecting intrinsic minimums. Resolves as ",[18,57980,57981],{},"min(max-content, limit)",[2274,57983,57984],{},"Pill buttons, fluid cards, dynamic form fields.",[2757,57986,57988],{"id":57987},"axis-specific-behavior-writing-modes","Axis-Specific Behavior & Writing Modes",[14,57990,57991,57992,69,57994,57996,57997,24264,58000,3041,58002,58004],{},"Intrinsic sizing respects the ",[18,57993,42414],{},[18,57995,7050],{}," properties. In horizontal text (",[18,57998,57999],{},"writing-mode: horizontal-tb",[18,58001,38658],{},[18,58003,17909],{}," affect the inline axis (width). In vertical layouts, they map to height. Always test axis resolution when building RTL or multi-language interfaces.",[281,58006,58008],{"className":438,"code":58007,"language":440,"meta":286,"style":286},"\u002F* Keyword Demonstration *\u002F\n.intrinsic-demo {\n  display: inline-block;\n  padding: 0.75rem 1rem;\n  background: #f4f4f5;\n  border-radius: 0.5rem;\n}\n\n.demo-min {\n  width: min-content;\n}\n.demo-max {\n  width: max-content;\n}\n.demo-fit {\n  width: fit-content(300px);\n}\n",[18,58009,58010,58015,58022,58032,58048,58059,58071,58075,58079,58086,58096,58100,58107,58117,58121,58128,58144],{"__ignoreMap":286},[290,58011,58012],{"class":163,"line":292},[290,58013,58014],{"class":455},"\u002F* Keyword Demonstration *\u002F\n",[290,58016,58017,58020],{"class":163,"line":330},[290,58018,58019],{"class":303},".intrinsic-demo",[290,58021,450],{"class":295},[290,58023,58024,58026,58028,58030],{"class":163,"line":337},[290,58025,17742],{"class":461},[290,58027,465],{"class":295},[290,58029,17747],{"class":461},[290,58031,471],{"class":295},[290,58033,58034,58036,58038,58040,58042,58044,58046],{"class":163,"line":364},[290,58035,10433],{"class":461},[290,58037,465],{"class":295},[290,58039,823],{"class":461},[290,58041,801],{"class":541},[290,58043,804],{"class":461},[290,58045,801],{"class":541},[290,58047,471],{"class":295},[290,58049,58050,58052,58054,58057],{"class":163,"line":386},[290,58051,1186],{"class":461},[290,58053,465],{"class":295},[290,58055,58056],{"class":461},"#f4f4f5",[290,58058,471],{"class":295},[290,58060,58061,58063,58065,58067,58069],{"class":163,"line":408},[290,58062,1663],{"class":461},[290,58064,465],{"class":295},[290,58066,798],{"class":461},[290,58068,801],{"class":541},[290,58070,471],{"class":295},[290,58072,58073],{"class":163,"line":428},[290,58074,620],{"class":295},[290,58076,58077],{"class":163,"line":517},[290,58078,334],{"emptyLinePlaceholder":333},[290,58080,58081,58084],{"class":163,"line":523},[290,58082,58083],{"class":303},".demo-min",[290,58085,450],{"class":295},[290,58087,58088,58090,58092,58094],{"class":163,"line":532},[290,58089,17904],{"class":461},[290,58091,465],{"class":295},[290,58093,38658],{"class":461},[290,58095,471],{"class":295},[290,58097,58098],{"class":163,"line":551},[290,58099,620],{"class":295},[290,58101,58102,58105],{"class":163,"line":586},[290,58103,58104],{"class":303},".demo-max",[290,58106,450],{"class":295},[290,58108,58109,58111,58113,58115],{"class":163,"line":602},[290,58110,17904],{"class":461},[290,58112,465],{"class":295},[290,58114,17909],{"class":461},[290,58116,471],{"class":295},[290,58118,58119],{"class":163,"line":617},[290,58120,620],{"class":295},[290,58122,58123,58126],{"class":163,"line":623},[290,58124,58125],{"class":303},".demo-fit",[290,58127,450],{"class":295},[290,58129,58130,58132,58134,58136,58138,58140,58142],{"class":163,"line":628},[290,58131,17904],{"class":461},[290,58133,465],{"class":295},[290,58135,55424],{"class":461},[290,58137,484],{"class":295},[290,58139,5397],{"class":461},[290,58141,674],{"class":541},[290,58143,500],{"class":295},[290,58145,58146],{"class":163,"line":634},[290,58147,620],{"class":295},[47,58149],{},[50,58151,58153],{"id":58152},"practical-component-architecture-patterns","Practical Component Architecture Patterns",[14,58155,58156,58157,58159],{},"Intrinsic keywords shine when applied to reusable UI components. By letting content dictate dimensions, you eliminate arbitrary breakpoints and create self-healing layouts. These patterns integrate seamlessly with ",[27,58158,38098],{"href":38097}," to build modular design systems.",[2757,58161,58163],{"id":58162},"fluid-media-containers","Fluid Media Containers",[14,58165,58166,58167,3942,58169,58171,58172,58174,58175,42],{},"Combine ",[18,58168,17909],{},[18,58170,38572],{}," to prevent layout shifts during image loading while allowing natural scaling. The ",[18,58173,38572],{}," property is the linchpin here; for the full pattern set covering responsive images, video, and embeds, see ",[27,58176,58178],{"href":58177},"\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Faspect-ratio-for-responsive-media\u002F","aspect-ratio for responsive media",[281,58180,58182],{"className":438,"code":58181,"language":440,"meta":286,"style":286},".media-container {\n  width: fit-content(100%);\n  max-width: max-content;\n  aspect-ratio: 16 \u002F 9;\n  overflow: hidden;\n  border-radius: 0.5rem;\n}\n\n.media-container img {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n}\n",[18,58183,58184,58191,58207,58217,58231,58241,58253,58257,58261,58269,58281,58293,58303],{"__ignoreMap":286},[290,58185,58186,58189],{"class":163,"line":292},[290,58187,58188],{"class":303},".media-container",[290,58190,450],{"class":295},[290,58192,58193,58195,58197,58199,58201,58203,58205],{"class":163,"line":330},[290,58194,17904],{"class":461},[290,58196,465],{"class":295},[290,58198,55424],{"class":461},[290,58200,484],{"class":295},[290,58202,165],{"class":461},[290,58204,11018],{"class":541},[290,58206,500],{"class":295},[290,58208,58209,58211,58213,58215],{"class":163,"line":337},[290,58210,17916],{"class":461},[290,58212,465],{"class":295},[290,58214,17909],{"class":461},[290,58216,471],{"class":295},[290,58218,58219,58221,58223,58225,58227,58229],{"class":163,"line":364},[290,58220,57521],{"class":461},[290,58222,465],{"class":295},[290,58224,9212],{"class":461},[290,58226,1203],{"class":295},[290,58228,247],{"class":461},[290,58230,471],{"class":295},[290,58232,58233,58235,58237,58239],{"class":163,"line":386},[290,58234,7803],{"class":461},[290,58236,465],{"class":295},[290,58238,7808],{"class":461},[290,58240,471],{"class":295},[290,58242,58243,58245,58247,58249,58251],{"class":163,"line":408},[290,58244,1663],{"class":461},[290,58246,465],{"class":295},[290,58248,798],{"class":461},[290,58250,801],{"class":541},[290,58252,471],{"class":295},[290,58254,58255],{"class":163,"line":428},[290,58256,620],{"class":295},[290,58258,58259],{"class":163,"line":517},[290,58260,334],{"emptyLinePlaceholder":333},[290,58262,58263,58265,58267],{"class":163,"line":523},[290,58264,58188],{"class":303},[290,58266,36315],{"class":299},[290,58268,450],{"class":295},[290,58270,58271,58273,58275,58277,58279],{"class":163,"line":532},[290,58272,17904],{"class":461},[290,58274,465],{"class":295},[290,58276,165],{"class":461},[290,58278,11018],{"class":541},[290,58280,471],{"class":295},[290,58282,58283,58285,58287,58289,58291],{"class":163,"line":551},[290,58284,18162],{"class":461},[290,58286,465],{"class":295},[290,58288,165],{"class":461},[290,58290,11018],{"class":541},[290,58292,471],{"class":295},[290,58294,58295,58297,58299,58301],{"class":163,"line":586},[290,58296,57548],{"class":461},[290,58298,465],{"class":295},[290,58300,36366],{"class":461},[290,58302,471],{"class":295},[290,58304,58305],{"class":163,"line":602},[290,58306,620],{"class":295},[2757,58308,58310],{"id":58309},"self-sizing-data-tables","Self-Sizing Data Tables",[14,58312,1499,58313,58315],{},[18,58314,38658],{}," to force columns to respect their longest cell, then enable horizontal scrolling on overflow.",[281,58317,58319],{"className":438,"code":58318,"language":440,"meta":286,"style":286},".data-table {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(min-content, 1fr));\n  overflow-x: auto;\n  contain: layout style; \u002F* Prevents layout thrashing *\u002F\n}\n\n.data-table__cell {\n  white-space: nowrap;\n  min-width: max-content;\n  padding: 0.5rem;\n}\n",[18,58320,58321,58328,58338,58366,58377,58390,58394,58398,58405,58417,58428,58440],{"__ignoreMap":286},[290,58322,58323,58326],{"class":163,"line":292},[290,58324,58325],{"class":303},".data-table",[290,58327,450],{"class":295},[290,58329,58330,58332,58334,58336],{"class":163,"line":330},[290,58331,17742],{"class":461},[290,58333,465],{"class":295},[290,58335,9147],{"class":461},[290,58337,471],{"class":295},[290,58339,58340,58342,58344,58346,58348,58350,58352,58354,58356,58358,58360,58362,58364],{"class":163,"line":337},[290,58341,47811],{"class":461},[290,58343,465],{"class":295},[290,58345,44542],{"class":461},[290,58347,484],{"class":295},[290,58349,44547],{"class":461},[290,58351,569],{"class":295},[290,58353,44552],{"class":461},[290,58355,484],{"class":295},[290,58357,38658],{"class":461},[290,58359,569],{"class":295},[290,58361,468],{"class":461},[290,58363,11964],{"class":541},[290,58365,11616],{"class":295},[290,58367,58368,58371,58373,58375],{"class":163,"line":364},[290,58369,58370],{"class":461},"  overflow-x",[290,58372,465],{"class":295},[290,58374,250],{"class":461},[290,58376,471],{"class":295},[290,58378,58379,58381,58383,58385,58387],{"class":163,"line":386},[290,58380,40253],{"class":461},[290,58382,41070],{"class":295},[290,58384,1430],{"class":461},[290,58386,828],{"class":295},[290,58388,58389],{"class":455},"\u002F* Prevents layout thrashing *\u002F\n",[290,58391,58392],{"class":163,"line":408},[290,58393,620],{"class":295},[290,58395,58396],{"class":163,"line":428},[290,58397,334],{"emptyLinePlaceholder":333},[290,58399,58400,58403],{"class":163,"line":517},[290,58401,58402],{"class":303},".data-table__cell",[290,58404,450],{"class":295},[290,58406,58407,58410,58412,58415],{"class":163,"line":523},[290,58408,58409],{"class":461},"  white-space",[290,58411,465],{"class":295},[290,58413,58414],{"class":461},"nowrap",[290,58416,471],{"class":295},[290,58418,58419,58422,58424,58426],{"class":163,"line":532},[290,58420,58421],{"class":461},"  min-width",[290,58423,465],{"class":295},[290,58425,17909],{"class":461},[290,58427,471],{"class":295},[290,58429,58430,58432,58434,58436,58438],{"class":163,"line":551},[290,58431,10433],{"class":461},[290,58433,465],{"class":295},[290,58435,798],{"class":461},[290,58437,801],{"class":541},[290,58439,471],{"class":295},[290,58441,58442],{"class":163,"line":586},[290,58443,620],{"class":295},[2757,58445,58447],{"id":58446},"dynamic-pill-buttons-tags","Dynamic Pill Buttons & Tags",[14,58449,58450,58452],{},[18,58451,55390],{}," ensures buttons never stretch awkwardly but cap at a readable maximum.",[281,58454,58456],{"className":438,"code":58455,"language":440,"meta":286,"style":286},".pill-tag {\n  display: inline-flex;\n  align-items: center;\n  width: fit-content(180px);\n  padding: 0.375rem 0.75rem;\n  border-radius: 999px;\n  background: var(--color-surface);\n  border: 1px solid var(--color-border);\n}\n",[18,58457,58458,58465,58475,58485,58501,58517,58529,58544,58565],{"__ignoreMap":286},[290,58459,58460,58463],{"class":163,"line":292},[290,58461,58462],{"class":303},".pill-tag",[290,58464,450],{"class":295},[290,58466,58467,58469,58471,58473],{"class":163,"line":330},[290,58468,17742],{"class":461},[290,58470,465],{"class":295},[290,58472,27407],{"class":461},[290,58474,471],{"class":295},[290,58476,58477,58479,58481,58483],{"class":163,"line":337},[290,58478,30215],{"class":461},[290,58480,465],{"class":295},[290,58482,9157],{"class":461},[290,58484,471],{"class":295},[290,58486,58487,58489,58491,58493,58495,58497,58499],{"class":163,"line":364},[290,58488,17904],{"class":461},[290,58490,465],{"class":295},[290,58492,55424],{"class":461},[290,58494,484],{"class":295},[290,58496,2602],{"class":461},[290,58498,674],{"class":541},[290,58500,500],{"class":295},[290,58502,58503,58505,58507,58509,58511,58513,58515],{"class":163,"line":386},[290,58504,10433],{"class":461},[290,58506,465],{"class":295},[290,58508,12724],{"class":461},[290,58510,801],{"class":541},[290,58512,50200],{"class":461},[290,58514,801],{"class":541},[290,58516,471],{"class":295},[290,58518,58519,58521,58523,58525,58527],{"class":163,"line":408},[290,58520,1663],{"class":461},[290,58522,465],{"class":295},[290,58524,10465],{"class":461},[290,58526,674],{"class":541},[290,58528,471],{"class":295},[290,58530,58531,58533,58535,58537,58539,58542],{"class":163,"line":428},[290,58532,1186],{"class":461},[290,58534,465],{"class":295},[290,58536,1622],{"class":461},[290,58538,484],{"class":295},[290,58540,58541],{"class":1561},"--color-surface",[290,58543,500],{"class":295},[290,58545,58546,58548,58550,58552,58554,58556,58558,58560,58563],{"class":163,"line":517},[290,58547,1948],{"class":461},[290,58549,465],{"class":295},[290,58551,468],{"class":461},[290,58553,674],{"class":541},[290,58555,852],{"class":461},[290,58557,1635],{"class":461},[290,58559,484],{"class":295},[290,58561,58562],{"class":1561},"--color-border",[290,58564,500],{"class":295},[290,58566,58567],{"class":163,"line":523},[290,58568,620],{"class":295},[47,58570],{},[50,58572,58574],{"id":58573},"advanced-integration-with-grid-and-flexbox","Advanced Integration with Grid and Flexbox",[14,58576,58577],{},"Intrinsic values resolve differently depending on the layout model. Mastering these interactions prevents sizing conflicts in complex architectures.",[2757,58579,58581],{"id":58580},"grid-track-sizing","Grid Track Sizing",[14,58583,58584,58587],{},[18,58585,58586],{},"minmax(min-content, 1fr)"," creates fluid tracks that shrink to content but expand to fill available space.",[281,58589,58591],{"className":438,"code":58590,"language":440,"meta":286,"style":286},".responsive-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(min-content, 1fr));\n  gap: 1rem;\n}\n",[18,58592,58593,58600,58610,58638,58650],{"__ignoreMap":286},[290,58594,58595,58598],{"class":163,"line":292},[290,58596,58597],{"class":303},".responsive-grid",[290,58599,450],{"class":295},[290,58601,58602,58604,58606,58608],{"class":163,"line":330},[290,58603,17742],{"class":461},[290,58605,465],{"class":295},[290,58607,9147],{"class":461},[290,58609,471],{"class":295},[290,58611,58612,58614,58616,58618,58620,58622,58624,58626,58628,58630,58632,58634,58636],{"class":163,"line":337},[290,58613,47811],{"class":461},[290,58615,465],{"class":295},[290,58617,44542],{"class":461},[290,58619,484],{"class":295},[290,58621,47699],{"class":461},[290,58623,569],{"class":295},[290,58625,44552],{"class":461},[290,58627,484],{"class":295},[290,58629,38658],{"class":461},[290,58631,569],{"class":295},[290,58633,468],{"class":461},[290,58635,11964],{"class":541},[290,58637,11616],{"class":295},[290,58639,58640,58642,58644,58646,58648],{"class":163,"line":364},[290,58641,26819],{"class":461},[290,58643,465],{"class":295},[290,58645,468],{"class":461},[290,58647,801],{"class":541},[290,58649,471],{"class":295},[290,58651,58652],{"class":163,"line":386},[290,58653,620],{"class":295},[2757,58655,58657],{"id":58656},"flexbox-conflicts","Flexbox Conflicts",[14,58659,58660,58661,58664,58665,58667,58668,58671,58672,42],{},"In Flexbox, ",[18,58662,58663],{},"flex-basis"," overrides intrinsic width unless explicitly set to ",[18,58666,250],{},". When ",[18,58669,58670],{},"flex-basis: auto",", the browser uses the element's intrinsic size as the starting point before distributing remaining space via ",[18,58673,58674],{},"flex-grow",[281,58676,58678],{"className":438,"code":58677,"language":440,"meta":286,"style":286},".flex-row {\n  display: flex;\n  gap: 1rem;\n}\n\n.flex-item {\n  flex: 1 1 auto; \u002F* Uses intrinsic width as basis *\u002F\n  min-width: min-content; \u002F* Prevents text overflow *\u002F\n}\n",[18,58679,58680,58687,58697,58709,58713,58717,58724,58742,58755],{"__ignoreMap":286},[290,58681,58682,58685],{"class":163,"line":292},[290,58683,58684],{"class":303},".flex-row",[290,58686,450],{"class":295},[290,58688,58689,58691,58693,58695],{"class":163,"line":330},[290,58690,17742],{"class":461},[290,58692,465],{"class":295},[290,58694,9055],{"class":461},[290,58696,471],{"class":295},[290,58698,58699,58701,58703,58705,58707],{"class":163,"line":337},[290,58700,26819],{"class":461},[290,58702,465],{"class":295},[290,58704,468],{"class":461},[290,58706,801],{"class":541},[290,58708,471],{"class":295},[290,58710,58711],{"class":163,"line":364},[290,58712,620],{"class":295},[290,58714,58715],{"class":163,"line":386},[290,58716,334],{"emptyLinePlaceholder":333},[290,58718,58719,58722],{"class":163,"line":408},[290,58720,58721],{"class":303},".flex-item",[290,58723,450],{"class":295},[290,58725,58726,58729,58731,58733,58735,58737,58739],{"class":163,"line":428},[290,58727,58728],{"class":461},"  flex",[290,58730,465],{"class":295},[290,58732,468],{"class":461},[290,58734,804],{"class":461},[290,58736,18149],{"class":461},[290,58738,828],{"class":295},[290,58740,58741],{"class":455},"\u002F* Uses intrinsic width as basis *\u002F\n",[290,58743,58744,58746,58748,58750,58752],{"class":163,"line":517},[290,58745,58421],{"class":461},[290,58747,465],{"class":295},[290,58749,38658],{"class":461},[290,58751,828],{"class":295},[290,58753,58754],{"class":455},"\u002F* Prevents text overflow *\u002F\n",[290,58756,58757],{"class":163,"line":523},[290,58758,620],{"class":295},[2757,58760,58762],{"id":58761},"subgrid-alignment","Subgrid Alignment",[14,58764,58765,58766,58768],{},"When using ",[18,58767,47601],{},", intrinsic keywords on child elements propagate to the parent track, enabling deeply nested layouts that share a single sizing context.",[14,58770,58771,58774,58775,3942,58777,58779],{},[62,58772,58773],{},"Performance Warning:"," Intrinsic calculations require the browser to measure content before finalizing layout. On deeply nested, highly dynamic lists (e.g., infinite scroll feeds), pair ",[18,58776,55390],{},[18,58778,53368],{}," to defer off-screen intrinsic calculations.",[47,58781],{},[50,58783,15880],{"id":15879},[14,58785,58786,58787,723],{},"Intrinsic sizing is fully supported in all modern browsers (Chrome 105+, Firefox 103+, Safari 15.4+). For legacy environments (IE11, pre-Chromium Edge), implement progressive enhancement using ",[18,58788,2086],{},[281,58790,58792],{"className":438,"code":58791,"language":440,"meta":286,"style":286},".component {\n  width: 100%; \u002F* Fallback for legacy browsers *\u002F\n}\n\n@supports (width: fit-content(100%)) {\n  .component {\n    width: fit-content(100%);\n  }\n}\n",[18,58793,58794,58800,58815,58819,58823,58843,58849,58865,58869],{"__ignoreMap":286},[290,58795,58796,58798],{"class":163,"line":292},[290,58797,55852],{"class":303},[290,58799,450],{"class":295},[290,58801,58802,58804,58806,58808,58810,58812],{"class":163,"line":330},[290,58803,17904],{"class":461},[290,58805,465],{"class":295},[290,58807,165],{"class":461},[290,58809,11018],{"class":541},[290,58811,828],{"class":295},[290,58813,58814],{"class":455},"\u002F* Fallback for legacy browsers *\u002F\n",[290,58816,58817],{"class":163,"line":337},[290,58818,620],{"class":295},[290,58820,58821],{"class":163,"line":364},[290,58822,334],{"emptyLinePlaceholder":333},[290,58824,58825,58827,58829,58831,58833,58835,58837,58839,58841],{"class":163,"line":386},[290,58826,2086],{"class":541},[290,58828,3595],{"class":295},[290,58830,1748],{"class":461},[290,58832,465],{"class":295},[290,58834,55424],{"class":461},[290,58836,484],{"class":295},[290,58838,165],{"class":461},[290,58840,11018],{"class":541},[290,58842,14796],{"class":295},[290,58844,58845,58847],{"class":163,"line":408},[290,58846,42282],{"class":303},[290,58848,450],{"class":295},[290,58850,58851,58853,58855,58857,58859,58861,58863],{"class":163,"line":428},[290,58852,9090],{"class":461},[290,58854,465],{"class":295},[290,58856,55424],{"class":461},[290,58858,484],{"class":295},[290,58860,165],{"class":461},[290,58862,11018],{"class":541},[290,58864,500],{"class":295},[290,58866,58867],{"class":163,"line":517},[290,58868,771],{"class":295},[290,58870,58871],{"class":163,"line":523},[290,58872,620],{"class":295},[14,58874,58875,58876,58878],{},"Always test with ",[18,58877,2584],{}," and high-contrast modes, as intrinsic sizing can alter focus ring placement and hit areas.",[47,58880],{},[50,58882,58884],{"id":58883},"common-issues-devtools-debugging-workflow","Common Issues & DevTools Debugging Workflow",[2250,58886,58887,58897],{},[2253,58888,58889],{},[2256,58890,58891,58893,58895],{},[2259,58892,2338],{},[2259,58894,3876],{},[2259,58896,8563],{},[2269,58898,58899,58918,58935,58952],{},[2256,58900,58901,58904,58909],{},[2274,58902,58903],{},"Unexpected horizontal scrollbars",[2274,58905,58906,58908],{},[18,58907,17909],{}," exceeds viewport width",[2274,58910,58911,58912,58915,58916],{},"Wrap in ",[18,58913,58914],{},"overflow-x: auto"," or cap with ",[18,58917,55390],{},[2256,58919,58920,58923,58926],{},[2274,58921,58922],{},"Layout shifts on dynamic injection",[2274,58924,58925],{},"Intrinsic calc resolves after paint",[2274,58927,1499,58928,3041,58930,58932,58933],{},[18,58929,37680],{},[18,58931,26114],{}," placeholders + ",[18,58934,15861],{},[2256,58936,58937,58940,58945],{},[2274,58938,58939],{},"Flex track unpredictability",[2274,58941,38582,58942,58944],{},[18,58943,58663],{}," and intrinsic keywords",[2274,58946,25110,58947,58949,58950],{},[18,58948,58670],{}," and explicitly define ",[18,58951,26114],{},[2256,58953,58954,58957,58960],{},[2274,58955,58956],{},"Performance degradation on large lists",[2274,58958,58959],{},"Repeated intrinsic measurements",[2274,58961,29271,58962,58964],{},[18,58963,40216],{}," and virtualize DOM nodes",[2757,58966,38361],{"id":38360},[3017,58968,58969,58980,58991,59004,59014],{},[1396,58970,58971,58972,16198,58974,58976,58977,58979],{},"Open ",[62,58973,38421],{},[62,58975,13462],{}," panel (Chrome\u002FEdge) or ",[62,58978,13462],{}," tab (Firefox).",[1396,58981,33126,58982,58985,58986,3041,58988,58990],{},[62,58983,58984],{},"Show intrinsic sizing"," to visualize ",[18,58987,38658],{},[18,58989,17909],{}," boundaries.",[1396,58992,58993,58994,16198,58996,3041,58998,59000,59001,59003],{},"Inspect ",[18,58995,13407],{},[18,58997,1748],{},[18,58999,2722],{}," to verify resolution order (",[18,59002,55390],{}," caps correctly).",[1396,59005,59006,59007,59009,59010,59013],{},"Use the ",[62,59008,3024],{}," panel → ",[62,59011,59012],{},"Highlight layout shifts"," to catch CLS caused by late intrinsic resolution.",[1396,59015,25028,59016,59019,59020,42],{},[18,59017,59018],{},"outline: 1px solid red"," to parent containers to visually track overflow before applying ",[18,59021,1731],{},[47,59023],{},[50,59025,13480],{"id":13479},[1393,59027,59028,59043,59054,59065],{},[1396,59029,59030,59035,59036,569,59038,569,59040,59042],{},[27,59031,59034],{"href":59032,"rel":59033},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-sizing-3\u002F",[13489],"CSS Sizing Module Level 3"," – Defines ",[18,59037,38658],{},[18,59039,17909],{},[18,59041,55390],{},", and intrinsic sizing algorithms.",[1396,59044,59045,59050,59051,59053],{},[27,59046,59049],{"href":59047,"rel":59048},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-flexbox-1\u002F",[13489],"CSS Flexible Box Layout Module Level 1"," – Details ",[18,59052,58663],{}," resolution with intrinsic values.",[1396,59055,59056,59061,59062,59064],{},[27,59057,59060],{"href":59058,"rel":59059},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-grid-2\u002F",[13489],"CSS Grid Layout Module Level 2"," – Explains ",[18,59063,46241],{}," intrinsic track sizing and subgrid propagation.",[1396,59066,59067,59072],{},[27,59068,59071],{"href":59069,"rel":59070},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-contain-2\u002F",[13489],"CSS Containment Module Level 2"," – Performance optimization guidelines for intrinsic-heavy layouts.",[47,59074],{},[50,59076,1316],{"id":1315},[14,59078,59079,59082],{},[62,59080,59081],{},"When should I use intrinsic sizing over percentage-based widths?","\nUse intrinsic sizing when component dimensions should be dictated by content length, typography, or media assets rather than arbitrary viewport percentages. It prevents awkward whitespace and improves readability in dynamic data scenarios.",[14,59084,59085,59090,59091,59093,59094,59097,59098,59100],{},[62,59086,5714,59087,59089],{},[18,59088,55390],{}," work the same way across all layout models?","\nNo. In Flexbox, it behaves similarly to ",[18,59092,17909],{}," with a cap. In Grid, it resolves to ",[18,59095,59096],{},"min(max-content, specified limit)",". Always test axis-specific behavior and consider ",[18,59099,42414],{}," implications.",[14,59102,59103,59106,59107,59109],{},[62,59104,59105],{},"How do intrinsic values impact Core Web Vitals?","\nProperly implemented intrinsic sizing reduces Cumulative Layout Shift (CLS) by allowing elements to reserve accurate space during initial render. However, overuse on large datasets can increase layout computation time, potentially impacting Interaction to Next Paint (INP). Mitigate with ",[18,59108,40216],{}," and virtualization.",[47,59111],{},[50,59113,1391],{"id":1390},[1393,59115,59116,59121,59126,59131,59136],{},[1396,59117,59118,59120],{},[27,59119,11296],{"href":5777}," — the parent guide on building layouts without breakpoints.",[1396,59122,59123,59125],{},[27,59124,57928],{"href":57693}," — the resolution algorithm behind each intrinsic keyword.",[1396,59127,59128,59130],{},[27,59129,58178],{"href":58177}," — reserve space for images and video without layout shift.",[1396,59132,59133,59135],{},[27,59134,38098],{"href":38097}," — apply intrinsic sizing inside container-aware components.",[1396,59137,59138,59140],{},[27,59139,33331],{"href":8756}," — cross-area: avoid layout-triggering properties the way intrinsic sizing avoids reflow.",[1430,59142,59143],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":286,"searchDepth":330,"depth":330,"links":59145},[59146,59147,59148,59151,59156,59161,59162,59165,59166,59167],{"id":57754,"depth":337,"text":57755},{"id":57819,"depth":330,"text":57820},{"id":57921,"depth":330,"text":57922,"children":59149},[59150],{"id":57987,"depth":337,"text":57988},{"id":58152,"depth":330,"text":58153,"children":59152},[59153,59154,59155],{"id":58162,"depth":337,"text":58163},{"id":58309,"depth":337,"text":58310},{"id":58446,"depth":337,"text":58447},{"id":58573,"depth":330,"text":58574,"children":59157},[59158,59159,59160],{"id":58580,"depth":337,"text":58581},{"id":58656,"depth":337,"text":58657},{"id":58761,"depth":337,"text":58762},{"id":15879,"depth":330,"text":15880},{"id":58883,"depth":330,"text":58884,"children":59163},[59164],{"id":38360,"depth":337,"text":38361},{"id":13479,"depth":330,"text":13480},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"CSS intrinsic sizing techniques: min-content, max-content, fit-content, and layout patterns for responsive UI components that adapt without breakpoints.",{"seoTitle":59170,"datePublished":1447,"dateModified":1447,"faq":59171},"Intrinsic Sizing Techniques in Modern CSS",[59172,59174,59177],{"q":59081,"a":59173},"Use intrinsic sizing when component dimensions should be dictated by content length, typography, or media assets rather than arbitrary viewport percentages. It prevents awkward whitespace and improves readability in dynamic data scenarios.",{"q":59175,"a":59176},"Does fit-content() work the same way across all layout models?","No. In Flexbox it behaves similarly to max-content with a cap, while in Grid it resolves to min(max-content, specified limit). Always test axis-specific behavior and consider writing-mode implications.",{"q":59105,"a":59178},"Properly implemented intrinsic sizing reduces Cumulative Layout Shift by reserving accurate space during render, but overuse on large datasets can raise layout computation time and hurt Interaction to Next Paint. Mitigate with contain: layout and virtualization.","\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques",{"title":57740,"description":59168},"mastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Findex","uiNIBYgXO5o8LAXhKcciKB-vmJ3KAiATX-IqzYd76j0",{"id":59184,"title":59185,"body":59186,"description":60290,"extension":1444,"meta":60291,"navigation":333,"path":60302,"seo":60303,"stem":60304,"__hash__":60305},"content\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fmin-max-fit-content-explained\u002Findex.md","min-content, max-content, and fit-content() Explained",{"type":7,"value":59187,"toc":60281},[59188,59191,59205,59209,59215,59222,59231,59233,59237,59240,59285,59288,59914,59929,59965,59969,59979,60043,60046,60048,60052,60064,60179,60191,60193,60206,60208,60219,60233,60241,60249,60251,60279],[10,59189,59185],{"id":59190},"min-content-max-content-and-fit-content-explained",[14,59192,59193,59194,569,59196,8393,59198,59200,59201,51247,59203,42],{},"CSS sizing keywords like ",[18,59195,38658],{},[18,59197,17909],{},[18,59199,55390],{}," describe a box's width in terms of what it contains rather than a fixed length or a percentage of its parent. They are the vocabulary of intrinsic sizing, and knowing exactly what each one computes turns a category of fiddly layout problems — auto-width buttons, shrink-to-fit sidebars, label columns that never wrap awkwardly — into one-line declarations. This guide explains what each keyword evaluates to and where it is the right tool. It is part of ",[27,59202,47441],{"href":47440},[27,59204,11296],{"href":5777},[50,59206,59208],{"id":59207},"why-intrinsic-keywords-instead-of-fixed-widths-or-percentages","Why intrinsic keywords instead of fixed widths or percentages",[14,59210,59211,59212,59214],{},"The common alternatives each have a failure mode. A fixed ",[18,59213,1748],{}," ignores the content and either clips it or leaves dead space. A percentage width tracks the parent but is blind to what is inside the box, so a tiny label and a paragraph get the same width. The intrinsic keywords flip the relationship: they ask the content how much room it needs and size the box from the answer, which is precisely what you want for elements whose size should follow their text.",[14,59216,59217,59218,59221],{},"There is no JavaScript measurement involved, which matters for both performance and correctness. A common pattern people reach for is reading ",[18,59219,59220],{},"scrollWidth"," in script to size an element to its content; that forces a synchronous layout and runs after paint, so it can flash. The intrinsic keywords are resolved by the engine during layout itself, so the box is correct on first paint with no measurement pass and no flash. Accessibility benefits too: because the box sizes to real content, text reflows naturally under zoom instead of being trapped at a scripted pixel width that ignores the larger glyphs.",[14,59223,59224,59225,59227,59228,59230],{},"The cost is that these keywords can produce surprising widths if you do not know their definitions — a ",[18,59226,38658],{}," box can collapse to one long word, and a ",[18,59229,17909],{}," box can blow past its parent. The fix is understanding, not avoidance.",[47,59232],{},[50,59234,59236],{"id":59235},"what-each-keyword-computes","What each keyword computes",[14,59238,59239],{},"The three keywords answer three different questions about the same content:",[1393,59241,59242,59251,59259],{},[1396,59243,59244,59246,59247,59250],{},[18,59245,38658],{}," is the ",[62,59248,59249],{},"smallest"," width the box can take without its content overflowing. Practically, it is the width of the widest unbreakable unit — the longest word, the longest URL, the widest replaced element. Text wraps at every opportunity.",[1396,59252,59253,59255,59256,59258],{},[18,59254,17909],{}," is the box's ",[62,59257,53786],{}," width: how wide it would be if nothing ever wrapped, as on an infinitely wide canvas. For a paragraph that is the full text on one line.",[1396,59260,59261,59264,59265,59267,59268,59271,59272,59274,59275,59277,59278,59281,59282,42],{},[18,59262,59263],{},"fit-content(limit)"," is a hybrid: it behaves like ",[18,59266,17909],{}," until the content would exceed ",[18,59269,59270],{},"limit",", then it caps at ",[18,59273,59270],{}," and lets the content wrap. The bare ",[18,59276,55424],{}," keyword is equivalent to ",[18,59279,59280],{},"fit-content(stretch)",", clamping the preferred size to the available space. Formally it is ",[18,59283,59284],{},"min(max-content, max(min-content, available-or-limit))",[14,59286,59287],{},"The implementation below renders the same paragraph in four boxes so you can see the differences directly.",[281,59289,59291],{"className":283,"code":59290,"language":285,"meta":286,"style":286},"\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\u003Cstyle>\n  body { font-family: system-ui, sans-serif; margin: 2rem; max-width: 40rem; }\n\n  .box {\n    border: 2px solid #7aa2ff;\n    padding: 0.5rem;\n    margin-bottom: 1.5rem;\n    background: #7aa2ff14;\n  }\n  .label { font: 600 0.75rem ui-monospace, monospace; color: #555; margin: 0 0 0.25rem; }\n  .box p { margin: 0; line-height: 1.4; }\n\n  \u002F* Collapses to the widest single word; everything else wraps *\u002F\n  .min  { width: min-content; }\n\n  \u002F* Preferred width: the whole sentence on one line, even past the parent *\u002F\n  .max  { width: max-content; }\n\n  \u002F* max-content until it would exceed 18rem, then caps and wraps *\u002F\n  .fit  { width: fit-content(18rem); }\n\n  \u002F* Bare keyword: shrink-wrap, but never exceed the available space *\u002F\n  .fitbare { width: fit-content; }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv class=\"box min\">\n    \u003Cp class=\"label\">width: min-content\u003C\u002Fp>\n    \u003Cp>Intrinsic sizing keywords describe width by content.\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\n  \u003Cdiv class=\"box max\">\n    \u003Cp class=\"label\">width: max-content\u003C\u002Fp>\n    \u003Cp>Intrinsic sizing keywords describe width by content.\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\n  \u003Cdiv class=\"box fit\">\n    \u003Cp class=\"label\">width: fit-content(18rem)\u003C\u002Fp>\n    \u003Cp>Intrinsic sizing keywords describe width by content.\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\n  \u003Cdiv class=\"box fitbare\">\n    \u003Cp class=\"label\">width: fit-content\u003C\u002Fp>\n    \u003Cp>Intrinsic sizing keywords describe width by content.\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,59292,59293,59303,59317,59325,59339,59359,59367,59405,59409,59416,59433,59445,59458,59468,59472,59519,59543,59547,59552,59567,59571,59576,59591,59595,59600,59621,59625,59630,59645,59653,59661,59669,59684,59703,59716,59724,59728,59743,59762,59774,59782,59786,59801,59820,59832,59840,59844,59859,59878,59890,59898,59906],{"__ignoreMap":286},[290,59294,59295,59297,59299,59301],{"class":163,"line":292},[290,59296,8982],{"class":295},[290,59298,35913],{"class":299},[290,59300,8988],{"class":303},[290,59302,327],{"class":295},[290,59304,59305,59307,59309,59311,59313,59315],{"class":163,"line":330},[290,59306,296],{"class":295},[290,59308,285],{"class":299},[290,59310,8999],{"class":303},[290,59312,307],{"class":295},[290,59314,9004],{"class":310},[290,59316,327],{"class":295},[290,59318,59319,59321,59323],{"class":163,"line":337},[290,59320,296],{"class":295},[290,59322,9013],{"class":299},[290,59324,327],{"class":295},[290,59326,59327,59329,59331,59333,59335,59337],{"class":163,"line":364},[290,59328,296],{"class":295},[290,59330,9022],{"class":299},[290,59332,9025],{"class":303},[290,59334,307],{"class":295},[290,59336,9030],{"class":310},[290,59338,327],{"class":295},[290,59340,59341,59343,59345,59347,59349,59351,59353,59355,59357],{"class":163,"line":386},[290,59342,296],{"class":295},[290,59344,9022],{"class":299},[290,59346,35962],{"class":303},[290,59348,307],{"class":295},[290,59350,35967],{"class":310},[290,59352,35970],{"class":303},[290,59354,307],{"class":295},[290,59356,35975],{"class":310},[290,59358,327],{"class":295},[290,59360,59361,59363,59365],{"class":163,"line":408},[290,59362,296],{"class":295},[290,59364,1430],{"class":299},[290,59366,327],{"class":295},[290,59368,59369,59371,59373,59375,59377,59379,59381,59383,59385,59387,59389,59391,59393,59395,59397,59399,59401,59403],{"class":163,"line":428},[290,59370,44444],{"class":299},[290,59372,790],{"class":295},[290,59374,50110],{"class":461},[290,59376,465],{"class":295},[290,59378,50115],{"class":461},[290,59380,569],{"class":295},[290,59382,44479],{"class":461},[290,59384,828],{"class":295},[290,59386,2725],{"class":461},[290,59388,465],{"class":295},[290,59390,194],{"class":461},[290,59392,801],{"class":541},[290,59394,828],{"class":295},[290,59396,41191],{"class":461},[290,59398,465],{"class":295},[290,59400,4146],{"class":461},[290,59402,801],{"class":541},[290,59404,809],{"class":295},[290,59406,59407],{"class":163,"line":517},[290,59408,334],{"emptyLinePlaceholder":333},[290,59410,59411,59414],{"class":163,"line":523},[290,59412,59413],{"class":303},"  .box",[290,59415,450],{"class":295},[290,59417,59418,59420,59422,59424,59426,59428,59431],{"class":163,"line":532},[290,59419,21285],{"class":461},[290,59421,465],{"class":295},[290,59423,194],{"class":461},[290,59425,674],{"class":541},[290,59427,852],{"class":461},[290,59429,59430],{"class":461}," #7aa2ff",[290,59432,471],{"class":295},[290,59434,59435,59437,59439,59441,59443],{"class":163,"line":551},[290,59436,36040],{"class":461},[290,59438,465],{"class":295},[290,59440,798],{"class":461},[290,59442,801],{"class":541},[290,59444,471],{"class":295},[290,59446,59447,59450,59452,59454,59456],{"class":163,"line":586},[290,59448,59449],{"class":461},"    margin-bottom",[290,59451,465],{"class":295},[290,59453,168],{"class":461},[290,59455,801],{"class":541},[290,59457,471],{"class":295},[290,59459,59460,59462,59464,59466],{"class":163,"line":602},[290,59461,9124],{"class":461},[290,59463,465],{"class":295},[290,59465,56923],{"class":461},[290,59467,471],{"class":295},[290,59469,59470],{"class":163,"line":617},[290,59471,771],{"class":295},[290,59473,59474,59476,59478,59480,59482,59484,59486,59488,59490,59492,59494,59496,59498,59500,59502,59504,59506,59508,59510,59512,59515,59517],{"class":163,"line":623},[290,59475,50189],{"class":303},[290,59477,790],{"class":295},[290,59479,46459],{"class":461},[290,59481,465],{"class":295},[290,59483,4176],{"class":461},[290,59485,50200],{"class":461},[290,59487,801],{"class":541},[290,59489,50205],{"class":461},[290,59491,569],{"class":295},[290,59493,50210],{"class":461},[290,59495,828],{"class":295},[290,59497,9133],{"class":461},[290,59499,465],{"class":295},[290,59501,15996],{"class":461},[290,59503,828],{"class":295},[290,59505,2725],{"class":461},[290,59507,465],{"class":295},[290,59509,487],{"class":461},[290,59511,1198],{"class":461},[290,59513,59514],{"class":461}," 0.25",[290,59516,801],{"class":541},[290,59518,809],{"class":295},[290,59520,59521,59523,59525,59527,59529,59531,59533,59535,59537,59539,59541],{"class":163,"line":628},[290,59522,59413],{"class":303},[290,59524,39281],{"class":299},[290,59526,790],{"class":295},[290,59528,2725],{"class":461},[290,59530,465],{"class":295},[290,59532,487],{"class":461},[290,59534,828],{"class":295},[290,59536,50124],{"class":461},[290,59538,465],{"class":295},[290,59540,1824],{"class":461},[290,59542,809],{"class":295},[290,59544,59545],{"class":163,"line":634},[290,59546,334],{"emptyLinePlaceholder":333},[290,59548,59549],{"class":163,"line":649},[290,59550,59551],{"class":455},"  \u002F* Collapses to the widest single word; everything else wraps *\u002F\n",[290,59553,59554,59557,59559,59561,59563,59565],{"class":163,"line":660},[290,59555,59556],{"class":303},"  .min",[290,59558,4612],{"class":295},[290,59560,1748],{"class":461},[290,59562,465],{"class":295},[290,59564,38658],{"class":461},[290,59566,809],{"class":295},[290,59568,59569],{"class":163,"line":688},[290,59570,334],{"emptyLinePlaceholder":333},[290,59572,59573],{"class":163,"line":693},[290,59574,59575],{"class":455},"  \u002F* Preferred width: the whole sentence on one line, even past the parent *\u002F\n",[290,59577,59578,59581,59583,59585,59587,59589],{"class":163,"line":698},[290,59579,59580],{"class":303},"  .max",[290,59582,4612],{"class":295},[290,59584,1748],{"class":461},[290,59586,465],{"class":295},[290,59588,17909],{"class":461},[290,59590,809],{"class":295},[290,59592,59593],{"class":163,"line":704},[290,59594,334],{"emptyLinePlaceholder":333},[290,59596,59597],{"class":163,"line":710},[290,59598,59599],{"class":455},"  \u002F* max-content until it would exceed 18rem, then caps and wraps *\u002F\n",[290,59601,59602,59605,59607,59609,59611,59613,59615,59617,59619],{"class":163,"line":717},[290,59603,59604],{"class":303},"  .fit",[290,59606,4612],{"class":295},[290,59608,1748],{"class":461},[290,59610,465],{"class":295},[290,59612,55424],{"class":461},[290,59614,484],{"class":295},[290,59616,17921],{"class":461},[290,59618,801],{"class":541},[290,59620,1122],{"class":295},[290,59622,59623],{"class":163,"line":730},[290,59624,334],{"emptyLinePlaceholder":333},[290,59626,59627],{"class":163,"line":742},[290,59628,59629],{"class":455},"  \u002F* Bare keyword: shrink-wrap, but never exceed the available space *\u002F\n",[290,59631,59632,59635,59637,59639,59641,59643],{"class":163,"line":768},[290,59633,59634],{"class":303},"  .fitbare",[290,59636,790],{"class":295},[290,59638,1748],{"class":461},[290,59640,465],{"class":295},[290,59642,55424],{"class":461},[290,59644,809],{"class":295},[290,59646,59647,59649,59651],{"class":163,"line":774},[290,59648,431],{"class":295},[290,59650,1430],{"class":299},[290,59652,327],{"class":295},[290,59654,59655,59657,59659],{"class":163,"line":779},[290,59656,431],{"class":295},[290,59658,9013],{"class":299},[290,59660,327],{"class":295},[290,59662,59663,59665,59667],{"class":163,"line":784},[290,59664,296],{"class":295},[290,59666,9239],{"class":299},[290,59668,327],{"class":295},[290,59670,59671,59673,59675,59677,59679,59682],{"class":163,"line":812},[290,59672,367],{"class":295},[290,59674,342],{"class":299},[290,59676,314],{"class":303},[290,59678,307],{"class":295},[290,59680,59681],{"class":310},"\"box min\"",[290,59683,327],{"class":295},[290,59685,59686,59688,59690,59692,59694,59696,59699,59701],{"class":163,"line":860},[290,59687,4290],{"class":295},[290,59689,14],{"class":299},[290,59691,314],{"class":303},[290,59693,307],{"class":295},[290,59695,50688],{"class":310},[290,59697,59698],{"class":295},">width: min-content\u003C\u002F",[290,59700,14],{"class":299},[290,59702,327],{"class":295},[290,59704,59705,59707,59709,59712,59714],{"class":163,"line":865},[290,59706,4290],{"class":295},[290,59708,14],{"class":299},[290,59710,59711],{"class":295},">Intrinsic sizing keywords describe width by content.\u003C\u002F",[290,59713,14],{"class":299},[290,59715,327],{"class":295},[290,59717,59718,59720,59722],{"class":163,"line":871},[290,59719,4315],{"class":295},[290,59721,342],{"class":299},[290,59723,327],{"class":295},[290,59725,59726],{"class":163,"line":880},[290,59727,334],{"emptyLinePlaceholder":333},[290,59729,59730,59732,59734,59736,59738,59741],{"class":163,"line":896},[290,59731,367],{"class":295},[290,59733,342],{"class":299},[290,59735,314],{"class":303},[290,59737,307],{"class":295},[290,59739,59740],{"class":310},"\"box max\"",[290,59742,327],{"class":295},[290,59744,59745,59747,59749,59751,59753,59755,59758,59760],{"class":163,"line":4734},[290,59746,4290],{"class":295},[290,59748,14],{"class":299},[290,59750,314],{"class":303},[290,59752,307],{"class":295},[290,59754,50688],{"class":310},[290,59756,59757],{"class":295},">width: max-content\u003C\u002F",[290,59759,14],{"class":299},[290,59761,327],{"class":295},[290,59763,59764,59766,59768,59770,59772],{"class":163,"line":4742},[290,59765,4290],{"class":295},[290,59767,14],{"class":299},[290,59769,59711],{"class":295},[290,59771,14],{"class":299},[290,59773,327],{"class":295},[290,59775,59776,59778,59780],{"class":163,"line":4761},[290,59777,4315],{"class":295},[290,59779,342],{"class":299},[290,59781,327],{"class":295},[290,59783,59784],{"class":163,"line":4766},[290,59785,334],{"emptyLinePlaceholder":333},[290,59787,59788,59790,59792,59794,59796,59799],{"class":163,"line":4786},[290,59789,367],{"class":295},[290,59791,342],{"class":299},[290,59793,314],{"class":303},[290,59795,307],{"class":295},[290,59797,59798],{"class":310},"\"box fit\"",[290,59800,327],{"class":295},[290,59802,59803,59805,59807,59809,59811,59813,59816,59818],{"class":163,"line":9635},[290,59804,4290],{"class":295},[290,59806,14],{"class":299},[290,59808,314],{"class":303},[290,59810,307],{"class":295},[290,59812,50688],{"class":310},[290,59814,59815],{"class":295},">width: fit-content(18rem)\u003C\u002F",[290,59817,14],{"class":299},[290,59819,327],{"class":295},[290,59821,59822,59824,59826,59828,59830],{"class":163,"line":9641},[290,59823,4290],{"class":295},[290,59825,14],{"class":299},[290,59827,59711],{"class":295},[290,59829,14],{"class":299},[290,59831,327],{"class":295},[290,59833,59834,59836,59838],{"class":163,"line":9647},[290,59835,4315],{"class":295},[290,59837,342],{"class":299},[290,59839,327],{"class":295},[290,59841,59842],{"class":163,"line":9665},[290,59843,334],{"emptyLinePlaceholder":333},[290,59845,59846,59848,59850,59852,59854,59857],{"class":163,"line":9683},[290,59847,367],{"class":295},[290,59849,342],{"class":299},[290,59851,314],{"class":303},[290,59853,307],{"class":295},[290,59855,59856],{"class":310},"\"box fitbare\"",[290,59858,327],{"class":295},[290,59860,59861,59863,59865,59867,59869,59871,59874,59876],{"class":163,"line":9688},[290,59862,4290],{"class":295},[290,59864,14],{"class":299},[290,59866,314],{"class":303},[290,59868,307],{"class":295},[290,59870,50688],{"class":310},[290,59872,59873],{"class":295},">width: fit-content\u003C\u002F",[290,59875,14],{"class":299},[290,59877,327],{"class":295},[290,59879,59880,59882,59884,59886,59888],{"class":163,"line":9694},[290,59881,4290],{"class":295},[290,59883,14],{"class":299},[290,59885,59711],{"class":295},[290,59887,14],{"class":299},[290,59889,327],{"class":295},[290,59891,59892,59894,59896],{"class":163,"line":9700},[290,59893,4315],{"class":295},[290,59895,342],{"class":299},[290,59897,327],{"class":295},[290,59899,59900,59902,59904],{"class":163,"line":9710},[290,59901,431],{"class":295},[290,59903,9239],{"class":299},[290,59905,327],{"class":295},[290,59907,59908,59910,59912],{"class":163,"line":9716},[290,59909,431],{"class":295},[290,59911,285],{"class":299},[290,59913,327],{"class":295},[14,59915,1517,59916,59918,59919,59921,59922,59925,59926,59928],{},[18,59917,38658],{}," box narrows until its tallest, thinnest column of wrapped text fits the longest word; ",[18,59920,17909],{}," stretches the sentence onto one line and can overflow the page; ",[18,59923,59924],{},"fit-content(18rem)"," shrink-wraps but stops at 18rem; bare ",[18,59927,55424],{}," shrink-wraps but never exceeds the body width.",[133,59930,140,59932,140,59935,140,59938,140,59941,140,59943,140,59946,140,59948,140,59951,140,59955,140,59958,140,59960,140,59963],{"viewBox":4133,"role":136,"ariaLabel":59931,"xmlns":138,"style":139},"The same box rendered under min-content, max-content, and fit-content widths showing different resulting widths",[142,59933,59934],{},"Same content under three intrinsic widths",[146,59936,59937],{},"Three boxes containing identical text: a narrow min-content box, a wide overflowing max-content box, and a capped fit-content box in between.",[150,59939,59940],{"x":152,"y":153,"style":1781},"same content, three widths",[171,59942],{"x":4146,"y":1786,"width":5144,"height":5115,"rx":5901,"fill":177,"opacity":11349,"stroke":177,"strokeWidth":168},[150,59944,59945],{"x":8882,"y":57802,"style":221},"wraps",[150,59947,38658],{"x":8882,"y":1822,"style":183},[171,59949],{"x":4146,"y":174,"width":59950,"height":8879,"rx":5901,"fill":177,"opacity":11349,"stroke":177,"strokeWidth":168},"650",[150,59952,59954],{"x":8950,"y":59953,"style":221},"227","one line, may overflow parent",[150,59956,17909],{"x":8950,"y":59957,"style":183},"265",[171,59959],{"x":1787,"y":1786,"width":5397,"height":5115,"rx":5901,"fill":177,"opacity":11349,"stroke":177,"strokeWidth":168},[150,59961,59962],{"x":5397,"y":165,"style":221},"grows to content, capped at limit",[150,59964,59263],{"x":5397,"y":1822,"style":183},[50,59966,59968],{"id":59967},"key-technique-fit-content-as-a-grid-track","Key technique: fit-content() as a grid track",[14,59970,59971,59972,59974,59975,59978],{},"The single most valuable place these keywords appear is grid track sizing, where ",[18,59973,55390],{}," solves the classic label-column problem. A track defined as ",[18,59976,59977],{},"fit-content(12rem)"," sizes itself to its widest cell content but refuses to exceed 12rem, so a sidebar of labels is as wide as the longest label needs and no wider, yet a freakishly long label wraps instead of pushing the main column off-screen.",[281,59980,59982],{"className":438,"code":59981,"language":440,"meta":286,"style":286},".layout {\n  display: grid;\n  \u002F* label column shrink-wraps up to 12rem; content takes the rest *\u002F\n  grid-template-columns: fit-content(12rem) 1fr;\n  gap: 1rem;\n}\n",[18,59983,59984,59990,60000,60005,60027,60039],{"__ignoreMap":286},[290,59985,59986,59988],{"class":163,"line":292},[290,59987,47789],{"class":303},[290,59989,450],{"class":295},[290,59991,59992,59994,59996,59998],{"class":163,"line":330},[290,59993,17742],{"class":461},[290,59995,465],{"class":295},[290,59997,9147],{"class":461},[290,59999,471],{"class":295},[290,60001,60002],{"class":163,"line":337},[290,60003,60004],{"class":455},"  \u002F* label column shrink-wraps up to 12rem; content takes the rest *\u002F\n",[290,60006,60007,60009,60011,60013,60015,60017,60019,60021,60023,60025],{"class":163,"line":364},[290,60008,47811],{"class":461},[290,60010,465],{"class":295},[290,60012,55424],{"class":461},[290,60014,484],{"class":295},[290,60016,5894],{"class":461},[290,60018,801],{"class":541},[290,60020,490],{"class":295},[290,60022,468],{"class":461},[290,60024,11964],{"class":541},[290,60026,471],{"class":295},[290,60028,60029,60031,60033,60035,60037],{"class":163,"line":386},[290,60030,26819],{"class":461},[290,60032,465],{"class":295},[290,60034,468],{"class":461},[290,60036,801],{"class":541},[290,60038,471],{"class":295},[290,60040,60041],{"class":163,"line":408},[290,60042,620],{"class":295},[14,60044,60045],{},"This is impossible to express cleanly with fixed or percentage widths, because the correct width depends on the content.",[47,60047],{},[50,60049,60051],{"id":60050},"variation-rtl-and-a-max-content-button-row","Variation: RTL and a max-content button row",[14,60053,60054,60056,60057,60060,60061,60063],{},[18,60055,17909],{}," shines for a row of buttons that must each hug their label, never stretch, and stay correct in right-to-left layouts. Setting each control to ",[18,60058,60059],{},"width: max-content"," makes it exactly as wide as its text plus padding, and because it is content-driven it mirrors correctly under ",[18,60062,47282],{}," with no extra rules.",[281,60065,60067],{"className":438,"code":60066,"language":440,"meta":286,"style":286},".toolbar {\n  display: flex;\n  gap: 0.5rem;\n}\n.toolbar button {\n  width: max-content;   \u002F* hugs its own label, never stretches *\u002F\n  padding-inline: 1rem; \u002F* logical padding flips automatically for RTL *\u002F\n}\n.toolbar[dir=\"rtl\"] {\n  \u002F* no width changes needed; max-content + logical padding handle it *\u002F\n  flex-direction: row-reverse;\n}\n",[18,60068,60069,60076,60086,60098,60102,60110,60123,60139,60143,60159,60164,60175],{"__ignoreMap":286},[290,60070,60071,60074],{"class":163,"line":292},[290,60072,60073],{"class":303},".toolbar",[290,60075,450],{"class":295},[290,60077,60078,60080,60082,60084],{"class":163,"line":330},[290,60079,17742],{"class":461},[290,60081,465],{"class":295},[290,60083,9055],{"class":461},[290,60085,471],{"class":295},[290,60087,60088,60090,60092,60094,60096],{"class":163,"line":337},[290,60089,26819],{"class":461},[290,60091,465],{"class":295},[290,60093,798],{"class":461},[290,60095,801],{"class":541},[290,60097,471],{"class":295},[290,60099,60100],{"class":163,"line":364},[290,60101,620],{"class":295},[290,60103,60104,60106,60108],{"class":163,"line":386},[290,60105,60073],{"class":303},[290,60107,7409],{"class":299},[290,60109,450],{"class":295},[290,60111,60112,60114,60116,60118,60120],{"class":163,"line":408},[290,60113,17904],{"class":461},[290,60115,465],{"class":295},[290,60117,17909],{"class":461},[290,60119,4514],{"class":295},[290,60121,60122],{"class":455},"\u002F* hugs its own label, never stretches *\u002F\n",[290,60124,60125,60128,60130,60132,60134,60136],{"class":163,"line":428},[290,60126,60127],{"class":461},"  padding-inline",[290,60129,465],{"class":295},[290,60131,468],{"class":461},[290,60133,801],{"class":541},[290,60135,828],{"class":295},[290,60137,60138],{"class":455},"\u002F* logical padding flips automatically for RTL *\u002F\n",[290,60140,60141],{"class":163,"line":517},[290,60142,620],{"class":295},[290,60144,60145,60147,60149,60152,60154,60157],{"class":163,"line":523},[290,60146,60073],{"class":303},[290,60148,1140],{"class":295},[290,60150,60151],{"class":303},"dir",[290,60153,307],{"class":541},[290,60155,60156],{"class":310},"\"rtl\"",[290,60158,14739],{"class":295},[290,60160,60161],{"class":163,"line":532},[290,60162,60163],{"class":455},"  \u002F* no width changes needed; max-content + logical padding handle it *\u002F\n",[290,60165,60166,60168,60170,60173],{"class":163,"line":551},[290,60167,40310],{"class":461},[290,60169,465],{"class":295},[290,60171,60172],{"class":461},"row-reverse",[290,60174,471],{"class":295},[290,60176,60177],{"class":163,"line":586},[290,60178,620],{"class":295},[14,60180,29317,60181,18300,60184,3041,60187,60190],{},[18,60182,60183],{},"padding-inline",[18,60185,60186],{},"padding-left",[18,60188,60189],{},"padding-right"," keeps the hugging behavior symmetric in both writing directions.",[50,60192,1299],{"id":1298},[14,60194,60195,69,60197,60199,60200,60202,60203,60205],{},[18,60196,38658],{},[18,60198,17909],{}," are supported as width and height values in Chrome 46+, Edge 79+, Safari 11+, and Firefox 66+, so they are safe everywhere today. ",[18,60201,55390],{}," as a track and box sizing function is supported in Chrome 57+, Edge 79+, Safari 11+, and Firefox 91+. No fallback is needed for current engines; if you target very old browsers, a fixed ",[18,60204,41191],{}," is the closest graceful degradation.",[50,60207,1316],{"id":1315},[14,60209,60210,60213,60215,60216,60218],{},[62,60211,60212],{},"What is the difference between min-content and max-content?",[18,60214,38658],{}," is the smallest a box can be without overflowing its content, roughly the width of its longest unbreakable word. ",[18,60217,17909],{}," is the box's preferred width with no wrapping at all, the width it would take on an infinitely wide canvas.",[14,60220,60221,5735,60224,60226,60227,60229,60230,60232],{},[62,60222,60223],{},"When should I use fit-content() instead of max-content?",[18,60225,55390],{}," when you want a box to shrink-wrap its content but never exceed a cap. It behaves like ",[18,60228,17909],{}," until the content reaches the limit you pass, then it stops growing and wraps, which ",[18,60231,17909],{}," never does.",[14,60234,60235,37757,60238,60240],{},[62,60236,60237],{},"Does fit-content() work as a grid track size?",[18,60239,59263],{}," is a valid grid track sizing function: the track sizes to its content up to the limit, then caps. It is one of the most useful sidebar and label-column track definitions.",[14,60242,60243,1322,60246,60248],{},[62,60244,60245],{},"Why does my width: min-content element look too narrow?",[18,60247,38658],{}," collapses to the longest unbreakable run of content. Long words, URLs, or non-breaking phrases set the floor; if there are none, the box can become as narrow as a single character.",[50,60250,1391],{"id":1390},[1393,60252,60253,60258,60264,60269,60274],{},[1396,60254,60255,60257],{},[27,60256,47441],{"href":47440}," — the parent guide for content-driven sizing.",[1396,60259,60260,60263],{},[27,60261,60262],{"href":58177},"aspect-ratio for Responsive Media"," — reserving box dimensions before media loads.",[1396,60265,60266,60268],{},[27,60267,51182],{"href":11317}," — pairing intrinsic widths with fluid padding tokens.",[1396,60270,60271,60273],{},[27,60272,37796],{"href":37795}," — sizing components by their own context.",[1396,60275,60276,60278],{},[27,60277,18535],{"href":18534}," — cross-area: content-sized controls that animate on hover.",[1430,60280,47384],{},{"title":286,"searchDepth":330,"depth":330,"links":60282},[60283,60284,60285,60286,60287,60288,60289],{"id":59207,"depth":330,"text":59208},{"id":59235,"depth":330,"text":59236},{"id":59967,"depth":330,"text":59968},{"id":60050,"depth":330,"text":60051},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"What min-content, max-content, and fit-content() each compute, how the browser sizes a box under them, and the practical layout cases where each one is the right tool.",{"seoTitle":60292,"datePublished":1447,"dateModified":1447,"faq":60293},"min-content, max-content, fit-content()",[60294,60296,60298,60300],{"q":60212,"a":60295},"min-content is the smallest a box can be without overflowing its content, roughly the width of its longest unbreakable word. max-content is the box's preferred width with no wrapping at all, the width it would take on an infinitely wide canvas.",{"q":60223,"a":60297},"Use fit-content() when you want a box to shrink-wrap its content but never exceed a cap. It behaves like max-content until the content reaches the limit you pass, then it stops growing and wraps, which max-content never does.",{"q":60237,"a":60299},"Yes. fit-content(limit) is a valid grid track sizing function: the track sizes to its content up to the limit, then caps. It is one of the most useful sidebar and label-column track definitions.",{"q":60245,"a":60301},"Because min-content collapses to the longest unbreakable run of content. Long words, URLs, or non-breaking phrases set the floor; if there are none, the box can become as narrow as a single character.","\u002Fmastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fmin-max-fit-content-explained",{"title":59185,"description":60290},"mastering-container-queries-responsive-layouts\u002Fintrinsic-sizing-techniques\u002Fmin-max-fit-content-explained\u002Findex","_gs9c1as58XVkoRKXFlOoIXZWIOnBJVAI9_HMlfXWeQ",{"id":60307,"title":60308,"body":60309,"description":61368,"extension":1444,"meta":61369,"navigation":333,"path":61381,"seo":61382,"stem":61383,"__hash__":61384},"content\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcascade-layers-for-reset-and-tokens\u002Findex.md","Cascade Layers for Reset and Design Tokens",{"type":7,"value":60310,"toc":61359},[60311,60314,60330,60332,60336,60349,60352,60354,60356,60359,61070,61073,61121,61123,61127,61137,61139,61143,61149,61246,61272,61274,61276,61293,61295,61297,61303,61312,61321,61330,61332,61334,61356],[10,60312,60308],{"id":60313},"cascade-layers-for-reset-and-design-tokens",[14,60315,60316,60317,60319,60320,60322,60323,60326,60327,60329],{},"Specificity battles are the tax of a growing stylesheet: a reset uses broad selectors, a component overrides them, a utility tries to override the component, and soon every rule is fighting for priority with ",[18,60318,3900],{}," and selector stacking. The ",[18,60321,12681],{}," rule fixes this at the root by letting authoring order, not specificity, decide who wins. This guide, part of the ",[27,60324,60325],{"href":56329},"modern CSS reset strategies"," collection within ",[27,60328,11296],{"href":5777},", shows how to order a reset, design tokens, and components into predictable layers, then drop container queries on top without disturbing the stack.",[47,60331],{},[50,60333,60335],{"id":60334},"approach-rationale-priority-by-intent-not-by-selector","Approach rationale: priority by intent, not by selector",[14,60337,60338,60339,60341,60342,60345,60346,60348],{},"Without layers, the cascade ranks normal declarations by specificity, so a reset written as ",[18,60340,4415],{}," is weak but a reset written as ",[18,60343,60344],{},"ul li a"," quietly outranks a component's ",[18,60347,15250],{},". Authors then escalate specificity to win, and the stylesheet becomes a ladder no one can climb back down. Cascade layers invert the model: you name an ordered set of layers once, and every rule's priority is decided first by which layer it sits in. A reset in an early layer can use whatever selectors it likes and will still lose to a later component layer, regardless of specificity.",[14,60350,60351],{},"The win is architectural clarity. Reset goes first because it should always be overridable. Design tokens — the custom properties that define colour, spacing, and type scales — sit next so components can consume them. Components come after tokens. Utilities, if you use them, come last so a single class can override a component on demand. There is no JavaScript and no preprocessor involved; this is native cascade behaviour and the right baseline for any design system that also leans on container queries for component responsiveness.",[47,60353],{},[50,60355,4203],{"id":4202},[14,60357,60358],{},"This self-contained file declares the layer order once, then fills each layer. Resize the wrapper to confirm the container query, which lives in the components layer, overrides cleanly without specificity tricks.",[281,60360,60362],{"className":283,"code":60361,"language":285,"meta":286,"style":286},"\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cstyle>\n  \u002F* Declare the order ONCE, up front. Layers listed here rank in this order\n     no matter where their rules are physically written later. *\u002F\n  @layer reset, tokens, components, utilities;\n\n  \u002F* RESET layer: broad selectors, intentionally low priority. *\u002F\n  @layer reset {\n    *, *::before, *::after { box-sizing: border-box; }\n    body { margin: 0; }\n    \u002F* Even this specific selector loses to the components layer below. *\u002F\n    ul.menu li a { color: black; text-decoration: underline; }\n  }\n\n  \u002F* TOKENS layer: design decisions exposed as custom properties.\n     Custom properties are not subject to layer ordering themselves\n     (they cascade as values), but housing them here documents intent. *\u002F\n  @layer tokens {\n    :root {\n      --color-accent: #7aa2ff;\n      --space-1: 0.5rem;\n      --space-2: 1rem;\n      --radius: 8px;\n      --font-ui: system-ui, sans-serif;\n    }\n  }\n\n  \u002F* COMPONENTS layer: consumes tokens, overrides reset by layer order. *\u002F\n  @layer components {\n    .card-shell { container-type: inline-size; container-name: card; }\n\n    .menu {\n      display: flex;\n      gap: var(--space-2);\n      list-style: none;\n      margin: 0;\n      padding: var(--space-1);\n      font-family: var(--font-ui);\n    }\n    \u002F* A single-class selector beats the reset's ul.menu li a purely\n       because components is a later layer than reset. No !important. *\u002F\n    .menu a { color: var(--color-accent); text-decoration: none; }\n\n    \u002F* Container query lives in the SAME layer as the component it adjusts,\n       so its overrides rank with the component, not against it. *\u002F\n    @container card (max-width: 360px) {\n      .menu { flex-direction: column; gap: var(--space-1); }\n    }\n  }\n\n  \u002F* UTILITIES layer: last, so one class can override a component. *\u002F\n  @layer utilities {\n    .text-muted { color: #6b7280; }\n  }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv class=\"card-shell\">\n    \u003Cul class=\"menu\">\n      \u003Cli>\u003Ca href=\"#\">Home\u003C\u002Fa>\u003C\u002Fli>\n      \u003Cli>\u003Ca href=\"#\">Docs\u003C\u002Fa>\u003C\u002Fli>\n      \u003Cli>\u003Ca href=\"#\" class=\"text-muted\">Archive\u003C\u002Fa>\u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,60363,60364,60374,60388,60396,60410,60418,60423,60428,60436,60440,60445,60452,60479,60494,60499,60531,60535,60539,60544,60549,60554,60561,60568,60579,60592,60605,60618,60633,60637,60641,60645,60650,60656,60672,60676,60683,60693,60708,60719,60730,60745,60761,60765,60770,60775,60804,60808,60813,60818,60825,60853,60857,60861,60865,60870,60877,60893,60897,60905,60913,60921,60936,60950,60977,61004,61038,61046,61054,61062],{"__ignoreMap":286},[290,60365,60366,60368,60370,60372],{"class":163,"line":292},[290,60367,8982],{"class":295},[290,60369,35913],{"class":299},[290,60371,8988],{"class":303},[290,60373,327],{"class":295},[290,60375,60376,60378,60380,60382,60384,60386],{"class":163,"line":330},[290,60377,296],{"class":295},[290,60379,285],{"class":299},[290,60381,8999],{"class":303},[290,60383,307],{"class":295},[290,60385,9004],{"class":310},[290,60387,327],{"class":295},[290,60389,60390,60392,60394],{"class":163,"line":337},[290,60391,296],{"class":295},[290,60393,9013],{"class":299},[290,60395,327],{"class":295},[290,60397,60398,60400,60402,60404,60406,60408],{"class":163,"line":364},[290,60399,296],{"class":295},[290,60401,9022],{"class":299},[290,60403,9025],{"class":303},[290,60405,307],{"class":295},[290,60407,9030],{"class":310},[290,60409,327],{"class":295},[290,60411,60412,60414,60416],{"class":163,"line":386},[290,60413,296],{"class":295},[290,60415,1430],{"class":299},[290,60417,327],{"class":295},[290,60419,60420],{"class":163,"line":408},[290,60421,60422],{"class":455},"  \u002F* Declare the order ONCE, up front. Layers listed here rank in this order\n",[290,60424,60425],{"class":163,"line":428},[290,60426,60427],{"class":455},"     no matter where their rules are physically written later. *\u002F\n",[290,60429,60430,60433],{"class":163,"line":517},[290,60431,60432],{"class":541},"  @layer",[290,60434,60435],{"class":295}," reset, tokens, components, utilities;\n",[290,60437,60438],{"class":163,"line":523},[290,60439,334],{"emptyLinePlaceholder":333},[290,60441,60442],{"class":163,"line":532},[290,60443,60444],{"class":455},"  \u002F* RESET layer: broad selectors, intentionally low priority. *\u002F\n",[290,60446,60447,60449],{"class":163,"line":551},[290,60448,60432],{"class":541},[290,60450,60451],{"class":295}," reset {\n",[290,60453,60454,60457,60459,60461,60463,60465,60467,60469,60471,60473,60475,60477],{"class":163,"line":586},[290,60455,60456],{"class":299},"    *",[290,60458,569],{"class":295},[290,60460,4415],{"class":299},[290,60462,2811],{"class":303},[290,60464,569],{"class":295},[290,60466,4415],{"class":299},[290,60468,2412],{"class":303},[290,60470,790],{"class":295},[290,60472,46435],{"class":461},[290,60474,465],{"class":295},[290,60476,46440],{"class":461},[290,60478,809],{"class":295},[290,60480,60481,60484,60486,60488,60490,60492],{"class":163,"line":602},[290,60482,60483],{"class":299},"    body",[290,60485,790],{"class":295},[290,60487,2725],{"class":461},[290,60489,465],{"class":295},[290,60491,487],{"class":461},[290,60493,809],{"class":295},[290,60495,60496],{"class":163,"line":617},[290,60497,60498],{"class":455},"    \u002F* Even this specific selector loses to the components layer below. *\u002F\n",[290,60500,60501,60504,60507,60509,60511,60513,60515,60517,60520,60522,60525,60527,60529],{"class":163,"line":623},[290,60502,60503],{"class":299},"    ul",[290,60505,60506],{"class":303},".menu",[290,60508,32288],{"class":299},[290,60510,7556],{"class":299},[290,60512,790],{"class":295},[290,60514,9133],{"class":461},[290,60516,465],{"class":295},[290,60518,60519],{"class":461},"black",[290,60521,828],{"class":295},[290,60523,60524],{"class":461},"text-decoration",[290,60526,465],{"class":295},[290,60528,17831],{"class":461},[290,60530,809],{"class":295},[290,60532,60533],{"class":163,"line":628},[290,60534,771],{"class":295},[290,60536,60537],{"class":163,"line":634},[290,60538,334],{"emptyLinePlaceholder":333},[290,60540,60541],{"class":163,"line":649},[290,60542,60543],{"class":455},"  \u002F* TOKENS layer: design decisions exposed as custom properties.\n",[290,60545,60546],{"class":163,"line":660},[290,60547,60548],{"class":455},"     Custom properties are not subject to layer ordering themselves\n",[290,60550,60551],{"class":163,"line":688},[290,60552,60553],{"class":455},"     (they cascade as values), but housing them here documents intent. *\u002F\n",[290,60555,60556,60558],{"class":163,"line":693},[290,60557,60432],{"class":541},[290,60559,60560],{"class":295}," tokens {\n",[290,60562,60563,60566],{"class":163,"line":698},[290,60564,60565],{"class":303},"    :root",[290,60567,450],{"class":295},[290,60569,60570,60573,60575,60577],{"class":163,"line":704},[290,60571,60572],{"class":1561},"      --color-accent",[290,60574,465],{"class":295},[290,60576,177],{"class":461},[290,60578,471],{"class":295},[290,60580,60581,60584,60586,60588,60590],{"class":163,"line":710},[290,60582,60583],{"class":1561},"      --space-1",[290,60585,465],{"class":295},[290,60587,798],{"class":461},[290,60589,801],{"class":541},[290,60591,471],{"class":295},[290,60593,60594,60597,60599,60601,60603],{"class":163,"line":717},[290,60595,60596],{"class":1561},"      --space-2",[290,60598,465],{"class":295},[290,60600,468],{"class":461},[290,60602,801],{"class":541},[290,60604,471],{"class":295},[290,60606,60607,60610,60612,60614,60616],{"class":163,"line":730},[290,60608,60609],{"class":1561},"      --radius",[290,60611,465],{"class":295},[290,60613,176],{"class":461},[290,60615,674],{"class":541},[290,60617,471],{"class":295},[290,60619,60620,60623,60625,60627,60629,60631],{"class":163,"line":742},[290,60621,60622],{"class":1561},"      --font-ui",[290,60624,465],{"class":295},[290,60626,50115],{"class":461},[290,60628,569],{"class":295},[290,60630,44479],{"class":461},[290,60632,471],{"class":295},[290,60634,60635],{"class":163,"line":768},[290,60636,8200],{"class":295},[290,60638,60639],{"class":163,"line":774},[290,60640,771],{"class":295},[290,60642,60643],{"class":163,"line":779},[290,60644,334],{"emptyLinePlaceholder":333},[290,60646,60647],{"class":163,"line":784},[290,60648,60649],{"class":455},"  \u002F* COMPONENTS layer: consumes tokens, overrides reset by layer order. *\u002F\n",[290,60651,60652,60654],{"class":163,"line":812},[290,60653,60432],{"class":541},[290,60655,12747],{"class":295},[290,60657,60658,60661,60663,60665,60667,60669],{"class":163,"line":860},[290,60659,60660],{"class":303},"    .card-shell",[290,60662,790],{"class":295},[290,60664,24401],{"class":461},[290,60666,11512],{"class":295},[290,60668,26072],{"class":461},[290,60670,60671],{"class":295},": card; }\n",[290,60673,60674],{"class":163,"line":865},[290,60675,334],{"emptyLinePlaceholder":333},[290,60677,60678,60681],{"class":163,"line":871},[290,60679,60680],{"class":303},"    .menu",[290,60682,450],{"class":295},[290,60684,60685,60687,60689,60691],{"class":163,"line":880},[290,60686,37053],{"class":461},[290,60688,465],{"class":295},[290,60690,9055],{"class":461},[290,60692,471],{"class":295},[290,60694,60695,60697,60699,60701,60703,60706],{"class":163,"line":896},[290,60696,38032],{"class":461},[290,60698,465],{"class":295},[290,60700,1622],{"class":461},[290,60702,484],{"class":295},[290,60704,60705],{"class":1561},"--space-2",[290,60707,500],{"class":295},[290,60709,60710,60713,60715,60717],{"class":163,"line":4734},[290,60711,60712],{"class":461},"      list-style",[290,60714,465],{"class":295},[290,60716,72],{"class":461},[290,60718,471],{"class":295},[290,60720,60721,60724,60726,60728],{"class":163,"line":4742},[290,60722,60723],{"class":461},"      margin",[290,60725,465],{"class":295},[290,60727,487],{"class":461},[290,60729,471],{"class":295},[290,60731,60732,60734,60736,60738,60740,60743],{"class":163,"line":4761},[290,60733,42626],{"class":461},[290,60735,465],{"class":295},[290,60737,1622],{"class":461},[290,60739,484],{"class":295},[290,60741,60742],{"class":1561},"--space-1",[290,60744,500],{"class":295},[290,60746,60747,60750,60752,60754,60756,60759],{"class":163,"line":4766},[290,60748,60749],{"class":461},"      font-family",[290,60751,465],{"class":295},[290,60753,1622],{"class":461},[290,60755,484],{"class":295},[290,60757,60758],{"class":1561},"--font-ui",[290,60760,500],{"class":295},[290,60762,60763],{"class":163,"line":4786},[290,60764,8200],{"class":295},[290,60766,60767],{"class":163,"line":9635},[290,60768,60769],{"class":455},"    \u002F* A single-class selector beats the reset's ul.menu li a purely\n",[290,60771,60772],{"class":163,"line":9641},[290,60773,60774],{"class":455},"       because components is a later layer than reset. No !important. *\u002F\n",[290,60776,60777,60779,60781,60783,60785,60787,60789,60791,60794,60796,60798,60800,60802],{"class":163,"line":9647},[290,60778,60680],{"class":303},[290,60780,7556],{"class":299},[290,60782,790],{"class":295},[290,60784,9133],{"class":461},[290,60786,465],{"class":295},[290,60788,1622],{"class":461},[290,60790,484],{"class":295},[290,60792,60793],{"class":1561},"--color-accent",[290,60795,14061],{"class":295},[290,60797,60524],{"class":461},[290,60799,465],{"class":295},[290,60801,72],{"class":461},[290,60803,809],{"class":295},[290,60805,60806],{"class":163,"line":9665},[290,60807,334],{"emptyLinePlaceholder":333},[290,60809,60810],{"class":163,"line":9683},[290,60811,60812],{"class":455},"    \u002F* Container query lives in the SAME layer as the component it adjusts,\n",[290,60814,60815],{"class":163,"line":9688},[290,60816,60817],{"class":455},"       so its overrides rank with the component, not against it. *\u002F\n",[290,60819,60820,60822],{"class":163,"line":9694},[290,60821,36253],{"class":541},[290,60823,60824],{"class":295}," card (max-width: 360px) {\n",[290,60826,60827,60830,60832,60835,60837,60839,60841,60843,60845,60847,60849,60851],{"class":163,"line":9700},[290,60828,60829],{"class":303},"      .menu",[290,60831,790],{"class":295},[290,60833,60834],{"class":461},"flex-direction",[290,60836,465],{"class":295},[290,60838,40315],{"class":461},[290,60840,828],{"class":295},[290,60842,9070],{"class":461},[290,60844,465],{"class":295},[290,60846,1622],{"class":461},[290,60848,484],{"class":295},[290,60850,60742],{"class":1561},[290,60852,1122],{"class":295},[290,60854,60855],{"class":163,"line":9710},[290,60856,8200],{"class":295},[290,60858,60859],{"class":163,"line":9716},[290,60860,771],{"class":295},[290,60862,60863],{"class":163,"line":9738},[290,60864,334],{"emptyLinePlaceholder":333},[290,60866,60867],{"class":163,"line":9748},[290,60868,60869],{"class":455},"  \u002F* UTILITIES layer: last, so one class can override a component. *\u002F\n",[290,60871,60872,60874],{"class":163,"line":9754},[290,60873,60432],{"class":541},[290,60875,60876],{"class":295}," utilities {\n",[290,60878,60879,60882,60884,60886,60888,60891],{"class":163,"line":9760},[290,60880,60881],{"class":303},"    .text-muted",[290,60883,790],{"class":295},[290,60885,9133],{"class":461},[290,60887,465],{"class":295},[290,60889,60890],{"class":461},"#6b7280",[290,60892,809],{"class":295},[290,60894,60895],{"class":163,"line":9777},[290,60896,771],{"class":295},[290,60898,60899,60901,60903],{"class":163,"line":9787},[290,60900,431],{"class":295},[290,60902,1430],{"class":299},[290,60904,327],{"class":295},[290,60906,60907,60909,60911],{"class":163,"line":9793},[290,60908,431],{"class":295},[290,60910,9013],{"class":299},[290,60912,327],{"class":295},[290,60914,60915,60917,60919],{"class":163,"line":9799},[290,60916,296],{"class":295},[290,60918,9239],{"class":299},[290,60920,327],{"class":295},[290,60922,60923,60925,60927,60929,60931,60934],{"class":163,"line":9805},[290,60924,367],{"class":295},[290,60926,342],{"class":299},[290,60928,314],{"class":303},[290,60930,307],{"class":295},[290,60932,60933],{"class":310},"\"card-shell\"",[290,60935,327],{"class":295},[290,60937,60938,60940,60942,60944,60946,60948],{"class":163,"line":9811},[290,60939,4290],{"class":295},[290,60941,1393],{"class":299},[290,60943,314],{"class":303},[290,60945,307],{"class":295},[290,60947,311],{"class":310},[290,60949,327],{"class":295},[290,60951,60952,60954,60956,60958,60960,60962,60964,60966,60969,60971,60973,60975],{"class":163,"line":9820},[290,60953,36430],{"class":295},[290,60955,1396],{"class":299},[290,60957,9303],{"class":295},[290,60959,27],{"class":299},[290,60961,17687],{"class":303},[290,60963,307],{"class":295},[290,60965,48567],{"class":310},[290,60967,60968],{"class":295},">Home\u003C\u002F",[290,60970,27],{"class":299},[290,60972,11472],{"class":295},[290,60974,1396],{"class":299},[290,60976,327],{"class":295},[290,60978,60979,60981,60983,60985,60987,60989,60991,60993,60996,60998,61000,61002],{"class":163,"line":9829},[290,60980,36430],{"class":295},[290,60982,1396],{"class":299},[290,60984,9303],{"class":295},[290,60986,27],{"class":299},[290,60988,17687],{"class":303},[290,60990,307],{"class":295},[290,60992,48567],{"class":310},[290,60994,60995],{"class":295},">Docs\u003C\u002F",[290,60997,27],{"class":299},[290,60999,11472],{"class":295},[290,61001,1396],{"class":299},[290,61003,327],{"class":295},[290,61005,61006,61008,61010,61012,61014,61016,61018,61020,61022,61024,61027,61030,61032,61034,61036],{"class":163,"line":27197},[290,61007,36430],{"class":295},[290,61009,1396],{"class":299},[290,61011,9303],{"class":295},[290,61013,27],{"class":299},[290,61015,17687],{"class":303},[290,61017,307],{"class":295},[290,61019,48567],{"class":310},[290,61021,314],{"class":303},[290,61023,307],{"class":295},[290,61025,61026],{"class":310},"\"text-muted\"",[290,61028,61029],{"class":295},">Archive\u003C\u002F",[290,61031,27],{"class":299},[290,61033,11472],{"class":295},[290,61035,1396],{"class":299},[290,61037,327],{"class":295},[290,61039,61040,61042,61044],{"class":163,"line":27202},[290,61041,36502],{"class":295},[290,61043,1393],{"class":299},[290,61045,327],{"class":295},[290,61047,61048,61050,61052],{"class":163,"line":27209},[290,61049,4315],{"class":295},[290,61051,342],{"class":299},[290,61053,327],{"class":295},[290,61055,61056,61058,61060],{"class":163,"line":27220},[290,61057,431],{"class":295},[290,61059,9239],{"class":299},[290,61061,327],{"class":295},[290,61063,61064,61066,61068],{"class":163,"line":27235},[290,61065,431],{"class":295},[290,61067,285],{"class":299},[290,61069,327],{"class":295},[14,61071,61072],{},"The diagram below shows how the engine ranks these layers for normal declarations.",[133,61074,140,61076,140,61079,140,61082,140,61085,140,61087,140,61091,140,61093,140,61096,140,61098,140,61101,140,61103,140,61106,140,61108,140,61112,140,61116],{"viewBox":32013,"role":136,"ariaLabel":61075,"xmlns":138,"style":139},"A stack of cascade layers ordered reset, tokens, components, utilities, with later layers winning for normal declarations",[142,61077,61078],{},"Cascade layer stack order",[146,61080,61081],{},"Four stacked layers from reset at the bottom to utilities at the top, with arrows showing later layers win for normal styles.",[150,61083,61084],{"x":152,"y":153,"style":1781},"@layer reset, tokens, components, utilities;",[171,61086],{"x":174,"y":2637,"width":2613,"height":5139,"rx":5901,"fill":177,"opacity":3241,"stroke":167,"strokeWidth":1789},[150,61088,61090],{"x":152,"y":61089,"style":5883},"277","reset (lowest priority)",[171,61092],{"x":174,"y":17549,"width":2613,"height":5139,"rx":5901,"fill":177,"opacity":22004,"stroke":167,"strokeWidth":1789},[150,61094,61095],{"x":152,"y":8921,"style":5883},"tokens",[171,61097],{"x":174,"y":6708,"width":2613,"height":5139,"rx":5901,"fill":177,"opacity":11349,"stroke":167,"strokeWidth":1789},[150,61099,61100],{"x":152,"y":19652,"style":5883},"components + @container",[171,61102],{"x":174,"y":9105,"width":2613,"height":5139,"rx":5901,"fill":177,"opacity":199,"stroke":167,"strokeWidth":1789},[150,61104,61105],{"x":152,"y":27945,"style":5883},"utilities (wins for normal styles)",[163,61107],{"x1":4147,"y1":10283,"x2":4147,"y2":4196,"stroke":167,"strokeWidth":168,"opacity":21064},[61109,61110],"polygon",{"points":61111,"fill":167,"opacity":21064},"120,80 114,96 126,96",[150,61113,61115],{"x":6642,"y":13703,"style":4181,"transform":61114},"rotate(-90 96 195)","normal styles: later wins",[150,61117,61120],{"x":4176,"y":13703,"style":61118,"transform":61119},"text-anchor:middle;fill:currentColor;font:11px ui-monospace,monospace;opacity:0.7","rotate(90 600 195)","!important reverses order",[47,61122],{},[50,61124,61126],{"id":61125},"key-technique-callout-declaring-layer-order-up-front","Key technique callout: declaring layer order up front",[14,61128,61129,61130,61132,61133,61136],{},"The single most important line is ",[18,61131,61084],{}," at the very top. This statement names the layers and fixes their order before any of them contain rules. From that point the physical position of a ",[18,61134,61135],{},"@layer reset { ... }"," block in the file no longer matters — you could write the reset last and it would still rank lowest. This decoupling is what lets large teams append component styles anywhere without re-reasoning about specificity: priority is a property of the named layer, decided once. Forget this declaration and layers instead rank in first-seen order, which is fragile and bug-prone.",[47,61138],{},[50,61140,61142],{"id":61141},"variation-nested-layers-and-an-imported-reset","Variation: nested layers and an imported reset",[14,61144,61145,61146,42],{},"Larger systems sublayer for finer control, and external resets can be imported straight into a layer so third-party CSS never escapes your ordering. Tokens defined here also pair naturally with cross-area animation work, such as ",[27,61147,61148],{"href":4900},"fluid spacing tokens driving transition durations",[281,61150,61152],{"className":438,"code":61151,"language":440,"meta":286,"style":286},"\u002F* Pull a third-party reset directly into the reset layer. *\u002F\n@import url(\"modern-normalize.css\") layer(reset);\n\n\u002F* Sublayers: components.base ranks below components.overrides. *\u002F\n@layer components {\n  @layer base, overrides;\n  @layer base { .btn { padding: 0.5rem 1rem; } }\n  @layer overrides { .btn--ghost { background: transparent; } }\n}\n",[18,61153,61154,61159,61175,61179,61184,61190,61197,61222,61242],{"__ignoreMap":286},[290,61155,61156],{"class":163,"line":292},[290,61157,61158],{"class":455},"\u002F* Pull a third-party reset directly into the reset layer. *\u002F\n",[290,61160,61161,61164,61167,61169,61172],{"class":163,"line":330},[290,61162,61163],{"class":541},"@import",[290,61165,61166],{"class":461}," url",[290,61168,484],{"class":295},[290,61170,61171],{"class":310},"\"modern-normalize.css\"",[290,61173,61174],{"class":295},") layer(reset);\n",[290,61176,61177],{"class":163,"line":337},[290,61178,334],{"emptyLinePlaceholder":333},[290,61180,61181],{"class":163,"line":364},[290,61182,61183],{"class":455},"\u002F* Sublayers: components.base ranks below components.overrides. *\u002F\n",[290,61185,61186,61188],{"class":163,"line":386},[290,61187,12681],{"class":541},[290,61189,12747],{"class":295},[290,61191,61192,61194],{"class":163,"line":408},[290,61193,60432],{"class":541},[290,61195,61196],{"class":295}," base, overrides;\n",[290,61198,61199,61201,61204,61206,61208,61210,61212,61214,61216,61218,61220],{"class":163,"line":428},[290,61200,60432],{"class":541},[290,61202,61203],{"class":295}," base { ",[290,61205,15250],{"class":303},[290,61207,790],{"class":295},[290,61209,793],{"class":461},[290,61211,465],{"class":295},[290,61213,798],{"class":461},[290,61215,801],{"class":541},[290,61217,804],{"class":461},[290,61219,801],{"class":541},[290,61221,12260],{"class":295},[290,61223,61224,61226,61229,61232,61234,61236,61238,61240],{"class":163,"line":517},[290,61225,60432],{"class":541},[290,61227,61228],{"class":295}," overrides { ",[290,61230,61231],{"class":303},".btn--ghost",[290,61233,790],{"class":295},[290,61235,1217],{"class":461},[290,61237,465],{"class":295},[290,61239,14187],{"class":461},[290,61241,12260],{"class":295},[290,61243,61244],{"class":163,"line":523},[290,61245,620],{"class":295},[14,61247,61248,61249,61252,61253,61256,61257,61260,61261,61263,61264,61267,61268,61271],{},"Sublayers nest the same rule recursively: within ",[18,61250,61251],{},"components",", the ",[18,61254,61255],{},"overrides"," sublayer beats ",[18,61258,61259],{},"base",", while the whole ",[18,61262,61251],{}," layer still beats ",[18,61265,61266],{},"reset",". Anything imported with ",[18,61269,61270],{},"layer(reset)"," is permanently subordinate to your components, no matter how aggressive the vendor selectors are.",[47,61273],{},[50,61275,1299],{"id":1298},[14,61277,61278,61279,61281,61282,61285,61286,61288,61289,61292],{},"Cascade layers are Baseline and shipped in Chrome and Edge 99, Safari 15.4, and Firefox 97, so ",[18,61280,12681],{}," is safe across all current engines in 2026. The ",[18,61283,61284],{},"@import ... layer()"," syntax landed alongside the rule in the same versions. If you must support older browsers, note that an unlayered fallback stylesheet will simply win everywhere (unlayered beats layered), so guard with ",[18,61287,2086],{}," only if a layered rule must not leak — for example ",[18,61290,61291],{},"@supports at-rule detection via @layer"," is not yet a thing, so feature-detect by shipping the layered sheet to modern engines and a flattened sheet otherwise.",[47,61294],{},[50,61296,1316],{"id":1315},[14,61298,61299,61302],{},[62,61300,61301],{},"What problem do cascade layers actually solve?","\nThey let you declare cascade priority by authoring order rather than by selector specificity. A reset placed in an early layer can use simple selectors and still be overridden by component styles in a later layer, even when the reset's selectors are more specific.",[14,61304,61305,61308,61309,61311],{},[62,61306,61307],{},"Where do unlayered styles fall in the cascade?","\nUnlayered styles win over all layered styles of the same origin and importance. Anything you leave outside ",[18,61310,12681],{}," effectively becomes the highest-priority normal layer, which is why most architectures layer everything or keep only deliberate overrides unlayered.",[14,61313,61314,61317,61318,61320],{},[62,61315,61316],{},"Do container queries need their own layer?","\nNot necessarily. A ",[18,61319,12001],{}," rule carries the specificity of the selectors inside it, so it belongs in whichever layer those component styles live. Keeping component and container-query rules in the same layer keeps responsive overrides predictable.",[14,61322,61323,61329],{},[62,61324,61325,61326,61328],{},"How do ",[18,61327,3900],{}," interact with cascade layers?","\nImportant declarations reverse layer order: among important styles, earlier layers win over later ones. This is deliberate, letting a reset or utility layer assert non-negotiable rules that components cannot accidentally override.",[47,61331],{},[50,61333,1391],{"id":1390},[1393,61335,61336,61341,61346,61351],{},[1396,61337,61338,61340],{},[27,61339,56330],{"href":56329}," — the parent guide on building a spec-compliant baseline.",[1396,61342,61343,61345],{},[27,61344,8763],{"href":4036}," — scoping component queries that live inside these layers.",[1396,61347,61348,61350],{},[27,61349,10100],{"href":1426}," — a component layer in practice.",[1396,61352,61353,61355],{},[27,61354,3355],{"href":10147}," — cross-area guide on structuring the design tokens housed in the tokens layer.",[1430,61357,61358],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":286,"searchDepth":330,"depth":330,"links":61360},[61361,61362,61363,61364,61365,61366,61367],{"id":60334,"depth":330,"text":60335},{"id":4202,"depth":330,"text":4203},{"id":61125,"depth":330,"text":61126},{"id":61141,"depth":330,"text":61142},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Use @layer to order a CSS reset, design tokens, and components predictably so specificity battles disappear, with container queries layered cleanly on top.",{"seoTitle":61370,"datePublished":1447,"dateModified":1447,"faq":61371},"Cascade Layers for Reset and Tokens",[61372,61374,61376,61378],{"q":61301,"a":61373},"They let you declare cascade priority by authoring order rather than by selector specificity. A reset placed in an early layer can use simple selectors and still be overridden by component styles in a later layer, even when the reset's selectors are more specific.",{"q":61307,"a":61375},"Unlayered styles win over all layered styles of the same origin and importance. Anything you leave outside @layer effectively becomes the highest-priority normal layer, which is why most architectures layer everything or keep only deliberate overrides unlayered.",{"q":61316,"a":61377},"Not necessarily. A @container rule carries the specificity of the selectors inside it, so it belongs in whichever layer those component styles live. Keeping component and container-query rules in the same layer keeps responsive overrides predictable.",{"q":61379,"a":61380},"How do !important interact with cascade layers?","Important declarations reverse layer order: among important styles, earlier layers win over later ones. This is deliberate, letting a reset or utility layer assert non-negotiable rules that components cannot accidentally override.","\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcascade-layers-for-reset-and-tokens",{"title":60308,"description":61368},"mastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcascade-layers-for-reset-and-tokens\u002Findex","_P7VQopR5xirluTgfmdLUA5xNhf66Ym7eEvixT4I27k",{"id":61386,"title":61387,"body":61388,"description":63358,"extension":1444,"meta":63359,"navigation":333,"path":63371,"seo":63372,"stem":63373,"__hash__":63374},"content\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Findex.md","Modern CSS Reset Strategies: A Spec-Compliant Foundation",{"type":7,"value":61389,"toc":63345},[61390,61393,61399,61403,61427,61470,61472,61476,61483,61489,61689,61703,61705,61709,61719,61948,61957,61959,61963,61972,62255,62268,62270,62274,62282,62387,62391,62414,62416,62420,62426,62968,62973,62991,62993,62997,63072,63089,63091,63095,63156,63160,63203,63205,63207,63218,63230,63241,63258,63260,63262,63305,63307,63309,63342],[10,61391,61387],{"id":61392},"modern-css-reset-strategies-a-spec-compliant-foundation",[14,61394,61395,61396,61398],{},"A comprehensive breakdown of modern CSS reset strategies tailored for component-driven architectures. Unlike legacy resets that aggressively strip browser defaults, contemporary approaches prioritize spec-compliant baselines that preserve accessibility while establishing predictable styling boundaries. This guide bridges foundational reset techniques with responsive layout systems, ensuring seamless integration with ",[27,61397,11296],{"href":5777}," and downstream component architectures.",[14,61400,61401],{},[62,61402,53713],{},[1393,61404,61405,61408,61418,61424],{},[1396,61406,61407],{},"Shift from aggressive global resets to opinionated, accessibility-first baselines",[1396,61409,61410,61411,69,61414,61417],{},"Leverage CSS ",[18,61412,61413],{},"revert",[18,61415,61416],{},"revert-layer"," for precise cascade control",[1396,61419,61420,61421,61423],{},"Integrate resets with ",[18,61422,12681],{}," to eliminate specificity conflicts",[1396,61425,61426],{},"Align reset strategies with container query boundaries for isolated components",[133,61428,140,61431,140,61434,140,61437,140,61440,140,61442,140,61445,140,61448,140,61450,140,61453,140,61455,140,61457,140,61461,140,61463,140,61466],{"viewBox":61429,"role":136,"ariaLabel":61430,"xmlns":138,"style":139},"0 0 720 260","Cascade layer order placing reset at the lowest priority and utilities at the highest",[142,61432,61433],{},"Cascade layer priority order",[146,61435,61436],{},"Four stacked layers from reset at the bottom to utilities at the top, with priority increasing upward.",[150,61438,61439],{"x":152,"y":153,"style":1781},"@layer reset, base, components, utilities",[171,61441],{"x":174,"y":174,"width":2613,"height":32050,"rx":5901,"fill":177,"opacity":40019},[150,61443,61090],{"x":152,"y":61444,"style":19647},"223",[171,61446],{"x":174,"y":8904,"width":2613,"height":32050,"rx":5901,"fill":177,"opacity":61447},"0.20",[150,61449,61259],{"x":152,"y":18866,"style":19647},[171,61451],{"x":174,"y":2614,"width":2613,"height":32050,"rx":5901,"fill":177,"opacity":61452},"0.26",[150,61454,61251],{"x":152,"y":5909,"style":19647},[171,61456],{"x":174,"y":29485,"width":2613,"height":32050,"rx":5901,"fill":177,"opacity":14044},[150,61458,61460],{"x":152,"y":61459,"style":19647},"91","utilities (wins)",[163,61462],{"x1":4195,"y1":34174,"x2":4195,"y2":159,"stroke":167,"strokeWidth":168,"opacity":232},[61109,61464],{"points":61465,"fill":167,"opacity":232},"140,72 134,88 146,88",[150,61467,61469],{"x":4147,"y":8912,"style":10252,"transform":61468},"rotate(-90 120 155)","priority",[47,61471],{},[50,61473,61475],{"id":61474},"the-evolution-from-legacy-resets-to-modern-baselines","The Evolution from Legacy Resets to Modern Baselines",[14,61477,61478,61479,61482],{},"Traditional resets like ",[18,61480,61481],{},"* { margin: 0; padding: 0; }"," were born in an era of table-based layouts and inconsistent browser rendering engines. Today, they actively harm component isolation, strip native accessibility features (like focus rings and semantic spacing), and force developers to manually re-implement baseline typography and form controls.",[14,61484,61485,61486,61488],{},"Modern CSS reset strategies embrace the browser's default stylesheet as a starting point, neutralizing only the properties that cause layout unpredictability. The cornerstone of this approach is ",[18,61487,57915],{}," combined with zero-specificity selectors.",[281,61490,61492],{"className":438,"code":61491,"language":440,"meta":286,"style":286},"\u002F* ❌ Legacy: Breaks accessibility & forces manual re-implementation *\u002F\n*,\n*::before,\n*::after {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n}\n\n\u002F* ✅ Modern: Zero-specificity baseline preserving native semantics *\u002F\n@layer reset {\n  *,\n  *::before,\n  *::after {\n    box-sizing: border-box;\n  }\n\n  :where(body) {\n    margin: 0;\n    font-family:\n      system-ui,\n      -apple-system,\n      sans-serif;\n    line-height: 1.5;\n    -webkit-font-smoothing: antialiased;\n  }\n}\n",[18,61493,61494,61499,61505,61513,61521,61531,61541,61552,61556,61560,61565,61571,61577,61585,61593,61603,61607,61611,61622,61632,61638,61645,61652,61659,61669,61681,61685],{"__ignoreMap":286},[290,61495,61496],{"class":163,"line":292},[290,61497,61498],{"class":455},"\u002F* ❌ Legacy: Breaks accessibility & forces manual re-implementation *\u002F\n",[290,61500,61501,61503],{"class":163,"line":330},[290,61502,4415],{"class":299},[290,61504,548],{"class":295},[290,61506,61507,61509,61511],{"class":163,"line":337},[290,61508,4415],{"class":299},[290,61510,2811],{"class":303},[290,61512,548],{"class":295},[290,61514,61515,61517,61519],{"class":163,"line":364},[290,61516,4415],{"class":299},[290,61518,2412],{"class":303},[290,61520,450],{"class":295},[290,61522,61523,61525,61527,61529],{"class":163,"line":386},[290,61524,32235],{"class":461},[290,61526,465],{"class":295},[290,61528,487],{"class":461},[290,61530,471],{"class":295},[290,61532,61533,61535,61537,61539],{"class":163,"line":408},[290,61534,10433],{"class":461},[290,61536,465],{"class":295},[290,61538,487],{"class":461},[290,61540,471],{"class":295},[290,61542,61543,61546,61548,61550],{"class":163,"line":428},[290,61544,61545],{"class":461},"  box-sizing",[290,61547,465],{"class":295},[290,61549,46440],{"class":461},[290,61551,471],{"class":295},[290,61553,61554],{"class":163,"line":517},[290,61555,620],{"class":295},[290,61557,61558],{"class":163,"line":523},[290,61559,334],{"emptyLinePlaceholder":333},[290,61561,61562],{"class":163,"line":532},[290,61563,61564],{"class":455},"\u002F* ✅ Modern: Zero-specificity baseline preserving native semantics *\u002F\n",[290,61566,61567,61569],{"class":163,"line":551},[290,61568,12681],{"class":541},[290,61570,60451],{"class":295},[290,61572,61573,61575],{"class":163,"line":586},[290,61574,2802],{"class":299},[290,61576,548],{"class":295},[290,61578,61579,61581,61583],{"class":163,"line":602},[290,61580,2802],{"class":299},[290,61582,2811],{"class":303},[290,61584,548],{"class":295},[290,61586,61587,61589,61591],{"class":163,"line":617},[290,61588,2802],{"class":299},[290,61590,2412],{"class":303},[290,61592,450],{"class":295},[290,61594,61595,61597,61599,61601],{"class":163,"line":623},[290,61596,56080],{"class":461},[290,61598,465],{"class":295},[290,61600,46440],{"class":461},[290,61602,471],{"class":295},[290,61604,61605],{"class":163,"line":628},[290,61606,771],{"class":295},[290,61608,61609],{"class":163,"line":634},[290,61610,334],{"emptyLinePlaceholder":333},[290,61612,61613,61616,61618,61620],{"class":163,"line":649},[290,61614,61615],{"class":303},"  :where",[290,61617,484],{"class":295},[290,61619,9239],{"class":299},[290,61621,646],{"class":295},[290,61623,61624,61626,61628,61630],{"class":163,"line":660},[290,61625,39260],{"class":461},[290,61627,465],{"class":295},[290,61629,487],{"class":461},[290,61631,471],{"class":295},[290,61633,61634,61636],{"class":163,"line":688},[290,61635,56111],{"class":461},[290,61637,529],{"class":295},[290,61639,61640,61643],{"class":163,"line":693},[290,61641,61642],{"class":461},"      system-ui",[290,61644,548],{"class":295},[290,61646,61647,61650],{"class":163,"line":698},[290,61648,61649],{"class":461},"      -apple-system",[290,61651,548],{"class":295},[290,61653,61654,61657],{"class":163,"line":704},[290,61655,61656],{"class":461},"      sans-serif",[290,61658,471],{"class":295},[290,61660,61661,61663,61665,61667],{"class":163,"line":710},[290,61662,39248],{"class":461},[290,61664,465],{"class":295},[290,61666,168],{"class":461},[290,61668,471],{"class":295},[290,61670,61671,61674,61676,61679],{"class":163,"line":717},[290,61672,61673],{"class":461},"    -webkit-font-smoothing",[290,61675,465],{"class":295},[290,61677,61678],{"class":461},"antialiased",[290,61680,471],{"class":295},[290,61682,61683],{"class":163,"line":730},[290,61684,771],{"class":295},[290,61686,61687],{"class":163,"line":742},[290,61688,620],{"class":295},[14,61690,61691,61692,61695,61696,61699,61700,61702],{},"By using ",[18,61693,61694],{},":where()",", we guarantee ",[18,61697,61698],{},"0,0,0"," specificity. This means any component-level style will naturally override the reset without ",[18,61701,3900],{}," hacks or excessive selector nesting.",[47,61704],{},[50,61706,61708],{"id":61707},"implementing-css-cascade-layers-for-reset-management","Implementing CSS Cascade Layers for Reset Management",[14,61710,61711,61712,61714,61715,42],{},"Specificity wars are a legacy problem. The ",[18,61713,12681],{}," rule introduces explicit cascade ordering, allowing you to mathematically guarantee that your reset always sits at the bottom of the specificity hierarchy. Layers do double duty as the home for your design tokens too; for the full architecture that unifies reset and token layers, see ",[27,61716,61718],{"href":61717},"\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Fcascade-layers-for-reset-and-tokens\u002F","cascade layers for reset and tokens",[281,61720,61722],{"className":438,"code":61721,"language":440,"meta":286,"style":286},"\u002F* 1. Declare layers in execution order *\u002F\n@layer reset, base, components, utilities;\n\n\u002F* 2. Inject reset into the lowest tier *\u002F\n@layer reset {\n  *,\n  *::before,\n  *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n  :where(html) {\n    -moz-text-size-adjust: none;\n    -webkit-text-size-adjust: none;\n    text-size-adjust: none;\n  }\n  :where(img, picture, video, canvas, svg) {\n    display: block;\n    max-width: 100%;\n  }\n  :where(button) {\n    all: revert;\n    cursor: pointer;\n  }\n}\n",[18,61723,61724,61729,61736,61740,61745,61751,61757,61765,61773,61783,61793,61803,61807,61817,61828,61839,61850,61854,61882,61892,61904,61908,61918,61929,61940,61944],{"__ignoreMap":286},[290,61725,61726],{"class":163,"line":292},[290,61727,61728],{"class":455},"\u002F* 1. Declare layers in execution order *\u002F\n",[290,61730,61731,61733],{"class":163,"line":330},[290,61732,12681],{"class":541},[290,61734,61735],{"class":295}," reset, base, components, utilities;\n",[290,61737,61738],{"class":163,"line":337},[290,61739,334],{"emptyLinePlaceholder":333},[290,61741,61742],{"class":163,"line":364},[290,61743,61744],{"class":455},"\u002F* 2. Inject reset into the lowest tier *\u002F\n",[290,61746,61747,61749],{"class":163,"line":386},[290,61748,12681],{"class":541},[290,61750,60451],{"class":295},[290,61752,61753,61755],{"class":163,"line":408},[290,61754,2802],{"class":299},[290,61756,548],{"class":295},[290,61758,61759,61761,61763],{"class":163,"line":428},[290,61760,2802],{"class":299},[290,61762,2811],{"class":303},[290,61764,548],{"class":295},[290,61766,61767,61769,61771],{"class":163,"line":517},[290,61768,2802],{"class":299},[290,61770,2412],{"class":303},[290,61772,450],{"class":295},[290,61774,61775,61777,61779,61781],{"class":163,"line":523},[290,61776,56080],{"class":461},[290,61778,465],{"class":295},[290,61780,46440],{"class":461},[290,61782,471],{"class":295},[290,61784,61785,61787,61789,61791],{"class":163,"line":532},[290,61786,39260],{"class":461},[290,61788,465],{"class":295},[290,61790,487],{"class":461},[290,61792,471],{"class":295},[290,61794,61795,61797,61799,61801],{"class":163,"line":551},[290,61796,36040],{"class":461},[290,61798,465],{"class":295},[290,61800,487],{"class":461},[290,61802,471],{"class":295},[290,61804,61805],{"class":163,"line":586},[290,61806,771],{"class":295},[290,61808,61809,61811,61813,61815],{"class":163,"line":602},[290,61810,61615],{"class":303},[290,61812,484],{"class":295},[290,61814,285],{"class":299},[290,61816,646],{"class":295},[290,61818,61819,61822,61824,61826],{"class":163,"line":617},[290,61820,61821],{"class":461},"    -moz-text-size-adjust",[290,61823,465],{"class":295},[290,61825,72],{"class":461},[290,61827,471],{"class":295},[290,61829,61830,61833,61835,61837],{"class":163,"line":623},[290,61831,61832],{"class":461},"    -webkit-text-size-adjust",[290,61834,465],{"class":295},[290,61836,72],{"class":461},[290,61838,471],{"class":295},[290,61840,61841,61844,61846,61848],{"class":163,"line":628},[290,61842,61843],{"class":461},"    text-size-adjust",[290,61845,465],{"class":295},[290,61847,72],{"class":461},[290,61849,471],{"class":295},[290,61851,61852],{"class":163,"line":634},[290,61853,771],{"class":295},[290,61855,61856,61858,61860,61862,61864,61867,61869,61871,61873,61876,61878,61880],{"class":163,"line":649},[290,61857,61615],{"class":303},[290,61859,484],{"class":295},[290,61861,136],{"class":299},[290,61863,569],{"class":295},[290,61865,61866],{"class":299},"picture",[290,61868,569],{"class":295},[290,61870,4259],{"class":299},[290,61872,569],{"class":295},[290,61874,61875],{"class":299},"canvas",[290,61877,569],{"class":295},[290,61879,133],{"class":299},[290,61881,646],{"class":295},[290,61883,61884,61886,61888,61890],{"class":163,"line":660},[290,61885,34590],{"class":461},[290,61887,465],{"class":295},[290,61889,68],{"class":461},[290,61891,471],{"class":295},[290,61893,61894,61896,61898,61900,61902],{"class":163,"line":688},[290,61895,44622],{"class":461},[290,61897,465],{"class":295},[290,61899,165],{"class":461},[290,61901,11018],{"class":541},[290,61903,471],{"class":295},[290,61905,61906],{"class":163,"line":693},[290,61907,771],{"class":295},[290,61909,61910,61912,61914,61916],{"class":163,"line":698},[290,61911,61615],{"class":303},[290,61913,484],{"class":295},[290,61915,300],{"class":299},[290,61917,646],{"class":295},[290,61919,61920,61923,61925,61927],{"class":163,"line":704},[290,61921,61922],{"class":461},"    all",[290,61924,465],{"class":295},[290,61926,61413],{"class":461},[290,61928,471],{"class":295},[290,61930,61931,61934,61936,61938],{"class":163,"line":710},[290,61932,61933],{"class":461},"    cursor",[290,61935,465],{"class":295},[290,61937,18849],{"class":461},[290,61939,471],{"class":295},[290,61941,61942],{"class":163,"line":717},[290,61943,771],{"class":295},[290,61945,61946],{"class":163,"line":730},[290,61947,620],{"class":295},[14,61949,61950,61953,61954,61956],{},[62,61951,61952],{},"Progressive Enhancement & Fallbacks:"," Browsers that don't support ",[18,61955,12681],{}," (Chrome \u003C99, Safari \u003C15.4) silently ignore the at-rule and treat the block's contents as unlayered rules with the specificity and cascade order they would have without any layer wrapping. To ensure graceful degradation, place your reset stylesheet first in the DOM before any framework or component CSS so it is evaluated earliest in the cascade.",[47,61958],{},[50,61960,61962],{"id":61961},"spec-compliant-reset-properties-modern-selectors","Spec-Compliant Reset Properties & Modern Selectors",[14,61964,61965,61966,3942,61968,61971],{},"Modern UI development requires surgical resets rather than blanket overrides. By combining ",[18,61967,61694],{},[18,61969,61970],{},"all: revert",", we can safely neutralize inherited styles while respecting the browser's native stylesheet for interactive elements.",[281,61973,61975],{"className":438,"code":61974,"language":440,"meta":286,"style":286},"@layer reset {\n  \u002F* Neutralize default spacing without specificity *\u002F\n  :where(h1, h2, h3, h4, h5, h6, p, ul, ol, li, figure, blockquote, dl, dd) {\n    margin: 0;\n  }\n\n  \u002F* Intrinsic sizing for media elements *\u002F\n  :where(img, picture, video, canvas, svg) {\n    display: block;\n    max-width: 100%;\n    height: auto;\n  }\n\n  \u002F* Safe form reset preserving UX *\u002F\n  :where(input, button, textarea, select) {\n    font: inherit;\n    color: inherit;\n    appearance: auto;\n  }\n\n  \u002F* Reset button to native defaults, then apply baseline *\u002F\n  :where(button) {\n    all: revert;\n    cursor: pointer;\n  }\n}\n",[18,61976,61977,61983,61988,62056,62066,62070,62074,62079,62105,62115,62127,62137,62141,62145,62150,62173,62183,62193,62204,62208,62212,62217,62227,62237,62247,62251],{"__ignoreMap":286},[290,61978,61979,61981],{"class":163,"line":292},[290,61980,12681],{"class":541},[290,61982,60451],{"class":295},[290,61984,61985],{"class":163,"line":330},[290,61986,61987],{"class":455},"  \u002F* Neutralize default spacing without specificity *\u002F\n",[290,61989,61990,61992,61994,61996,61998,62000,62002,62004,62006,62009,62011,62014,62016,62019,62021,62023,62025,62027,62029,62031,62033,62035,62037,62039,62041,62044,62046,62049,62051,62054],{"class":163,"line":337},[290,61991,61615],{"class":303},[290,61993,484],{"class":295},[290,61995,10],{"class":299},[290,61997,569],{"class":295},[290,61999,50],{"class":299},[290,62001,569],{"class":295},[290,62003,2757],{"class":299},[290,62005,569],{"class":295},[290,62007,62008],{"class":299},"h4",[290,62010,569],{"class":295},[290,62012,62013],{"class":299},"h5",[290,62015,569],{"class":295},[290,62017,62018],{"class":299},"h6",[290,62020,569],{"class":295},[290,62022,14],{"class":299},[290,62024,569],{"class":295},[290,62026,1393],{"class":299},[290,62028,569],{"class":295},[290,62030,3017],{"class":299},[290,62032,569],{"class":295},[290,62034,1396],{"class":299},[290,62036,569],{"class":295},[290,62038,4243],{"class":299},[290,62040,569],{"class":295},[290,62042,62043],{"class":299},"blockquote",[290,62045,569],{"class":295},[290,62047,62048],{"class":299},"dl",[290,62050,569],{"class":295},[290,62052,62053],{"class":299},"dd",[290,62055,646],{"class":295},[290,62057,62058,62060,62062,62064],{"class":163,"line":364},[290,62059,39260],{"class":461},[290,62061,465],{"class":295},[290,62063,487],{"class":461},[290,62065,471],{"class":295},[290,62067,62068],{"class":163,"line":386},[290,62069,771],{"class":295},[290,62071,62072],{"class":163,"line":408},[290,62073,334],{"emptyLinePlaceholder":333},[290,62075,62076],{"class":163,"line":428},[290,62077,62078],{"class":455},"  \u002F* Intrinsic sizing for media elements *\u002F\n",[290,62080,62081,62083,62085,62087,62089,62091,62093,62095,62097,62099,62101,62103],{"class":163,"line":517},[290,62082,61615],{"class":303},[290,62084,484],{"class":295},[290,62086,136],{"class":299},[290,62088,569],{"class":295},[290,62090,61866],{"class":299},[290,62092,569],{"class":295},[290,62094,4259],{"class":299},[290,62096,569],{"class":295},[290,62098,61875],{"class":299},[290,62100,569],{"class":295},[290,62102,133],{"class":299},[290,62104,646],{"class":295},[290,62106,62107,62109,62111,62113],{"class":163,"line":523},[290,62108,34590],{"class":461},[290,62110,465],{"class":295},[290,62112,68],{"class":461},[290,62114,471],{"class":295},[290,62116,62117,62119,62121,62123,62125],{"class":163,"line":532},[290,62118,44622],{"class":461},[290,62120,465],{"class":295},[290,62122,165],{"class":461},[290,62124,11018],{"class":541},[290,62126,471],{"class":295},[290,62128,62129,62131,62133,62135],{"class":163,"line":551},[290,62130,39388],{"class":461},[290,62132,465],{"class":295},[290,62134,250],{"class":461},[290,62136,471],{"class":295},[290,62138,62139],{"class":163,"line":586},[290,62140,771],{"class":295},[290,62142,62143],{"class":163,"line":602},[290,62144,334],{"emptyLinePlaceholder":333},[290,62146,62147],{"class":163,"line":617},[290,62148,62149],{"class":455},"  \u002F* Safe form reset preserving UX *\u002F\n",[290,62151,62152,62154,62156,62158,62160,62162,62164,62167,62169,62171],{"class":163,"line":623},[290,62153,61615],{"class":303},[290,62155,484],{"class":295},[290,62157,18985],{"class":299},[290,62159,569],{"class":295},[290,62161,300],{"class":299},[290,62163,569],{"class":295},[290,62165,62166],{"class":299},"textarea",[290,62168,569],{"class":295},[290,62170,25394],{"class":299},[290,62172,646],{"class":295},[290,62174,62175,62177,62179,62181],{"class":163,"line":628},[290,62176,44461],{"class":461},[290,62178,465],{"class":295},[290,62180,1970],{"class":461},[290,62182,471],{"class":295},[290,62184,62185,62187,62189,62191],{"class":163,"line":634},[290,62186,36064],{"class":461},[290,62188,465],{"class":295},[290,62190,1970],{"class":461},[290,62192,471],{"class":295},[290,62194,62195,62198,62200,62202],{"class":163,"line":649},[290,62196,62197],{"class":461},"    appearance",[290,62199,465],{"class":295},[290,62201,250],{"class":461},[290,62203,471],{"class":295},[290,62205,62206],{"class":163,"line":660},[290,62207,771],{"class":295},[290,62209,62210],{"class":163,"line":688},[290,62211,334],{"emptyLinePlaceholder":333},[290,62213,62214],{"class":163,"line":693},[290,62215,62216],{"class":455},"  \u002F* Reset button to native defaults, then apply baseline *\u002F\n",[290,62218,62219,62221,62223,62225],{"class":163,"line":698},[290,62220,61615],{"class":303},[290,62222,484],{"class":295},[290,62224,300],{"class":299},[290,62226,646],{"class":295},[290,62228,62229,62231,62233,62235],{"class":163,"line":704},[290,62230,61922],{"class":461},[290,62232,465],{"class":295},[290,62234,61413],{"class":461},[290,62236,471],{"class":295},[290,62238,62239,62241,62243,62245],{"class":163,"line":710},[290,62240,61933],{"class":461},[290,62242,465],{"class":295},[290,62244,18849],{"class":461},[290,62246,471],{"class":295},[290,62248,62249],{"class":163,"line":717},[290,62250,771],{"class":295},[290,62252,62253],{"class":163,"line":730},[290,62254,620],{"class":295},[14,62256,62257,62258,62260,62261,569,62263,8393,62265,62267],{},"When connecting reset logic to ",[27,62259,26284],{"href":25340},", predictable scaling becomes achievable. By ensuring ",[18,62262,46435],{},[18,62264,41191],{},[18,62266,46459],{}," inheritance are normalized upfront, container queries can reliably calculate available inline space without fighting legacy margin collapse or unexpected padding bleed.",[47,62269],{},[50,62271,62273],{"id":62272},"integrating-resets-with-container-query-boundaries","Integrating Resets with Container Query Boundaries",[14,62275,62276,62277,69,62279,62281],{},"Global resets often interfere with ",[18,62278,24401],{},[18,62280,26072],{}," declarations, especially when embedding third-party widgets or micro-interactions inside isolated UI shells. Scoping reset logic to component boundaries prevents style leakage and ensures responsive behavior remains deterministic.",[281,62283,62285],{"className":438,"code":62284,"language":440,"meta":286,"style":286},".card {\n  container-type: inline-size;\n  container-name: card;\n}\n\n@container card (min-width: 400px) {\n  @layer reset {\n    :where(h2, p, button) {\n      margin: 0;\n      padding: 0;\n      font-size: revert;\n    }\n  }\n}\n",[18,62286,62287,62293,62299,62305,62309,62313,62319,62325,62344,62354,62364,62375,62379,62383],{"__ignoreMap":286},[290,62288,62289,62291],{"class":163,"line":292},[290,62290,11528],{"class":303},[290,62292,450],{"class":295},[290,62294,62295,62297],{"class":163,"line":330},[290,62296,11509],{"class":461},[290,62298,25565],{"class":295},[290,62300,62301,62303],{"class":163,"line":337},[290,62302,25575],{"class":461},[290,62304,37983],{"class":295},[290,62306,62307],{"class":163,"line":364},[290,62308,620],{"class":295},[290,62310,62311],{"class":163,"line":386},[290,62312,334],{"emptyLinePlaceholder":333},[290,62314,62315,62317],{"class":163,"line":408},[290,62316,12001],{"class":541},[290,62318,37998],{"class":295},[290,62320,62321,62323],{"class":163,"line":428},[290,62322,60432],{"class":541},[290,62324,60451],{"class":295},[290,62326,62327,62330,62332,62334,62336,62338,62340,62342],{"class":163,"line":517},[290,62328,62329],{"class":303},"    :where",[290,62331,484],{"class":295},[290,62333,50],{"class":299},[290,62335,569],{"class":295},[290,62337,14],{"class":299},[290,62339,569],{"class":295},[290,62341,300],{"class":299},[290,62343,646],{"class":295},[290,62345,62346,62348,62350,62352],{"class":163,"line":523},[290,62347,60723],{"class":461},[290,62349,465],{"class":295},[290,62351,487],{"class":461},[290,62353,471],{"class":295},[290,62355,62356,62358,62360,62362],{"class":163,"line":532},[290,62357,42626],{"class":461},[290,62359,465],{"class":295},[290,62361,487],{"class":461},[290,62363,471],{"class":295},[290,62365,62366,62369,62371,62373],{"class":163,"line":551},[290,62367,62368],{"class":461},"      font-size",[290,62370,465],{"class":295},[290,62372,61413],{"class":461},[290,62374,471],{"class":295},[290,62376,62377],{"class":163,"line":586},[290,62378,8200],{"class":295},[290,62380,62381],{"class":163,"line":602},[290,62382,771],{"class":295},[290,62384,62385],{"class":163,"line":617},[290,62386,620],{"class":295},[14,62388,62389],{},[62,62390,1681],{},[1393,62392,62393,62396,62404],{},[1396,62394,62395],{},"Use CSS nesting to scope resets directly inside component blocks.",[1396,62397,62398,62399,2351,62401,62403],{},"Avoid resetting ",[18,62400,50124],{},[18,62402,50110],{}," inside container contexts; let them inherit from the document root to maintain typographic rhythm.",[1396,62405,62406,62407,62409,62410,62413],{},"Test reset impact on fluid typography (",[18,62408,12276],{},") by verifying that ",[18,62411,62412],{},"font-size: revert"," correctly falls back to the computed container-relative scale rather than viewport breakpoints.",[47,62415],{},[50,62417,62419],{"id":62418},"production-ready-reset-patterns-for-component-libraries","Production-Ready Reset Patterns for Component Libraries",[14,62421,62422,62423,62425],{},"Design systems require atomic, versioned reset strategies that align with ",[27,62424,38098],{"href":38097}," for scalable UI development. Below is a complete, copy-paste-ready baseline optimized for modern frameworks and design tokens.",[281,62427,62429],{"className":438,"code":62428,"language":440,"meta":286,"style":286},"\u002F* modern-reset.css *\u002F\n@layer reset, base, components;\n\n@layer reset {\n  *,\n  *::before,\n  *::after {\n    box-sizing: border-box;\n    margin: 0;\n    padding: 0;\n  }\n\n  :where(html) {\n    -moz-text-size-adjust: none;\n    -webkit-text-size-adjust: none;\n    text-size-adjust: none;\n    scroll-behavior: smooth;\n  }\n\n  :where(body) {\n    min-height: 100dvh;\n    font-family:\n      system-ui,\n      -apple-system,\n      \"Segoe UI\",\n      Roboto,\n      Helvetica,\n      Arial,\n      sans-serif;\n    line-height: 1.5;\n    -webkit-font-smoothing: antialiased;\n    text-rendering: optimizeLegibility;\n  }\n\n  :where(img, picture, video, canvas, svg) {\n    display: block;\n    max-width: 100%;\n    height: auto;\n  }\n\n  :where(p, h1, h2, h3, h4, h5, h6, li, ul, ol, blockquote, figure, dl, dd) {\n    margin: 0;\n  }\n\n  :where(input, button, textarea, select) {\n    font: inherit;\n    color: inherit;\n    appearance: auto;\n  }\n\n  :where(button) {\n    all: revert;\n    cursor: pointer;\n  }\n\n  :where(:focus-visible) {\n    outline: revert;\n    outline-offset: 2px;\n  }\n}\n",[18,62430,62431,62436,62443,62447,62453,62459,62467,62475,62485,62495,62505,62509,62513,62523,62533,62543,62553,62564,62568,62572,62582,62594,62600,62606,62612,62619,62624,62631,62638,62644,62654,62664,62676,62680,62684,62710,62720,62732,62742,62746,62750,62812,62822,62826,62830,62852,62862,62872,62882,62886,62890,62900,62910,62920,62924,62928,62938,62948,62960,62964],{"__ignoreMap":286},[290,62432,62433],{"class":163,"line":292},[290,62434,62435],{"class":455},"\u002F* modern-reset.css *\u002F\n",[290,62437,62438,62440],{"class":163,"line":330},[290,62439,12681],{"class":541},[290,62441,62442],{"class":295}," reset, base, components;\n",[290,62444,62445],{"class":163,"line":337},[290,62446,334],{"emptyLinePlaceholder":333},[290,62448,62449,62451],{"class":163,"line":364},[290,62450,12681],{"class":541},[290,62452,60451],{"class":295},[290,62454,62455,62457],{"class":163,"line":386},[290,62456,2802],{"class":299},[290,62458,548],{"class":295},[290,62460,62461,62463,62465],{"class":163,"line":408},[290,62462,2802],{"class":299},[290,62464,2811],{"class":303},[290,62466,548],{"class":295},[290,62468,62469,62471,62473],{"class":163,"line":428},[290,62470,2802],{"class":299},[290,62472,2412],{"class":303},[290,62474,450],{"class":295},[290,62476,62477,62479,62481,62483],{"class":163,"line":517},[290,62478,56080],{"class":461},[290,62480,465],{"class":295},[290,62482,46440],{"class":461},[290,62484,471],{"class":295},[290,62486,62487,62489,62491,62493],{"class":163,"line":523},[290,62488,39260],{"class":461},[290,62490,465],{"class":295},[290,62492,487],{"class":461},[290,62494,471],{"class":295},[290,62496,62497,62499,62501,62503],{"class":163,"line":532},[290,62498,36040],{"class":461},[290,62500,465],{"class":295},[290,62502,487],{"class":461},[290,62504,471],{"class":295},[290,62506,62507],{"class":163,"line":551},[290,62508,771],{"class":295},[290,62510,62511],{"class":163,"line":586},[290,62512,334],{"emptyLinePlaceholder":333},[290,62514,62515,62517,62519,62521],{"class":163,"line":602},[290,62516,61615],{"class":303},[290,62518,484],{"class":295},[290,62520,285],{"class":299},[290,62522,646],{"class":295},[290,62524,62525,62527,62529,62531],{"class":163,"line":617},[290,62526,61821],{"class":461},[290,62528,465],{"class":295},[290,62530,72],{"class":461},[290,62532,471],{"class":295},[290,62534,62535,62537,62539,62541],{"class":163,"line":623},[290,62536,61832],{"class":461},[290,62538,465],{"class":295},[290,62540,72],{"class":461},[290,62542,471],{"class":295},[290,62544,62545,62547,62549,62551],{"class":163,"line":628},[290,62546,61843],{"class":461},[290,62548,465],{"class":295},[290,62550,72],{"class":461},[290,62552,471],{"class":295},[290,62554,62555,62557,62559,62562],{"class":163,"line":634},[290,62556,2871],{"class":461},[290,62558,465],{"class":295},[290,62560,62561],{"class":461},"smooth",[290,62563,471],{"class":295},[290,62565,62566],{"class":163,"line":649},[290,62567,771],{"class":295},[290,62569,62570],{"class":163,"line":660},[290,62571,334],{"emptyLinePlaceholder":333},[290,62573,62574,62576,62578,62580],{"class":163,"line":688},[290,62575,61615],{"class":303},[290,62577,484],{"class":295},[290,62579,9239],{"class":299},[290,62581,646],{"class":295},[290,62583,62584,62586,62588,62590,62592],{"class":163,"line":693},[290,62585,20469],{"class":461},[290,62587,465],{"class":295},[290,62589,165],{"class":461},[290,62591,46517],{"class":541},[290,62593,471],{"class":295},[290,62595,62596,62598],{"class":163,"line":698},[290,62597,56111],{"class":461},[290,62599,529],{"class":295},[290,62601,62602,62604],{"class":163,"line":704},[290,62603,61642],{"class":461},[290,62605,548],{"class":295},[290,62607,62608,62610],{"class":163,"line":710},[290,62609,61649],{"class":461},[290,62611,548],{"class":295},[290,62613,62614,62617],{"class":163,"line":717},[290,62615,62616],{"class":310},"      \"Segoe UI\"",[290,62618,548],{"class":295},[290,62620,62621],{"class":163,"line":730},[290,62622,62623],{"class":295},"      Roboto,\n",[290,62625,62626,62629],{"class":163,"line":742},[290,62627,62628],{"class":461},"      Helvetica",[290,62630,548],{"class":295},[290,62632,62633,62636],{"class":163,"line":768},[290,62634,62635],{"class":461},"      Arial",[290,62637,548],{"class":295},[290,62639,62640,62642],{"class":163,"line":774},[290,62641,61656],{"class":461},[290,62643,471],{"class":295},[290,62645,62646,62648,62650,62652],{"class":163,"line":779},[290,62647,39248],{"class":461},[290,62649,465],{"class":295},[290,62651,168],{"class":461},[290,62653,471],{"class":295},[290,62655,62656,62658,62660,62662],{"class":163,"line":784},[290,62657,61673],{"class":461},[290,62659,465],{"class":295},[290,62661,61678],{"class":461},[290,62663,471],{"class":295},[290,62665,62666,62669,62671,62674],{"class":163,"line":812},[290,62667,62668],{"class":461},"    text-rendering",[290,62670,465],{"class":295},[290,62672,62673],{"class":461},"optimizeLegibility",[290,62675,471],{"class":295},[290,62677,62678],{"class":163,"line":860},[290,62679,771],{"class":295},[290,62681,62682],{"class":163,"line":865},[290,62683,334],{"emptyLinePlaceholder":333},[290,62685,62686,62688,62690,62692,62694,62696,62698,62700,62702,62704,62706,62708],{"class":163,"line":871},[290,62687,61615],{"class":303},[290,62689,484],{"class":295},[290,62691,136],{"class":299},[290,62693,569],{"class":295},[290,62695,61866],{"class":299},[290,62697,569],{"class":295},[290,62699,4259],{"class":299},[290,62701,569],{"class":295},[290,62703,61875],{"class":299},[290,62705,569],{"class":295},[290,62707,133],{"class":299},[290,62709,646],{"class":295},[290,62711,62712,62714,62716,62718],{"class":163,"line":880},[290,62713,34590],{"class":461},[290,62715,465],{"class":295},[290,62717,68],{"class":461},[290,62719,471],{"class":295},[290,62721,62722,62724,62726,62728,62730],{"class":163,"line":896},[290,62723,44622],{"class":461},[290,62725,465],{"class":295},[290,62727,165],{"class":461},[290,62729,11018],{"class":541},[290,62731,471],{"class":295},[290,62733,62734,62736,62738,62740],{"class":163,"line":4734},[290,62735,39388],{"class":461},[290,62737,465],{"class":295},[290,62739,250],{"class":461},[290,62741,471],{"class":295},[290,62743,62744],{"class":163,"line":4742},[290,62745,771],{"class":295},[290,62747,62748],{"class":163,"line":4761},[290,62749,334],{"emptyLinePlaceholder":333},[290,62751,62752,62754,62756,62758,62760,62762,62764,62766,62768,62770,62772,62774,62776,62778,62780,62782,62784,62786,62788,62790,62792,62794,62796,62798,62800,62802,62804,62806,62808,62810],{"class":163,"line":4766},[290,62753,61615],{"class":303},[290,62755,484],{"class":295},[290,62757,14],{"class":299},[290,62759,569],{"class":295},[290,62761,10],{"class":299},[290,62763,569],{"class":295},[290,62765,50],{"class":299},[290,62767,569],{"class":295},[290,62769,2757],{"class":299},[290,62771,569],{"class":295},[290,62773,62008],{"class":299},[290,62775,569],{"class":295},[290,62777,62013],{"class":299},[290,62779,569],{"class":295},[290,62781,62018],{"class":299},[290,62783,569],{"class":295},[290,62785,1396],{"class":299},[290,62787,569],{"class":295},[290,62789,1393],{"class":299},[290,62791,569],{"class":295},[290,62793,3017],{"class":299},[290,62795,569],{"class":295},[290,62797,62043],{"class":299},[290,62799,569],{"class":295},[290,62801,4243],{"class":299},[290,62803,569],{"class":295},[290,62805,62048],{"class":299},[290,62807,569],{"class":295},[290,62809,62053],{"class":299},[290,62811,646],{"class":295},[290,62813,62814,62816,62818,62820],{"class":163,"line":4786},[290,62815,39260],{"class":461},[290,62817,465],{"class":295},[290,62819,487],{"class":461},[290,62821,471],{"class":295},[290,62823,62824],{"class":163,"line":9635},[290,62825,771],{"class":295},[290,62827,62828],{"class":163,"line":9641},[290,62829,334],{"emptyLinePlaceholder":333},[290,62831,62832,62834,62836,62838,62840,62842,62844,62846,62848,62850],{"class":163,"line":9647},[290,62833,61615],{"class":303},[290,62835,484],{"class":295},[290,62837,18985],{"class":299},[290,62839,569],{"class":295},[290,62841,300],{"class":299},[290,62843,569],{"class":295},[290,62845,62166],{"class":299},[290,62847,569],{"class":295},[290,62849,25394],{"class":299},[290,62851,646],{"class":295},[290,62853,62854,62856,62858,62860],{"class":163,"line":9665},[290,62855,44461],{"class":461},[290,62857,465],{"class":295},[290,62859,1970],{"class":461},[290,62861,471],{"class":295},[290,62863,62864,62866,62868,62870],{"class":163,"line":9683},[290,62865,36064],{"class":461},[290,62867,465],{"class":295},[290,62869,1970],{"class":461},[290,62871,471],{"class":295},[290,62873,62874,62876,62878,62880],{"class":163,"line":9688},[290,62875,62197],{"class":461},[290,62877,465],{"class":295},[290,62879,250],{"class":461},[290,62881,471],{"class":295},[290,62883,62884],{"class":163,"line":9694},[290,62885,771],{"class":295},[290,62887,62888],{"class":163,"line":9700},[290,62889,334],{"emptyLinePlaceholder":333},[290,62891,62892,62894,62896,62898],{"class":163,"line":9710},[290,62893,61615],{"class":303},[290,62895,484],{"class":295},[290,62897,300],{"class":299},[290,62899,646],{"class":295},[290,62901,62902,62904,62906,62908],{"class":163,"line":9716},[290,62903,61922],{"class":461},[290,62905,465],{"class":295},[290,62907,61413],{"class":461},[290,62909,471],{"class":295},[290,62911,62912,62914,62916,62918],{"class":163,"line":9738},[290,62913,61933],{"class":461},[290,62915,465],{"class":295},[290,62917,18849],{"class":461},[290,62919,471],{"class":295},[290,62921,62922],{"class":163,"line":9748},[290,62923,771],{"class":295},[290,62925,62926],{"class":163,"line":9754},[290,62927,334],{"emptyLinePlaceholder":333},[290,62929,62930,62932,62934,62936],{"class":163,"line":9760},[290,62931,61615],{"class":303},[290,62933,484],{"class":295},[290,62935,1495],{"class":303},[290,62937,646],{"class":295},[290,62939,62940,62942,62944,62946],{"class":163,"line":9777},[290,62941,2136],{"class":461},[290,62943,465],{"class":295},[290,62945,61413],{"class":461},[290,62947,471],{"class":295},[290,62949,62950,62952,62954,62956,62958],{"class":163,"line":9787},[290,62951,2161],{"class":461},[290,62953,465],{"class":295},[290,62955,194],{"class":461},[290,62957,674],{"class":541},[290,62959,471],{"class":295},[290,62961,62962],{"class":163,"line":9793},[290,62963,771],{"class":295},[290,62965,62966],{"class":163,"line":9799},[290,62967,620],{"class":295},[14,62969,62970],{},[62,62971,62972],{},"Monorepo & Maintenance Strategy:",[1393,62974,62975,62981,62984],{},[1396,62976,62977,62978,3724],{},"Version reset stylesheets independently using semantic versioning (e.g., ",[18,62979,62980],{},"@design-system\u002Freset@1.2.0",[1396,62982,62983],{},"Use PostCSS or Lightning CSS to strip unsupported features during build if targeting older browsers.",[1396,62985,62986,62987,62990],{},"Automate accessibility audits by pairing reset deployments with ",[18,62988,62989],{},"axe-core"," Lighthouse CI checks to verify focus states and contrast ratios remain intact.",[47,62992],{},[50,62994,62996],{"id":62995},"cross-browser-compatibility-progressive-enhancement","Cross-Browser Compatibility & Progressive Enhancement",[2250,62998,62999,63013],{},[2253,63000,63001],{},[2256,63002,63003,63005,63007,63009,63011],{},[2259,63004,3737],{},[2259,63006,2276],{},[2259,63008,2287],{},[2259,63010,2297],{},[2259,63012,2308],{},[2269,63014,63015,63029,63045,63060],{},[2256,63016,63017,63021,63023,63025,63027],{},[2274,63018,63019],{},[18,63020,12681],{},[2274,63022,13284],{},[2274,63024,13287],{},[2274,63026,2300],{},[2274,63028,13284],{},[2256,63030,63031,63035,63038,63041,63043],{},[2274,63032,63033],{},[18,63034,61694],{},[2274,63036,63037],{},"88+",[2274,63039,63040],{},"78+",[2274,63042,8507],{},[2274,63044,63037],{},[2256,63046,63047,63051,63053,63056,63058],{},[2274,63048,63049],{},[18,63050,61970],{},[2274,63052,8475],{},[2274,63054,63055],{},"67+",[2274,63057,8507],{},[2274,63059,8475],{},[2256,63061,63062,63064,63066,63068,63070],{},[2274,63063,41550],{},[2274,63065,29200],{},[2274,63067,37592],{},[2274,63069,8465],{},[2274,63071,29200],{},[14,63073,63074,13314,63076,63078,63079,63082,63083,63085,63086,63088],{},[62,63075,13313],{},[18,63077,12681],{}," support, rely on stylesheet ordering and cascade specificity. Place ",[18,63080,63081],{},"modern-reset.css"," before any framework or utility CSS. The browser's natural cascade evaluates it first, achieving near-identical results without syntax errors. There is no reliable ",[18,63084,2086],{}," test for ",[18,63087,12681],{},"; rely on source order instead.",[47,63090],{},[50,63092,63094],{"id":63093},"common-pitfalls-devtools-debugging-workflow","Common Pitfalls & DevTools Debugging Workflow",[2250,63096,63097,63105],{},[2253,63098,63099],{},[2256,63100,63101,63103],{},[2259,63102,2338],{},[2259,63104,2341],{},[2269,63106,63107,63124,63138],{},[2256,63108,63109,63112],{},[2274,63110,63111],{},"Global resets stripping native focus outlines",[2274,63113,1499,63114,63116,63117,63120,63121,63123],{},[18,63115,61694],{}," to target focus states explicitly and apply ",[18,63118,63119],{},"outline: revert"," or custom accessible focus rings (",[18,63122,1506],{}," fallbacks for older browsers).",[2256,63125,63126,63129],{},[2274,63127,63128],{},"Specificity conflicts between reset and component styles",[2274,63130,63131,63132,63134,63135,63137],{},"Enforce ",[18,63133,12681],{}," ordering. Keep resets in the lowest layer and use ",[18,63136,12821],{}," for UI overrides. Avoid inline styles.",[2256,63139,63140,63145],{},[2274,63141,63142,63143],{},"Form elements losing default styling after ",[18,63144,61970],{},[2274,63146,63147,63148,63151,63152,63155],{},"Target form inputs selectively with ",[18,63149,63150],{},":where(input, select, textarea)"," and apply ",[18,63153,63154],{},"appearance: auto"," or explicit baseline styles.",[14,63157,63158],{},[62,63159,3015],{},[3017,63161,63162,63168,63178,63185,63193],{},[1396,63163,63164,63165,63167],{},"Open the ",[62,63166,13452],{}," panel in Chrome\u002FFirefox DevTools.",[1396,63169,33126,63170,63173,63174,63177],{},[62,63171,63172],{},"Show all layers"," (Chrome) or inspect the ",[62,63175,63176],{},"Cascade Layers"," tab (Firefox 118+).",[1396,63179,63180,63181,63184],{},"Select a problematic element and verify the ",[18,63182,63183],{},"@layer reset"," rule appears at the bottom of the cascade stack.",[1396,63186,3032,63187,63189,63190,42],{},[18,63188,61970],{}," to observe native browser defaults reappear. If accessibility features (like focus rings) disappear, add ",[18,63191,63192],{},":where(:focus-visible) { outline: revert; }",[1396,63194,59006,63195,54424,63197,63199,63200,63202],{},[62,63196,13407],{},[18,63198,57915],{}," and inherited ",[18,63201,46459],{}," values aren't being overridden by framework utilities.",[47,63204],{},[50,63206,16218],{"id":16217},[14,63208,63209,63212,63213,69,63215,63217],{},[62,63210,63211],{},"Should I use a CSS reset or normalize.css in modern projects?","\nModern projects benefit from a hybrid approach: use a lightweight, spec-compliant reset that leverages ",[18,63214,12681],{},[18,63216,61694],{}," to establish a predictable baseline without stripping accessibility features, rather than relying on legacy normalize.css or aggressive universal resets.",[14,63219,63220,63223,63224,63226,63227,63229],{},[62,63221,63222],{},"How do cascade layers change reset implementation?","\nCascade layers (",[18,63225,12681],{},") allow you to explicitly define the order of stylesheet evaluation. By placing your reset in the first layer, you guarantee it has the lowest specificity, making component overrides predictable and eliminating the need for ",[18,63228,3900],{}," hacks.",[14,63231,63232,63235,63236,69,63238,63240],{},[62,63233,63234],{},"Can I scope a CSS reset to a single component?","\nYes. By combining CSS nesting with ",[18,63237,12681],{},[18,63239,61694],{},", you can apply reset rules exclusively within a component's boundary. This is highly recommended for design systems where global resets might interfere with embedded third-party widgets or micro-interactions.",[14,63242,63243,63248,63250,63251,69,63254,63257],{},[62,63244,5714,63245,63247],{},[18,63246,61970],{}," work safely on interactive elements?",[18,63249,61970],{}," safely restores browser defaults for the targeted element, but it can strip custom theming. Use it selectively on structural elements, and pair it with explicit ",[18,63252,63253],{},"appearance",[18,63255,63256],{},"cursor"," properties to maintain interactive UX consistency.",[47,63259],{},[2757,63261,13480],{"id":13479},[1393,63263,63264,63274,63286,63294],{},[1396,63265,63266],{},[27,63267,63269,63270,569,63272,3914],{"href":13495,"rel":63268},[13489],"CSS Cascading and Inheritance Level 5 (",[18,63271,12681],{},[18,63273,61413],{},[1396,63275,63276],{},[27,63277,63280,63281,569,63283,3914],{"href":63278,"rel":63279},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fselectors-4\u002F",[13489],"CSS Selectors Level 4 (",[18,63282,61694],{},[18,63284,63285],{},":is()",[1396,63287,63288],{},[27,63289,63291,63292,3914],{"href":38514,"rel":63290},[13489],"CSS Containment Module Level 3 (",[18,63293,24401],{},[1396,63295,63296],{},[27,63297,63300,63301,569,63303,3914],{"href":63298,"rel":63299},"https:\u002F\u002Fwww.w3.org\u002FTR\u002Fcss-ui-4\u002F",[13489],"CSS Basic User Interface Module Level 4 (",[18,63302,63253],{},[18,63304,63256],{},[47,63306],{},[50,63308,1391],{"id":1390},[1393,63310,63311,63316,63322,63327,63332],{},[1396,63312,63313,63315],{},[27,63314,11296],{"href":5777}," — the parent guide this baseline feeds into.",[1396,63317,63318,63321],{},[27,63319,63320],{"href":61717},"Cascade layers for reset and tokens"," — structure reset and design tokens into a predictable layer order.",[1396,63323,63324,63326],{},[27,63325,38098],{"href":38097}," — build on a clean baseline with container-aware components.",[1396,63328,63329,63331],{},[27,63330,26284],{"href":25340}," — declare containment contexts on top of the reset.",[1396,63333,63334,63336,63337,63339,63340,42],{},[27,63335,3350],{"href":3349}," — cross-area: keep ",[18,63338,1495],{}," rings intact after ",[18,63341,61970],{},[1430,63343,63344],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":286,"searchDepth":330,"depth":330,"links":63346},[63347,63348,63349,63350,63351,63352,63353,63354,63357],{"id":61474,"depth":330,"text":61475},{"id":61707,"depth":330,"text":61708},{"id":61961,"depth":330,"text":61962},{"id":62272,"depth":330,"text":62273},{"id":62418,"depth":330,"text":62419},{"id":62995,"depth":330,"text":62996},{"id":63093,"depth":330,"text":63094},{"id":16217,"depth":330,"text":16218,"children":63355},[63356],{"id":13479,"depth":337,"text":13480},{"id":1390,"depth":330,"text":1391},"Modern CSS reset strategies: spec-compliant baselines, accessibility-preserving defaults, and reset architectures for component-driven design systems.",{"seoTitle":63360,"datePublished":1447,"dateModified":1447,"faq":63361},"Modern CSS Reset Strategies Explained",[63362,63364,63366,63368],{"q":63211,"a":63363},"Modern projects benefit from a lightweight, spec-compliant reset that uses @layer and :where() to establish a predictable baseline without stripping accessibility features, rather than legacy normalize.css or aggressive universal resets.",{"q":63222,"a":63365},"Cascade layers let you explicitly define the order of stylesheet evaluation. Placing your reset in the first layer guarantees the lowest priority, making component overrides predictable and eliminating the need for !important hacks.",{"q":63234,"a":63367},"Yes. Combining CSS nesting with @layer and :where() applies reset rules only within a component's boundary, which is recommended for design systems where global resets might interfere with embedded widgets.",{"q":63369,"a":63370},"Does all: revert work safely on interactive elements?","all: revert restores browser defaults for the targeted element but can strip custom theming. Use it selectively on structural elements and pair it with explicit appearance and cursor properties to maintain interactive UX.","\u002Fmastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies",{"title":61387,"description":63358},"mastering-container-queries-responsive-layouts\u002Fmodern-css-reset-strategies\u002Findex","4DHkphMVYNO7ksdtprUVXFuai0CnmzHUBCUZeSOImjU",{"id":63376,"title":63377,"body":63378,"description":64086,"extension":1444,"meta":64087,"navigation":333,"path":64096,"seo":64097,"stem":64098,"__hash__":64099},"content\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fbuilding-responsive-cards-with-container-queries\u002Findex.md","Building Responsive Cards with Container Queries: A Production-Ready Guide",{"type":7,"value":63379,"toc":64076},[63380,63383,63396,63401,63419,63470,63472,63476,63489,63516,63524,63526,63533,63549,63668,63674,63676,63680,63693,63699,63860,63863,63865,63867,63903,63913,63915,63917,64008,64010,64012,64024,64029,64047,64049,64073],[10,63381,63377],{"id":63382},"building-responsive-cards-with-container-queries-a-production-ready-guide",[14,63384,63385,63386,63389,63390,63392,63393,63395],{},"This guide provides a production-ready blueprint for ",[27,63387,63388],{"href":38097},"building responsive cards with container queries",", moving beyond viewport-dependent breakpoints. By leveraging CSS containment and ",[18,63391,12001],{}," rules, frontend engineers can create truly modular UI components that adapt to their parent context. This pattern eliminates layout thrashing, improves render performance, and aligns with modern ",[27,63394,11296],{"href":5777}," methodologies.",[14,63397,63398],{},[62,63399,63400],{},"Key Implementation Goals:",[1393,63402,63403,63406,63413,63416],{},[1396,63404,63405],{},"Decouple component styling from viewport dimensions",[1396,63407,21710,63408,69,63410,63412],{},[18,63409,24401],{},[18,63411,26072],{}," correctly",[1396,63414,63415],{},"Handle legacy browser fallbacks gracefully",[1396,63417,63418],{},"Optimize for paint and layout performance",[133,63420,140,63422,140,63425,140,63428,140,63430,140,63433,140,63435,140,63437,140,63440,140,63442,140,63444,140,63446,140,63449,140,63451,140,63454,140,63457,140,63459,140,63461,140,63463,140,63465],{"viewBox":4133,"role":136,"ariaLabel":63421,"xmlns":138,"style":139},"A card stacked below 400px and laid out side by side at or above 400px container width",[142,63423,63424],{},"Card layout switch at the container threshold",[146,63426,63427],{},"Below 400px the media sits above the text; at or above 400px the media moves beside the text.",[150,63429,38239],{"x":152,"y":153,"style":1781},[150,63431,63432],{"x":1822,"y":4172,"style":2606},"narrow: stacked",[171,63434],{"x":5144,"y":5892,"width":174,"height":2602,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":14604},[171,63436],{"x":5115,"y":5115,"width":1830,"height":5144,"rx":5901,"fill":177,"opacity":11349},[150,63438,63439],{"x":1822,"y":182,"style":10262},"media",[171,63441],{"x":5115,"y":6673,"width":1830,"height":43253,"rx":1579,"fill":167,"opacity":199},[171,63443],{"x":5115,"y":6721,"width":1830,"height":43253,"rx":1579,"fill":167,"opacity":199},[171,63445],{"x":5115,"y":538,"width":5159,"height":43253,"rx":1579,"fill":167,"opacity":199},[150,63447,63448],{"x":5887,"y":4172,"style":2606},"wide: side by side",[171,63450],{"x":2618,"y":5892,"width":11114,"height":2602,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":14604},[171,63452],{"x":63453,"y":4196,"width":165,"height":4195,"rx":5901,"fill":177,"opacity":11349},"418",[150,63455,63439],{"x":63456,"y":4154,"style":10262},"468",[171,63458],{"x":10272,"y":165,"width":5163,"height":43253,"rx":1579,"fill":167,"opacity":199},[171,63460],{"x":10272,"y":181,"width":5163,"height":43253,"rx":1579,"fill":167,"opacity":199},[171,63462],{"x":10272,"y":17543,"width":5163,"height":43253,"rx":1579,"fill":167,"opacity":199},[171,63464],{"x":10272,"y":6673,"width":5115,"height":43253,"rx":1579,"fill":167,"opacity":199},[150,63466,63469],{"x":63467,"y":4154,"style":63468},"335","text-anchor:middle;fill:currentColor;font:18px sans-serif;opacity:0.6","→",[47,63471],{},[50,63473,63475],{"id":63474},"establishing-container-context","Establishing Container Context",[14,63477,63478,63479,18300,63481,50917,63483,63485,63486,63488],{},"To isolate a card's responsiveness from the viewport, establish a query context on its direct parent wrapper. Use ",[18,63480,25409],{},[18,63482,41019],{},[18,63484,26044],{}," value restricts containment to the horizontal axis, preventing unnecessary vertical reflows when dynamic content shifts height. Always assign a ",[18,63487,26072],{}," to scope queries and prevent cascade collisions in nested layouts.",[281,63490,63492],{"className":438,"code":63491,"language":440,"meta":286,"style":286},".card-wrapper {\n  container-type: inline-size;\n  container-name: card;\n}\n",[18,63493,63494,63500,63506,63512],{"__ignoreMap":286},[290,63495,63496,63498],{"class":163,"line":292},[290,63497,40135],{"class":303},[290,63499,450],{"class":295},[290,63501,63502,63504],{"class":163,"line":330},[290,63503,11509],{"class":461},[290,63505,25565],{"class":295},[290,63507,63508,63510],{"class":163,"line":337},[290,63509,25575],{"class":461},[290,63511,37983],{"class":295},[290,63513,63514],{"class":163,"line":364},[290,63515,620],{"class":295},[14,63517,63518,63520,63521,63523],{},[62,63519,5646],{}," The wrapper must have a resolved inline size (via flex, grid, ",[18,63522,41191],{},", or explicit width) to trigger the containment context. Unconstrained parents will not evaluate queries.",[47,63525],{},[50,63527,63529,63530,63532],{"id":63528},"writing-the-container-query","Writing the ",[18,63531,12001],{}," Query",[14,63534,63535,63536,63538,63539,63543,63544,63548],{},"Query thresholds should target the component's natural content breakpoints, not arbitrary viewport sizes. Combine ",[18,63537,12001],{}," with CSS Grid to shift from a stacked layout to a side-by-side layout. The same threshold-driven approach powers ",[27,63540,63542],{"href":63541},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-data-tables\u002F","container query data tables",", which collapse columns to stacked rows in narrow contexts, and ",[27,63545,63547],{"href":63546},"\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-navigation-without-media-queries\u002F","responsive navigation without media queries",", which folds links into a menu based on available bar width. You can also scope CSS custom properties directly to the container context for dynamic theming without global overrides.",[281,63550,63552],{"className":438,"code":63551,"language":440,"meta":286,"style":286},"@container card (min-width: 400px) {\n  .card {\n    display: grid;\n    grid-template-columns: 120px 1fr;\n    gap: 1rem;\n    align-items: center;\n  }\n\n  .card__media {\n    aspect-ratio: 1;\n    width: 100%;\n    height: auto;\n  }\n}\n",[18,63553,63554,63560,63566,63576,63592,63604,63614,63618,63622,63628,63638,63650,63660,63664],{"__ignoreMap":286},[290,63555,63556,63558],{"class":163,"line":292},[290,63557,12001],{"class":541},[290,63559,37998],{"class":295},[290,63561,63562,63564],{"class":163,"line":330},[290,63563,9083],{"class":303},[290,63565,450],{"class":295},[290,63567,63568,63570,63572,63574],{"class":163,"line":337},[290,63569,34590],{"class":461},[290,63571,465],{"class":295},[290,63573,9147],{"class":461},[290,63575,471],{"class":295},[290,63577,63578,63580,63582,63584,63586,63588,63590],{"class":163,"line":364},[290,63579,36011],{"class":461},[290,63581,465],{"class":295},[290,63583,4147],{"class":461},[290,63585,674],{"class":541},[290,63587,804],{"class":461},[290,63589,11964],{"class":541},[290,63591,471],{"class":295},[290,63593,63594,63596,63598,63600,63602],{"class":163,"line":386},[290,63595,36027],{"class":461},[290,63597,465],{"class":295},[290,63599,468],{"class":461},[290,63601,801],{"class":541},[290,63603,471],{"class":295},[290,63605,63606,63608,63610,63612],{"class":163,"line":408},[290,63607,42363],{"class":461},[290,63609,465],{"class":295},[290,63611,9157],{"class":461},[290,63613,471],{"class":295},[290,63615,63616],{"class":163,"line":428},[290,63617,771],{"class":295},[290,63619,63620],{"class":163,"line":517},[290,63621,334],{"emptyLinePlaceholder":333},[290,63623,63624,63626],{"class":163,"line":523},[290,63625,43567],{"class":303},[290,63627,450],{"class":295},[290,63629,63630,63632,63634,63636],{"class":163,"line":532},[290,63631,36346],{"class":461},[290,63633,465],{"class":295},[290,63635,468],{"class":461},[290,63637,471],{"class":295},[290,63639,63640,63642,63644,63646,63648],{"class":163,"line":551},[290,63641,9090],{"class":461},[290,63643,465],{"class":295},[290,63645,165],{"class":461},[290,63647,11018],{"class":541},[290,63649,471],{"class":295},[290,63651,63652,63654,63656,63658],{"class":163,"line":586},[290,63653,39388],{"class":461},[290,63655,465],{"class":295},[290,63657,250],{"class":461},[290,63659,471],{"class":295},[290,63661,63662],{"class":163,"line":602},[290,63663,771],{"class":295},[290,63665,63666],{"class":163,"line":617},[290,63667,620],{"class":295},[14,63669,63670,63671,63673],{},"Base styles should define the default stacked layout. The ",[18,63672,12001],{}," rule acts as a progressive enhancement, overriding only when the parent's inline size crosses the threshold.",[47,63675],{},[50,63677,63679],{"id":63678},"performance-containment-optimization","Performance & Containment Optimization",[14,63681,63682,63683,63685,63686,569,63688,1745,63690,63692],{},"Wrapping the card in ",[18,63684,34746],{}," creates a strict paint and layout boundary. The browser skips layout calculations outside the container when internal dimensions change, drastically reducing main-thread jank. Avoid querying on frequently animating properties (",[18,63687,103],{},[18,63689,76],{},[18,63691,2722],{},"), as this forces continuous re-evaluation.",[14,63694,63695,63696,63698],{},"Always wrap modern syntax in ",[18,63697,2086],{}," for progressive enhancement. Pair it with a viewport-based fallback for legacy environments.",[281,63700,63702],{"className":438,"code":63701,"language":440,"meta":286,"style":286},"@supports (container-type: inline-size) {\n  .card-wrapper {\n    container-type: inline-size;\n  }\n\n  @container (min-width: 400px) {\n    .card {\n      display: grid;\n      grid-template-columns: 120px 1fr;\n    }\n  }\n}\n\n@supports not (container-type: inline-size) {\n  @media (min-width: 768px) {\n    .card {\n      display: grid;\n      grid-template-columns: 120px 1fr;\n    }\n  }\n}\n",[18,63703,63704,63714,63720,63726,63730,63734,63740,63746,63756,63772,63776,63780,63784,63788,63800,63816,63822,63832,63848,63852,63856],{"__ignoreMap":286},[290,63705,63706,63708,63710,63712],{"class":163,"line":292},[290,63707,2086],{"class":541},[290,63709,3595],{"class":295},[290,63711,24401],{"class":461},[290,63713,26104],{"class":295},[290,63715,63716,63718],{"class":163,"line":330},[290,63717,40357],{"class":303},[290,63719,450],{"class":295},[290,63721,63722,63724],{"class":163,"line":337},[290,63723,37025],{"class":461},[290,63725,25565],{"class":295},[290,63727,63728],{"class":163,"line":364},[290,63729,771],{"class":295},[290,63731,63732],{"class":163,"line":386},[290,63733,334],{"emptyLinePlaceholder":333},[290,63735,63736,63738],{"class":163,"line":408},[290,63737,37040],{"class":541},[290,63739,12004],{"class":295},[290,63741,63742,63744],{"class":163,"line":428},[290,63743,26128],{"class":303},[290,63745,450],{"class":295},[290,63747,63748,63750,63752,63754],{"class":163,"line":517},[290,63749,37053],{"class":461},[290,63751,465],{"class":295},[290,63753,9147],{"class":461},[290,63755,471],{"class":295},[290,63757,63758,63760,63762,63764,63766,63768,63770],{"class":163,"line":523},[290,63759,36130],{"class":461},[290,63761,465],{"class":295},[290,63763,4147],{"class":461},[290,63765,674],{"class":541},[290,63767,804],{"class":461},[290,63769,11964],{"class":541},[290,63771,471],{"class":295},[290,63773,63774],{"class":163,"line":532},[290,63775,8200],{"class":295},[290,63777,63778],{"class":163,"line":551},[290,63779,771],{"class":295},[290,63781,63782],{"class":163,"line":586},[290,63783,620],{"class":295},[290,63785,63786],{"class":163,"line":602},[290,63787,334],{"emptyLinePlaceholder":333},[290,63789,63790,63792,63794,63796,63798],{"class":163,"line":617},[290,63791,2086],{"class":541},[290,63793,2116],{"class":541},[290,63795,3595],{"class":295},[290,63797,24401],{"class":461},[290,63799,26104],{"class":295},[290,63801,63802,63804,63806,63808,63810,63812,63814],{"class":163,"line":623},[290,63803,26109],{"class":541},[290,63805,3595],{"class":295},[290,63807,26114],{"class":461},[290,63809,465],{"class":295},[290,63811,38134],{"class":461},[290,63813,674],{"class":541},[290,63815,646],{"class":295},[290,63817,63818,63820],{"class":163,"line":628},[290,63819,26128],{"class":303},[290,63821,450],{"class":295},[290,63823,63824,63826,63828,63830],{"class":163,"line":634},[290,63825,37053],{"class":461},[290,63827,465],{"class":295},[290,63829,9147],{"class":461},[290,63831,471],{"class":295},[290,63833,63834,63836,63838,63840,63842,63844,63846],{"class":163,"line":649},[290,63835,36130],{"class":461},[290,63837,465],{"class":295},[290,63839,4147],{"class":461},[290,63841,674],{"class":541},[290,63843,804],{"class":461},[290,63845,11964],{"class":541},[290,63847,471],{"class":295},[290,63849,63850],{"class":163,"line":660},[290,63851,8200],{"class":295},[290,63853,63854],{"class":163,"line":688},[290,63855,771],{"class":295},[290,63857,63858],{"class":163,"line":693},[290,63859,620],{"class":295},[14,63861,63862],{},"Fallback media queries should approximate the container's expected maximum width to maintain visual parity across older browsers.",[47,63864],{},[50,63866,41669],{"id":41668},[2250,63868,63869,63877],{},[2253,63870,63871],{},[2256,63872,63873,63875],{},[2259,63874,34634],{},[2259,63876,2264],{},[2269,63878,63879,63885,63891,63897],{},[2256,63880,63881,63883],{},[2274,63882,2276],{},[2274,63884,29200],{},[2256,63886,63887,63889],{},[2274,63888,2297],{},[2274,63890,8465],{},[2256,63892,63893,63895],{},[2274,63894,2287],{},[2274,63896,37592],{},[2256,63898,63899,63901],{},[2274,63900,2308],{},[2274,63902,29200],{},[14,63904,63905,63907,63908,56550,63910,63912],{},[62,63906,13313],{}," Wrap container queries in ",[18,63909,26207],{},[18,63911,874],{}," fallback for older browsers, ensuring graceful degradation without layout breakage. Test fallback breakpoints against your most common grid column widths.",[47,63914],{},[50,63916,40706],{"id":40705},[2250,63918,63919,63930],{},[2253,63920,63921],{},[2256,63922,63923,63925,63927],{},[2259,63924,2338],{},[2259,63926,3876],{},[2259,63928,63929],{},"Direct Fix",[2269,63931,63932,63954,63977],{},[2256,63933,63934,63939,63947],{},[2274,63935,63936],{},[62,63937,63938],{},"Query not triggering on resize",[2274,63940,3930,63941,63943,63944,63946],{},[18,63942,24401],{}," on direct parent, or parent lacks a constrained inline size (e.g., ",[18,63945,57468],{}," in an unconstrained flex context).",[2274,63948,29271,63949,63951,63952,42],{},[18,63950,25409],{}," to the immediate parent. Ensure it participates in a grid\u002Fflex layout or has an explicit ",[18,63953,41191],{},[2256,63955,63956,63961,63967],{},[2274,63957,63958],{},[62,63959,63960],{},"Unwanted layout shift during dynamic load",[2274,63962,63963,63964,63966],{},"Missing height constraints or ",[18,63965,40216],{}," on the card, causing reflow as images\u002Ftext load.",[2274,63968,25028,63969,63971,63972,63151,63974,63976],{},[18,63970,34746],{}," to the wrapper. Reserve space with ",[18,63973,37680],{},[18,63975,38572],{}," to media elements.",[2256,63978,63979,63984,63992],{},[2274,63980,63981],{},[62,63982,63983],{},"Fallback styles overriding queries",[2274,63985,63986,63987,69,63989,63991],{},"CSS cascade specificity or incorrect ordering of ",[18,63988,874],{},[18,63990,12001],{}," blocks.",[2274,63993,63994,63995,63997,63998,64000,64001,64003,64004,64007],{},"Isolate modern queries using ",[18,63996,2086],{},". Place ",[18,63999,12001],{}," rules after base styles. Ensure fallback ",[18,64002,874],{}," queries use lower specificity or are scoped to a ",[18,64005,64006],{},".no-container-queries"," class.",[47,64009],{},[50,64011,1316],{"id":1315},[14,64013,64014,64017,64018,64020,64021,64023],{},[62,64015,64016],{},"Can I nest container queries for complex card grids?","\nYes. Each container establishes an independent query context. Assign unique ",[18,64019,26072],{}," values to parent and child wrappers. Nested ",[18,64022,12001],{}," rules will evaluate against their nearest ancestor with a matching name, enabling deeply nested components to respond to specific layout boundaries without interference.",[14,64025,64026,64028],{},[62,64027,40806],{},"\nNo. Media queries remain essential for global layout shifts, page-level typography scaling, and viewport-specific features (orientation, dark mode, reduced motion). Container queries are strictly for component-level responsiveness within their parent context.",[14,64030,64031,5735,64034,64036,64037,64040,64041,2351,64044,64046],{},[62,64032,64033],{},"How do I handle dynamic content height in responsive cards?",[18,64035,37680],{}," combined with ",[18,64038,64039],{},"flex: 1"," on the content area to allow natural expansion. Avoid fixed heights. Pair with ",[18,64042,64043],{},"contain: content",[18,64045,34746],{}," to prevent the container's inline size from being recalculated during height changes, ensuring stable paint boundaries.",[50,64048,1391],{"id":1390},[1393,64050,64051,64057,64063,64068],{},[1396,64052,64053,64056],{},[27,64054,64055],{"href":63541},"Container Query Data Tables"," — collapse columns the same way cards reflow.",[1396,64058,64059,64062],{},[27,64060,64061],{"href":63546},"Responsive Navigation Without Media Queries"," — fold a nav bar based on its own width.",[1396,64064,64065,64067],{},[27,64066,38098],{"href":38097}," — the parent guide for component-level responsiveness.",[1396,64069,64070,64072],{},[27,64071,18535],{"href":18534}," — add tactile feedback to card interactions.",[1430,64074,64075],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":286,"searchDepth":330,"depth":330,"links":64077},[64078,64079,64081,64082,64083,64084,64085],{"id":63474,"depth":330,"text":63475},{"id":63528,"depth":330,"text":64080},"Writing the @container Query",{"id":63678,"depth":330,"text":63679},{"id":41668,"depth":330,"text":41669},{"id":40705,"depth":330,"text":40706},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Build responsive card components with CSS container queries: production-ready patterns for modular, context-aware cards that adapt to their container.",{"seoTitle":64088,"datePublished":1447,"dateModified":1447,"faq":64089},"Responsive Cards with Container Queries",[64090,64092,64094],{"q":64016,"a":64091},"Yes. Each container establishes an independent query context. Assign unique container-name values to parent and child wrappers so nested @container rules evaluate against their nearest matching ancestor without interference.",{"q":40806,"a":64093},"No. Media queries remain essential for global layout shifts, page-level typography scaling, and viewport-specific features like orientation and dark mode. Container queries are for component-level responsiveness within a parent context.",{"q":64033,"a":64095},"Use min-height combined with flex: 1 on the content area to allow natural expansion and avoid fixed heights. Pair with contain: layout style to keep the container's inline size stable during height changes.","\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fbuilding-responsive-cards-with-container-queries",{"title":63377,"description":64086},"mastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fbuilding-responsive-cards-with-container-queries\u002Findex","l1Mv2ZpZxRO5o0j1_oCaXfDj1FOaTfmvloIvHXQC6Ws",{"id":64101,"title":64102,"body":64103,"description":65797,"extension":1444,"meta":65798,"navigation":333,"path":65809,"seo":65810,"stem":65811,"__hash__":65812},"content\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-data-tables\u002Findex.md","Container Query Data Tables: Reflowing Tables to Cards",{"type":7,"value":64104,"toc":65787},[64105,64108,64120,64122,64126,64135,64177,64179,64181,64188,65488,65494,65562,65564,65573,65586,65588,65592,65599,65671,65684,65686,65688,65705,65707,65709,65715,65731,65747,65753,65755,65757,65784],[10,64106,64102],{"id":64107},"container-query-data-tables-reflowing-tables-to-cards",[14,64109,64110,64111,60326,64114,64116,64117,64119],{},"A wide data table is hostile to narrow space. Inside a full-width page it scans fine, but drop the same table into a 360px card or a dashboard sidebar and it either overflows or shrinks columns into unreadable slivers. This guide, part of the ",[27,64112,64113],{"href":38097},"responsive component patterns",[27,64115,11296],{"href":5777},", shows how to reflow a table into a stacked card layout based on the table's own container width using ",[18,64118,12001],{},", while being honest about the accessibility cost of doing so.",[47,64121],{},[50,64123,64125],{"id":64124},"approach-rationale-container-width-and-the-semantics-tradeoff","Approach rationale: container width and the semantics tradeoff",[14,64127,64128,64129,64131,64132,64134],{},"The layout problem is a perfect fit for container queries. A table cares about how much horizontal room it has, not how wide the screen is, so wrapping it in a ",[18,64130,25409],{}," ancestor lets one ",[18,64133,12001],{}," rule serve the table wherever it lands. That part is uncontroversial.",[14,64136,64137,64138,64141,64142,569,64144,569,64146,569,64149,569,64152,64155,64156,2351,64158,33827,64160,2351,64163,64166,64167,569,64170,569,64173,64176],{},"The accessibility problem is harder and is the reason this pattern needs care. A native ",[18,64139,64140],{},"\u003Ctable>"," carries implicit ARIA roles — ",[18,64143,2250],{},[18,64145,38025],{},[18,64147,64148],{},"rowgroup",[18,64150,64151],{},"columnheader",[18,64153,64154],{},"cell"," — that screen readers use to announce \"row 3, column Price, $40\". The moment you set ",[18,64157,83],{},[18,64159,47555],{},[18,64161,64162],{},"\u003Ctr>",[18,64164,64165],{},"\u003Ctd>"," to stack them, browsers drop those implicit roles, and the table stops being a table to assistive technology. You have two defensible choices: reattach the roles explicitly with ",[18,64168,64169],{},"role=\"table\"",[18,64171,64172],{},"role=\"row\"",[18,64174,64175],{},"role=\"cell\"",", or skip reflow entirely and keep a real, horizontally scrollable table. The implementation below uses the explicit-role approach and labels each cell with a visible header so sighted card readers also keep their bearings.",[47,64178],{},[50,64180,4203],{"id":4202},[14,64182,64183,64184,64187],{},"Paste this into an HTML file and narrow the wrapper to watch each row become a card. The ",[18,64185,64186],{},"data-label"," attributes drive the per-cell header that appears only in card mode.",[281,64189,64191],{"className":283,"code":64190,"language":285,"meta":286,"style":286},"\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cstyle>\n  \u002F* The wrapper is the query container; the table queries THIS width. *\u002F\n  .table-shell {\n    container-type: inline-size;\n    container-name: datatable;\n    max-width: 100%;\n  }\n\n  table { width: 100%; border-collapse: collapse; }\n  caption { text-align: left; font-weight: 700; padding: 0.5rem 0; }\n  th, td { text-align: left; padding: 0.6rem 0.75rem; border-bottom: 1px solid #ddd; }\n  thead th { font-weight: 700; }\n\n  \u002F* The per-cell label is hidden in wide mode; the column header already shows. *\u002F\n  td::before { content: \"\"; }\n\n  \u002F* NARROW: when the container is small, stack each row as a card.\n     We set display on table parts, which strips implicit ARIA roles,\n     so the markup re-declares roles explicitly (see the HTML below). *\u002F\n  @container datatable (max-width: 520px) {\n    thead {\n      \u002F* Visually hide the header row but keep it in the accessibility tree. *\u002F\n      position: absolute;\n      width: 1px; height: 1px;\n      overflow: hidden; clip-path: inset(50%);\n    }\n\n    table, tbody, tr, td { display: block; width: 100%; }\n\n    tr {\n      border: 1px solid #ccc;\n      border-radius: 8px;\n      padding: 0.5rem 0.75rem;\n      margin-bottom: 0.75rem;\n    }\n\n    td {\n      display: grid;\n      grid-template-columns: 40% 1fr;\n      gap: 0.5rem;\n      border-bottom: 1px solid #eee;\n      padding: 0.4rem 0;\n    }\n    td:last-child { border-bottom: 0; }\n\n    \u002F* Show the mirrored header next to each value. attr() reads the\n       data-label, so no duplicated visible markup is needed. *\u002F\n    td::before {\n      content: attr(data-label);\n      font-weight: 600;\n      color: #444;\n    }\n  }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv class=\"table-shell\">\n    \u003C!-- Roles are declared explicitly so the card display does not erase\n         table semantics for screen readers. -->\n    \u003Ctable role=\"table\">\n      \u003Ccaption>Recent orders\u003C\u002Fcaption>\n      \u003Cthead role=\"rowgroup\">\n        \u003Ctr role=\"row\">\n          \u003Cth role=\"columnheader\" scope=\"col\">Order\u003C\u002Fth>\n          \u003Cth role=\"columnheader\" scope=\"col\">Customer\u003C\u002Fth>\n          \u003Cth role=\"columnheader\" scope=\"col\">Status\u003C\u002Fth>\n          \u003Cth role=\"columnheader\" scope=\"col\">Total\u003C\u002Fth>\n        \u003C\u002Ftr>\n      \u003C\u002Fthead>\n      \u003Ctbody role=\"rowgroup\">\n        \u003Ctr role=\"row\">\n          \u003Ctd role=\"cell\" data-label=\"Order\">#1042\u003C\u002Ftd>\n          \u003Ctd role=\"cell\" data-label=\"Customer\">A. Okafor\u003C\u002Ftd>\n          \u003Ctd role=\"cell\" data-label=\"Status\">Shipped\u003C\u002Ftd>\n          \u003Ctd role=\"cell\" data-label=\"Total\">$58.00\u003C\u002Ftd>\n        \u003C\u002Ftr>\n        \u003Ctr role=\"row\">\n          \u003Ctd role=\"cell\" data-label=\"Order\">#1043\u003C\u002Ftd>\n          \u003Ctd role=\"cell\" data-label=\"Customer\">M. Singh\u003C\u002Ftd>\n          \u003Ctd role=\"cell\" data-label=\"Status\">Pending\u003C\u002Ftd>\n          \u003Ctd role=\"cell\" data-label=\"Total\">$129.50\u003C\u002Ftd>\n        \u003C\u002Ftr>\n        \u003Ctr role=\"row\">\n          \u003Ctd role=\"cell\" data-label=\"Order\">#1044\u003C\u002Ftd>\n          \u003Ctd role=\"cell\" data-label=\"Customer\">L. Romero\u003C\u002Ftd>\n          \u003Ctd role=\"cell\" data-label=\"Status\">Refunded\u003C\u002Ftd>\n          \u003Ctd role=\"cell\" data-label=\"Total\">$12.00\u003C\u002Ftd>\n        \u003C\u002Ftr>\n      \u003C\u002Ftbody>\n    \u003C\u002Ftable>\n  \u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,64192,64193,64203,64217,64225,64239,64247,64252,64259,64265,64272,64284,64288,64292,64319,64355,64404,64422,64426,64431,64448,64452,64457,64462,64467,64474,64481,64486,64496,64518,64545,64549,64553,64590,64594,64601,64618,64631,64647,64660,64664,64668,64675,64685,64701,64713,64731,64745,64749,64766,64770,64775,64780,64788,64804,64815,64827,64831,64835,64843,64851,64859,64874,64879,64884,64899,64913,64928,64943,64971,64996,65021,65046,65054,65062,65076,65090,65118,65144,65170,65196,65204,65218,65243,65268,65293,65318,65326,65340,65365,65390,65415,65440,65448,65456,65464,65472,65480],{"__ignoreMap":286},[290,64194,64195,64197,64199,64201],{"class":163,"line":292},[290,64196,8982],{"class":295},[290,64198,35913],{"class":299},[290,64200,8988],{"class":303},[290,64202,327],{"class":295},[290,64204,64205,64207,64209,64211,64213,64215],{"class":163,"line":330},[290,64206,296],{"class":295},[290,64208,285],{"class":299},[290,64210,8999],{"class":303},[290,64212,307],{"class":295},[290,64214,9004],{"class":310},[290,64216,327],{"class":295},[290,64218,64219,64221,64223],{"class":163,"line":337},[290,64220,296],{"class":295},[290,64222,9013],{"class":299},[290,64224,327],{"class":295},[290,64226,64227,64229,64231,64233,64235,64237],{"class":163,"line":364},[290,64228,296],{"class":295},[290,64230,9022],{"class":299},[290,64232,9025],{"class":303},[290,64234,307],{"class":295},[290,64236,9030],{"class":310},[290,64238,327],{"class":295},[290,64240,64241,64243,64245],{"class":163,"line":386},[290,64242,296],{"class":295},[290,64244,1430],{"class":299},[290,64246,327],{"class":295},[290,64248,64249],{"class":163,"line":408},[290,64250,64251],{"class":455},"  \u002F* The wrapper is the query container; the table queries THIS width. *\u002F\n",[290,64253,64254,64257],{"class":163,"line":428},[290,64255,64256],{"class":303},"  .table-shell",[290,64258,450],{"class":295},[290,64260,64261,64263],{"class":163,"line":517},[290,64262,37025],{"class":461},[290,64264,25565],{"class":295},[290,64266,64267,64269],{"class":163,"line":523},[290,64268,37980],{"class":461},[290,64270,64271],{"class":295},": datatable;\n",[290,64273,64274,64276,64278,64280,64282],{"class":163,"line":532},[290,64275,44622],{"class":461},[290,64277,465],{"class":295},[290,64279,165],{"class":461},[290,64281,11018],{"class":541},[290,64283,471],{"class":295},[290,64285,64286],{"class":163,"line":551},[290,64287,771],{"class":295},[290,64289,64290],{"class":163,"line":586},[290,64291,334],{"emptyLinePlaceholder":333},[290,64293,64294,64297,64299,64301,64303,64305,64307,64309,64312,64314,64317],{"class":163,"line":602},[290,64295,64296],{"class":299},"  table",[290,64298,790],{"class":295},[290,64300,1748],{"class":461},[290,64302,465],{"class":295},[290,64304,165],{"class":461},[290,64306,11018],{"class":541},[290,64308,828],{"class":295},[290,64310,64311],{"class":461},"border-collapse",[290,64313,465],{"class":295},[290,64315,64316],{"class":461},"collapse",[290,64318,809],{"class":295},[290,64320,64321,64324,64326,64329,64331,64333,64335,64337,64339,64341,64343,64345,64347,64349,64351,64353],{"class":163,"line":617},[290,64322,64323],{"class":299},"  caption",[290,64325,790],{"class":295},[290,64327,64328],{"class":461},"text-align",[290,64330,465],{"class":295},[290,64332,2731],{"class":461},[290,64334,828],{"class":295},[290,64336,49146],{"class":461},[290,64338,465],{"class":295},[290,64340,26119],{"class":461},[290,64342,828],{"class":295},[290,64344,793],{"class":461},[290,64346,465],{"class":295},[290,64348,798],{"class":461},[290,64350,801],{"class":541},[290,64352,1198],{"class":461},[290,64354,809],{"class":295},[290,64356,64357,64360,64362,64364,64366,64368,64370,64372,64374,64376,64378,64380,64382,64384,64386,64388,64391,64393,64395,64397,64399,64402],{"class":163,"line":623},[290,64358,64359],{"class":299},"  th",[290,64361,569],{"class":295},[290,64363,2274],{"class":299},[290,64365,790],{"class":295},[290,64367,64328],{"class":461},[290,64369,465],{"class":295},[290,64371,2731],{"class":461},[290,64373,828],{"class":295},[290,64375,793],{"class":461},[290,64377,465],{"class":295},[290,64379,232],{"class":461},[290,64381,801],{"class":541},[290,64383,50200],{"class":461},[290,64385,801],{"class":541},[290,64387,828],{"class":295},[290,64389,64390],{"class":461},"border-bottom",[290,64392,465],{"class":295},[290,64394,468],{"class":461},[290,64396,674],{"class":541},[290,64398,852],{"class":461},[290,64400,64401],{"class":461}," #ddd",[290,64403,809],{"class":295},[290,64405,64406,64409,64412,64414,64416,64418,64420],{"class":163,"line":628},[290,64407,64408],{"class":299},"  thead",[290,64410,64411],{"class":299}," th",[290,64413,790],{"class":295},[290,64415,49146],{"class":461},[290,64417,465],{"class":295},[290,64419,26119],{"class":461},[290,64421,809],{"class":295},[290,64423,64424],{"class":163,"line":634},[290,64425,334],{"emptyLinePlaceholder":333},[290,64427,64428],{"class":163,"line":649},[290,64429,64430],{"class":455},"  \u002F* The per-cell label is hidden in wide mode; the column header already shows. *\u002F\n",[290,64432,64433,64436,64438,64440,64442,64444,64446],{"class":163,"line":660},[290,64434,64435],{"class":299},"  td",[290,64437,2811],{"class":303},[290,64439,790],{"class":295},[290,64441,11069],{"class":461},[290,64443,465],{"class":295},[290,64445,1916],{"class":310},[290,64447,809],{"class":295},[290,64449,64450],{"class":163,"line":688},[290,64451,334],{"emptyLinePlaceholder":333},[290,64453,64454],{"class":163,"line":693},[290,64455,64456],{"class":455},"  \u002F* NARROW: when the container is small, stack each row as a card.\n",[290,64458,64459],{"class":163,"line":698},[290,64460,64461],{"class":455},"     We set display on table parts, which strips implicit ARIA roles,\n",[290,64463,64464],{"class":163,"line":704},[290,64465,64466],{"class":455},"     so the markup re-declares roles explicitly (see the HTML below). *\u002F\n",[290,64468,64469,64471],{"class":163,"line":710},[290,64470,37040],{"class":541},[290,64472,64473],{"class":295}," datatable (max-width: 520px) {\n",[290,64475,64476,64479],{"class":163,"line":717},[290,64477,64478],{"class":299},"    thead",[290,64480,450],{"class":295},[290,64482,64483],{"class":163,"line":730},[290,64484,64485],{"class":455},"      \u002F* Visually hide the header row but keep it in the accessibility tree. *\u002F\n",[290,64487,64488,64490,64492,64494],{"class":163,"line":742},[290,64489,57017],{"class":461},[290,64491,465],{"class":295},[290,64493,1927],{"class":461},[290,64495,471],{"class":295},[290,64497,64498,64500,64502,64504,64506,64508,64510,64512,64514,64516],{"class":163,"line":768},[290,64499,57028],{"class":461},[290,64501,465],{"class":295},[290,64503,468],{"class":461},[290,64505,674],{"class":541},[290,64507,828],{"class":295},[290,64509,2722],{"class":461},[290,64511,465],{"class":295},[290,64513,468],{"class":461},[290,64515,674],{"class":541},[290,64517,471],{"class":295},[290,64519,64520,64523,64525,64527,64529,64532,64534,64537,64539,64541,64543],{"class":163,"line":774},[290,64521,64522],{"class":461},"      overflow",[290,64524,465],{"class":295},[290,64526,7808],{"class":461},[290,64528,828],{"class":295},[290,64530,64531],{"class":461},"clip-path",[290,64533,465],{"class":295},[290,64535,64536],{"class":461},"inset",[290,64538,484],{"class":295},[290,64540,1831],{"class":461},[290,64542,11018],{"class":541},[290,64544,500],{"class":295},[290,64546,64547],{"class":163,"line":779},[290,64548,8200],{"class":295},[290,64550,64551],{"class":163,"line":784},[290,64552,334],{"emptyLinePlaceholder":333},[290,64554,64555,64558,64560,64562,64564,64566,64568,64570,64572,64574,64576,64578,64580,64582,64584,64586,64588],{"class":163,"line":812},[290,64556,64557],{"class":299},"    table",[290,64559,569],{"class":295},[290,64561,2269],{"class":299},[290,64563,569],{"class":295},[290,64565,2256],{"class":299},[290,64567,569],{"class":295},[290,64569,2274],{"class":299},[290,64571,790],{"class":295},[290,64573,59],{"class":461},[290,64575,465],{"class":295},[290,64577,68],{"class":461},[290,64579,828],{"class":295},[290,64581,1748],{"class":461},[290,64583,465],{"class":295},[290,64585,165],{"class":461},[290,64587,11018],{"class":541},[290,64589,809],{"class":295},[290,64591,64592],{"class":163,"line":860},[290,64593,334],{"emptyLinePlaceholder":333},[290,64595,64596,64599],{"class":163,"line":865},[290,64597,64598],{"class":299},"    tr",[290,64600,450],{"class":295},[290,64602,64603,64606,64608,64610,64612,64614,64616],{"class":163,"line":871},[290,64604,64605],{"class":461},"      border",[290,64607,465],{"class":295},[290,64609,468],{"class":461},[290,64611,674],{"class":541},[290,64613,852],{"class":461},[290,64615,50182],{"class":461},[290,64617,471],{"class":295},[290,64619,64620,64623,64625,64627,64629],{"class":163,"line":880},[290,64621,64622],{"class":461},"      border-radius",[290,64624,465],{"class":295},[290,64626,176],{"class":461},[290,64628,674],{"class":541},[290,64630,471],{"class":295},[290,64632,64633,64635,64637,64639,64641,64643,64645],{"class":163,"line":896},[290,64634,42626],{"class":461},[290,64636,465],{"class":295},[290,64638,798],{"class":461},[290,64640,801],{"class":541},[290,64642,50200],{"class":461},[290,64644,801],{"class":541},[290,64646,471],{"class":295},[290,64648,64649,64652,64654,64656,64658],{"class":163,"line":4734},[290,64650,64651],{"class":461},"      margin-bottom",[290,64653,465],{"class":295},[290,64655,823],{"class":461},[290,64657,801],{"class":541},[290,64659,471],{"class":295},[290,64661,64662],{"class":163,"line":4742},[290,64663,8200],{"class":295},[290,64665,64666],{"class":163,"line":4761},[290,64667,334],{"emptyLinePlaceholder":333},[290,64669,64670,64673],{"class":163,"line":4766},[290,64671,64672],{"class":299},"    td",[290,64674,450],{"class":295},[290,64676,64677,64679,64681,64683],{"class":163,"line":4786},[290,64678,37053],{"class":461},[290,64680,465],{"class":295},[290,64682,9147],{"class":461},[290,64684,471],{"class":295},[290,64686,64687,64689,64691,64693,64695,64697,64699],{"class":163,"line":9635},[290,64688,36130],{"class":461},[290,64690,465],{"class":295},[290,64692,4146],{"class":461},[290,64694,11018],{"class":541},[290,64696,804],{"class":461},[290,64698,11964],{"class":541},[290,64700,471],{"class":295},[290,64702,64703,64705,64707,64709,64711],{"class":163,"line":9641},[290,64704,38032],{"class":461},[290,64706,465],{"class":295},[290,64708,798],{"class":461},[290,64710,801],{"class":541},[290,64712,471],{"class":295},[290,64714,64715,64718,64720,64722,64724,64726,64729],{"class":163,"line":9647},[290,64716,64717],{"class":461},"      border-bottom",[290,64719,465],{"class":295},[290,64721,468],{"class":461},[290,64723,674],{"class":541},[290,64725,852],{"class":461},[290,64727,64728],{"class":461}," #eee",[290,64730,471],{"class":295},[290,64732,64733,64735,64737,64739,64741,64743],{"class":163,"line":9665},[290,64734,42626],{"class":461},[290,64736,465],{"class":295},[290,64738,169],{"class":461},[290,64740,801],{"class":541},[290,64742,1198],{"class":461},[290,64744,471],{"class":295},[290,64746,64747],{"class":163,"line":9683},[290,64748,8200],{"class":295},[290,64750,64751,64753,64756,64758,64760,64762,64764],{"class":163,"line":9688},[290,64752,64672],{"class":299},[290,64754,64755],{"class":303},":last-child",[290,64757,790],{"class":295},[290,64759,64390],{"class":461},[290,64761,465],{"class":295},[290,64763,487],{"class":461},[290,64765,809],{"class":295},[290,64767,64768],{"class":163,"line":9694},[290,64769,334],{"emptyLinePlaceholder":333},[290,64771,64772],{"class":163,"line":9700},[290,64773,64774],{"class":455},"    \u002F* Show the mirrored header next to each value. attr() reads the\n",[290,64776,64777],{"class":163,"line":9710},[290,64778,64779],{"class":455},"       data-label, so no duplicated visible markup is needed. *\u002F\n",[290,64781,64782,64784,64786],{"class":163,"line":9716},[290,64783,64672],{"class":299},[290,64785,2811],{"class":303},[290,64787,450],{"class":295},[290,64789,64790,64793,64795,64798,64800,64802],{"class":163,"line":9738},[290,64791,64792],{"class":461},"      content",[290,64794,465],{"class":295},[290,64796,64797],{"class":461},"attr",[290,64799,484],{"class":295},[290,64801,64186],{"class":1561},[290,64803,500],{"class":295},[290,64805,64806,64809,64811,64813],{"class":163,"line":9748},[290,64807,64808],{"class":461},"      font-weight",[290,64810,465],{"class":295},[290,64812,4176],{"class":461},[290,64814,471],{"class":295},[290,64816,64817,64820,64822,64825],{"class":163,"line":9754},[290,64818,64819],{"class":461},"      color",[290,64821,465],{"class":295},[290,64823,64824],{"class":461},"#444",[290,64826,471],{"class":295},[290,64828,64829],{"class":163,"line":9760},[290,64830,8200],{"class":295},[290,64832,64833],{"class":163,"line":9777},[290,64834,771],{"class":295},[290,64836,64837,64839,64841],{"class":163,"line":9787},[290,64838,431],{"class":295},[290,64840,1430],{"class":299},[290,64842,327],{"class":295},[290,64844,64845,64847,64849],{"class":163,"line":9793},[290,64846,431],{"class":295},[290,64848,9013],{"class":299},[290,64850,327],{"class":295},[290,64852,64853,64855,64857],{"class":163,"line":9799},[290,64854,296],{"class":295},[290,64856,9239],{"class":299},[290,64858,327],{"class":295},[290,64860,64861,64863,64865,64867,64869,64872],{"class":163,"line":9805},[290,64862,367],{"class":295},[290,64864,342],{"class":299},[290,64866,314],{"class":303},[290,64868,307],{"class":295},[290,64870,64871],{"class":310},"\"table-shell\"",[290,64873,327],{"class":295},[290,64875,64876],{"class":163,"line":9811},[290,64877,64878],{"class":455},"    \u003C!-- Roles are declared explicitly so the card display does not erase\n",[290,64880,64881],{"class":163,"line":9820},[290,64882,64883],{"class":455},"         table semantics for screen readers. -->\n",[290,64885,64886,64888,64890,64892,64894,64897],{"class":163,"line":9829},[290,64887,4290],{"class":295},[290,64889,2250],{"class":299},[290,64891,17652],{"class":303},[290,64893,307],{"class":295},[290,64895,64896],{"class":310},"\"table\"",[290,64898,327],{"class":295},[290,64900,64901,64903,64906,64909,64911],{"class":163,"line":27197},[290,64902,36430],{"class":295},[290,64904,64905],{"class":299},"caption",[290,64907,64908],{"class":295},">Recent orders\u003C\u002F",[290,64910,64905],{"class":299},[290,64912,327],{"class":295},[290,64914,64915,64917,64919,64921,64923,64926],{"class":163,"line":27202},[290,64916,36430],{"class":295},[290,64918,2253],{"class":299},[290,64920,17652],{"class":303},[290,64922,307],{"class":295},[290,64924,64925],{"class":310},"\"rowgroup\"",[290,64927,327],{"class":295},[290,64929,64930,64932,64934,64936,64938,64941],{"class":163,"line":27209},[290,64931,36461],{"class":295},[290,64933,2256],{"class":299},[290,64935,17652],{"class":303},[290,64937,307],{"class":295},[290,64939,64940],{"class":310},"\"row\"",[290,64942,327],{"class":295},[290,64944,64945,64947,64949,64951,64953,64956,64959,64961,64964,64967,64969],{"class":163,"line":27220},[290,64946,43817],{"class":295},[290,64948,2259],{"class":299},[290,64950,17652],{"class":303},[290,64952,307],{"class":295},[290,64954,64955],{"class":310},"\"columnheader\"",[290,64957,64958],{"class":303}," scope",[290,64960,307],{"class":295},[290,64962,64963],{"class":310},"\"col\"",[290,64965,64966],{"class":295},">Order\u003C\u002F",[290,64968,2259],{"class":299},[290,64970,327],{"class":295},[290,64972,64973,64975,64977,64979,64981,64983,64985,64987,64989,64992,64994],{"class":163,"line":27235},[290,64974,43817],{"class":295},[290,64976,2259],{"class":299},[290,64978,17652],{"class":303},[290,64980,307],{"class":295},[290,64982,64955],{"class":310},[290,64984,64958],{"class":303},[290,64986,307],{"class":295},[290,64988,64963],{"class":310},[290,64990,64991],{"class":295},">Customer\u003C\u002F",[290,64993,2259],{"class":299},[290,64995,327],{"class":295},[290,64997,64998,65000,65002,65004,65006,65008,65010,65012,65014,65017,65019],{"class":163,"line":27240},[290,64999,43817],{"class":295},[290,65001,2259],{"class":299},[290,65003,17652],{"class":303},[290,65005,307],{"class":295},[290,65007,64955],{"class":310},[290,65009,64958],{"class":303},[290,65011,307],{"class":295},[290,65013,64963],{"class":310},[290,65015,65016],{"class":295},">Status\u003C\u002F",[290,65018,2259],{"class":299},[290,65020,327],{"class":295},[290,65022,65023,65025,65027,65029,65031,65033,65035,65037,65039,65042,65044],{"class":163,"line":43950},[290,65024,43817],{"class":295},[290,65026,2259],{"class":299},[290,65028,17652],{"class":303},[290,65030,307],{"class":295},[290,65032,64955],{"class":310},[290,65034,64958],{"class":303},[290,65036,307],{"class":295},[290,65038,64963],{"class":310},[290,65040,65041],{"class":295},">Total\u003C\u002F",[290,65043,2259],{"class":299},[290,65045,327],{"class":295},[290,65047,65048,65050,65052],{"class":163,"line":43959},[290,65049,43844],{"class":295},[290,65051,2256],{"class":299},[290,65053,327],{"class":295},[290,65055,65056,65058,65060],{"class":163,"line":43968},[290,65057,36493],{"class":295},[290,65059,2253],{"class":299},[290,65061,327],{"class":295},[290,65063,65064,65066,65068,65070,65072,65074],{"class":163,"line":43977},[290,65065,36430],{"class":295},[290,65067,2269],{"class":299},[290,65069,17652],{"class":303},[290,65071,307],{"class":295},[290,65073,64925],{"class":310},[290,65075,327],{"class":295},[290,65077,65078,65080,65082,65084,65086,65088],{"class":163,"line":43986},[290,65079,36461],{"class":295},[290,65081,2256],{"class":299},[290,65083,17652],{"class":303},[290,65085,307],{"class":295},[290,65087,64940],{"class":310},[290,65089,327],{"class":295},[290,65091,65092,65094,65096,65098,65100,65103,65106,65108,65111,65114,65116],{"class":163,"line":43995},[290,65093,43817],{"class":295},[290,65095,2274],{"class":299},[290,65097,17652],{"class":303},[290,65099,307],{"class":295},[290,65101,65102],{"class":310},"\"cell\"",[290,65104,65105],{"class":303}," data-label",[290,65107,307],{"class":295},[290,65109,65110],{"class":310},"\"Order\"",[290,65112,65113],{"class":295},">#1042\u003C\u002F",[290,65115,2274],{"class":299},[290,65117,327],{"class":295},[290,65119,65120,65122,65124,65126,65128,65130,65132,65134,65137,65140,65142],{"class":163,"line":45140},[290,65121,43817],{"class":295},[290,65123,2274],{"class":299},[290,65125,17652],{"class":303},[290,65127,307],{"class":295},[290,65129,65102],{"class":310},[290,65131,65105],{"class":303},[290,65133,307],{"class":295},[290,65135,65136],{"class":310},"\"Customer\"",[290,65138,65139],{"class":295},">A. Okafor\u003C\u002F",[290,65141,2274],{"class":299},[290,65143,327],{"class":295},[290,65145,65146,65148,65150,65152,65154,65156,65158,65160,65163,65166,65168],{"class":163,"line":45145},[290,65147,43817],{"class":295},[290,65149,2274],{"class":299},[290,65151,17652],{"class":303},[290,65153,307],{"class":295},[290,65155,65102],{"class":310},[290,65157,65105],{"class":303},[290,65159,307],{"class":295},[290,65161,65162],{"class":310},"\"Status\"",[290,65164,65165],{"class":295},">Shipped\u003C\u002F",[290,65167,2274],{"class":299},[290,65169,327],{"class":295},[290,65171,65172,65174,65176,65178,65180,65182,65184,65186,65189,65192,65194],{"class":163,"line":45151},[290,65173,43817],{"class":295},[290,65175,2274],{"class":299},[290,65177,17652],{"class":303},[290,65179,307],{"class":295},[290,65181,65102],{"class":310},[290,65183,65105],{"class":303},[290,65185,307],{"class":295},[290,65187,65188],{"class":310},"\"Total\"",[290,65190,65191],{"class":295},">$58.00\u003C\u002F",[290,65193,2274],{"class":299},[290,65195,327],{"class":295},[290,65197,65198,65200,65202],{"class":163,"line":45157},[290,65199,43844],{"class":295},[290,65201,2256],{"class":299},[290,65203,327],{"class":295},[290,65205,65206,65208,65210,65212,65214,65216],{"class":163,"line":45165},[290,65207,36461],{"class":295},[290,65209,2256],{"class":299},[290,65211,17652],{"class":303},[290,65213,307],{"class":295},[290,65215,64940],{"class":310},[290,65217,327],{"class":295},[290,65219,65220,65222,65224,65226,65228,65230,65232,65234,65236,65239,65241],{"class":163,"line":45173},[290,65221,43817],{"class":295},[290,65223,2274],{"class":299},[290,65225,17652],{"class":303},[290,65227,307],{"class":295},[290,65229,65102],{"class":310},[290,65231,65105],{"class":303},[290,65233,307],{"class":295},[290,65235,65110],{"class":310},[290,65237,65238],{"class":295},">#1043\u003C\u002F",[290,65240,2274],{"class":299},[290,65242,327],{"class":295},[290,65244,65245,65247,65249,65251,65253,65255,65257,65259,65261,65264,65266],{"class":163,"line":45184},[290,65246,43817],{"class":295},[290,65248,2274],{"class":299},[290,65250,17652],{"class":303},[290,65252,307],{"class":295},[290,65254,65102],{"class":310},[290,65256,65105],{"class":303},[290,65258,307],{"class":295},[290,65260,65136],{"class":310},[290,65262,65263],{"class":295},">M. Singh\u003C\u002F",[290,65265,2274],{"class":299},[290,65267,327],{"class":295},[290,65269,65270,65272,65274,65276,65278,65280,65282,65284,65286,65289,65291],{"class":163,"line":45199},[290,65271,43817],{"class":295},[290,65273,2274],{"class":299},[290,65275,17652],{"class":303},[290,65277,307],{"class":295},[290,65279,65102],{"class":310},[290,65281,65105],{"class":303},[290,65283,307],{"class":295},[290,65285,65162],{"class":310},[290,65287,65288],{"class":295},">Pending\u003C\u002F",[290,65290,2274],{"class":299},[290,65292,327],{"class":295},[290,65294,65295,65297,65299,65301,65303,65305,65307,65309,65311,65314,65316],{"class":163,"line":45211},[290,65296,43817],{"class":295},[290,65298,2274],{"class":299},[290,65300,17652],{"class":303},[290,65302,307],{"class":295},[290,65304,65102],{"class":310},[290,65306,65105],{"class":303},[290,65308,307],{"class":295},[290,65310,65188],{"class":310},[290,65312,65313],{"class":295},">$129.50\u003C\u002F",[290,65315,2274],{"class":299},[290,65317,327],{"class":295},[290,65319,65320,65322,65324],{"class":163,"line":45225},[290,65321,43844],{"class":295},[290,65323,2256],{"class":299},[290,65325,327],{"class":295},[290,65327,65328,65330,65332,65334,65336,65338],{"class":163,"line":45230},[290,65329,36461],{"class":295},[290,65331,2256],{"class":299},[290,65333,17652],{"class":303},[290,65335,307],{"class":295},[290,65337,64940],{"class":310},[290,65339,327],{"class":295},[290,65341,65342,65344,65346,65348,65350,65352,65354,65356,65358,65361,65363],{"class":163,"line":45235},[290,65343,43817],{"class":295},[290,65345,2274],{"class":299},[290,65347,17652],{"class":303},[290,65349,307],{"class":295},[290,65351,65102],{"class":310},[290,65353,65105],{"class":303},[290,65355,307],{"class":295},[290,65357,65110],{"class":310},[290,65359,65360],{"class":295},">#1044\u003C\u002F",[290,65362,2274],{"class":299},[290,65364,327],{"class":295},[290,65366,65367,65369,65371,65373,65375,65377,65379,65381,65383,65386,65388],{"class":163,"line":45240},[290,65368,43817],{"class":295},[290,65370,2274],{"class":299},[290,65372,17652],{"class":303},[290,65374,307],{"class":295},[290,65376,65102],{"class":310},[290,65378,65105],{"class":303},[290,65380,307],{"class":295},[290,65382,65136],{"class":310},[290,65384,65385],{"class":295},">L. Romero\u003C\u002F",[290,65387,2274],{"class":299},[290,65389,327],{"class":295},[290,65391,65392,65394,65396,65398,65400,65402,65404,65406,65408,65411,65413],{"class":163,"line":45246},[290,65393,43817],{"class":295},[290,65395,2274],{"class":299},[290,65397,17652],{"class":303},[290,65399,307],{"class":295},[290,65401,65102],{"class":310},[290,65403,65105],{"class":303},[290,65405,307],{"class":295},[290,65407,65162],{"class":310},[290,65409,65410],{"class":295},">Refunded\u003C\u002F",[290,65412,2274],{"class":299},[290,65414,327],{"class":295},[290,65416,65417,65419,65421,65423,65425,65427,65429,65431,65433,65436,65438],{"class":163,"line":45264},[290,65418,43817],{"class":295},[290,65420,2274],{"class":299},[290,65422,17652],{"class":303},[290,65424,307],{"class":295},[290,65426,65102],{"class":310},[290,65428,65105],{"class":303},[290,65430,307],{"class":295},[290,65432,65188],{"class":310},[290,65434,65435],{"class":295},">$12.00\u003C\u002F",[290,65437,2274],{"class":299},[290,65439,327],{"class":295},[290,65441,65442,65444,65446],{"class":163,"line":45294},[290,65443,43844],{"class":295},[290,65445,2256],{"class":299},[290,65447,327],{"class":295},[290,65449,65450,65452,65454],{"class":163,"line":45299},[290,65451,36493],{"class":295},[290,65453,2269],{"class":299},[290,65455,327],{"class":295},[290,65457,65458,65460,65462],{"class":163,"line":45308},[290,65459,36502],{"class":295},[290,65461,2250],{"class":299},[290,65463,327],{"class":295},[290,65465,65466,65468,65470],{"class":163,"line":45317},[290,65467,4315],{"class":295},[290,65469,342],{"class":299},[290,65471,327],{"class":295},[290,65473,65474,65476,65478],{"class":163,"line":45326},[290,65475,431],{"class":295},[290,65477,9239],{"class":299},[290,65479,327],{"class":295},[290,65481,65482,65484,65486],{"class":163,"line":45343},[290,65483,431],{"class":295},[290,65485,285],{"class":299},[290,65487,327],{"class":295},[14,65489,65490,65491,65493],{},"The diagram below shows the transition the single ",[18,65492,12001],{}," block produces.",[133,65495,140,65497,140,65500,140,65503,140,65506,140,65509,140,65511,140,65513,140,65517,140,65519,140,65521,140,65523,140,65527,140,65530,140,65533,140,65535,140,65538,140,65541,140,65544,140,65547,140,65549,140,65553,140,65556,140,65559],{"viewBox":2588,"role":136,"ariaLabel":65496,"xmlns":138,"style":139},"A four-column data table in a wide container becoming a column of labelled cards in a narrow container",[142,65498,65499],{},"Table to stacked cards transition",[146,65501,65502],{},"Left: a row-and-column table. Right: each row stacked as a card with label and value pairs.",[150,65504,65505],{"x":2602,"y":26451,"style":26429},"Wide container",[150,65507,65508],{"x":5887,"y":26451,"style":26429},"Narrow container",[171,65510],{"x":158,"y":8879,"width":6740,"height":4147,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},[171,65512],{"x":4146,"y":4172,"width":11114,"height":158,"fill":177,"opacity":11349},[150,65514,65516],{"x":4146,"y":8882,"style":65515},"text-anchor:start;fill:currentColor;font:11px sans-serif;opacity:0.85","    Order   Customer   Status   Total",[163,65518],{"x1":4146,"y1":18845,"x2":2613,"y2":18845,"stroke":167,"opacity":169},[163,65520],{"x1":4146,"y1":4158,"x2":2613,"y2":4158,"stroke":167,"opacity":169},[163,65522],{"x1":4146,"y1":40954,"x2":2613,"y2":40954,"stroke":167,"opacity":169},[150,65524,65526],{"x":4146,"y":5159,"style":65525},"text-anchor:start;fill:currentColor;font:11px sans-serif;opacity:0.7","    #1042   A. Okafor  Shipped  $58",[150,65528,65529],{"x":4146,"y":17575,"style":65525},"    #1043   M. Singh   Pending  $130",[150,65531,65532],{"x":2602,"y":4184,"style":10248},"@container (max-width: 520px)",[171,65534],{"x":15019,"y":8879,"width":8947,"height":4158,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},[150,65536,65537],{"x":37885,"y":29485,"style":65515},"Order      #1042",[150,65539,65540],{"x":37885,"y":5115,"style":65515},"Customer   A. Okafor",[150,65542,65543],{"x":37885,"y":2614,"style":65515},"Status     Shipped",[150,65545,65546],{"x":37885,"y":17575,"style":65515},"Total      $58.00",[171,65548],{"x":15019,"y":5154,"width":8947,"height":4158,"rx":176,"fill":72,"stroke":167,"strokeWidth":168,"opacity":232},[150,65550,65552],{"x":37885,"y":65551,"style":65515},"202","Order      #1043",[150,65554,65555],{"x":37885,"y":191,"style":65515},"Customer   M. Singh",[150,65557,65558],{"x":37885,"y":19916,"style":65515},"Status     Pending",[150,65560,65561],{"x":37885,"y":6736,"style":65515},"Total      $129.50",[47,65563],{},[50,65565,65567,65568,6941,65571],{"id":65566},"key-technique-callout-attr-in-content","Key technique callout: ",[18,65569,65570],{},"attr()",[18,65572,11069],{},[14,65574,65575,65576,65579,65580,65582,65583,65585],{},"The mechanism that makes a stacked cell self-describing is ",[18,65577,65578],{},"td::before { content: attr(data-label); }",". In card mode the column header row is removed from view, so each value would otherwise be a context-free string. ",[18,65581,65570],{}," pulls the per-cell label out of the ",[18,65584,64186],{}," attribute and renders it as generated content, pairing \"Status\" with \"Shipped\" inside the same grid cell. Because the label lives in an attribute rather than duplicated text nodes, screen readers do not read it twice, and the visible label stays in lockstep with the markup with no JavaScript syncing two copies.",[47,65587],{},[50,65589,65591],{"id":65590},"variation-keep-a-scrollable-real-table-for-dense-data","Variation: keep a scrollable real table for dense data",[14,65593,65594,65595,65598],{},"Reflow is the wrong call for dense numeric tables that users compare by column. For those, preserve the genuine table and let it scroll horizontally, which keeps every implicit ARIA role intact. This sizing approach pairs well with the ",[27,65596,65597],{"href":47440},"intrinsic sizing techniques"," used across this site.",[281,65600,65602],{"className":438,"code":65601,"language":440,"meta":286,"style":286},"@container datatable (max-width: 520px) {\n  .table-shell {\n    overflow-x: auto;\n    \u002F* Make the scroll affordance discoverable and keyboard-reachable. *\u002F\n    -webkit-overflow-scrolling: touch;\n  }\n  table { min-width: 40rem; } \u002F* force columns to keep readable width *\u002F\n}\n",[18,65603,65604,65610,65616,65627,65632,65644,65648,65667],{"__ignoreMap":286},[290,65605,65606,65608],{"class":163,"line":292},[290,65607,12001],{"class":541},[290,65609,64473],{"class":295},[290,65611,65612,65614],{"class":163,"line":330},[290,65613,64256],{"class":303},[290,65615,450],{"class":295},[290,65617,65618,65621,65623,65625],{"class":163,"line":337},[290,65619,65620],{"class":461},"    overflow-x",[290,65622,465],{"class":295},[290,65624,250],{"class":461},[290,65626,471],{"class":295},[290,65628,65629],{"class":163,"line":364},[290,65630,65631],{"class":455},"    \u002F* Make the scroll affordance discoverable and keyboard-reachable. *\u002F\n",[290,65633,65634,65637,65639,65642],{"class":163,"line":386},[290,65635,65636],{"class":461},"    -webkit-overflow-scrolling",[290,65638,465],{"class":295},[290,65640,65641],{"class":461},"touch",[290,65643,471],{"class":295},[290,65645,65646],{"class":163,"line":408},[290,65647,771],{"class":295},[290,65649,65650,65652,65654,65656,65658,65660,65662,65664],{"class":163,"line":428},[290,65651,64296],{"class":299},[290,65653,790],{"class":295},[290,65655,26114],{"class":461},[290,65657,465],{"class":295},[290,65659,4146],{"class":461},[290,65661,801],{"class":541},[290,65663,6446],{"class":295},[290,65665,65666],{"class":455},"\u002F* force columns to keep readable width *\u002F\n",[290,65668,65669],{"class":163,"line":517},[290,65670,620],{"class":295},[14,65672,25028,65673,65676,65677,65680,65681,65683],{},[18,65674,65675],{},"tabindex=\"0\""," and an ",[18,65678,65679],{},"aria-label"," to the scroll container so keyboard users can reach and pan it. This variant has zero semantic cost because the ",[18,65682,64140],{}," never changes its display type.",[47,65685],{},[50,65687,1299],{"id":1298},[14,65689,65690,65691,65693,65694,65696,65697,69,65699,65701,65702,65704],{},"Size container queries are Baseline since Chrome and Edge 105, Safari 16, and Firefox 110, so ",[18,65692,12001],{}," is dependable in 2026. The ",[18,65695,65570],{}," function in ",[18,65698,11069],{},[18,65700,64531],{}," for visually hiding the header are universally supported across those same engines. For pre-2023 browsers, wrap the reflow in ",[18,65703,26207],{}," and fall back to the scrollable-table variant, which needs no modern features at all.",[47,65706],{},[50,65708,1316],{"id":1315},[14,65710,65711,65714],{},[62,65712,65713],{},"Why reflow a table with a container query instead of a media query?","\nA container query reacts to the table wrapper's own width, so an embedded table inside a narrow card or sidebar collapses correctly regardless of viewport. Media queries only see the screen and misjudge embedded contexts.",[14,65716,65717,65720,65721,65723,65724,65726,65727,65730],{},[62,65718,65719],{},"Does turning a table into cards break accessibility?","\nIt can. Setting ",[18,65722,59],{}," values other than ",[18,65725,2250],{}," on table elements removes their implicit ARIA table roles, so screen readers no longer announce rows, columns, and headers. Restore semantics with explicit ",[18,65728,65729],{},"role"," attributes or keep a horizontally scrollable real table as the fallback.",[14,65732,65733,65736,65737,65739,65740,65742,65743,65746],{},[62,65734,65735],{},"How do I show column headers next to each value in card mode?","\nMirror each header into a ",[18,65738,64186],{}," attribute on the cell and render it with a CSS ",[18,65741,2811],{}," that reads ",[18,65744,65745],{},"content: attr(data-label)",". This keeps the visible label in sync without duplicating markup the screen reader reads twice.",[14,65748,65749,65752],{},[62,65750,65751],{},"Should every table reflow into cards on small screens?","\nNo. Dense numeric tables that users scan by column are often better kept scrollable. Reflow suits record-style data where each row is an independent entity, like orders or users.",[47,65754],{},[50,65756,1391],{"id":1390},[1393,65758,65759,65764,65769,65774,65779],{},[1396,65760,65761,65763],{},[27,65762,38098],{"href":38097}," — the parent guide on context-aware component architecture.",[1396,65765,65766,65768],{},[27,65767,10100],{"href":1426}," — the card layout this table borrows in narrow mode.",[1396,65770,65771,65773],{},[27,65772,64061],{"href":63546}," — sibling component that collapses on container width.",[1396,65775,65776,65778],{},[27,65777,47441],{"href":47440}," — controlling column widths with min\u002Fmax\u002Ffit-content.",[1396,65780,65781,65783],{},[27,65782,6532],{"href":3349}," — cross-area guide for the scrollable table's keyboard affordance.",[1430,65785,65786],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":286,"searchDepth":330,"depth":330,"links":65788},[65789,65790,65791,65793,65794,65795,65796],{"id":64124,"depth":330,"text":64125},{"id":4202,"depth":330,"text":4203},{"id":65566,"depth":330,"text":65792},"Key technique callout: attr() in content",{"id":65590,"depth":330,"text":65591},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Reflow wide data tables into stacked cards in narrow containers using @container, data-label attributes, and CSS Grid, with accessibility caveats explained.",{"seoTitle":65799,"datePublished":1447,"dateModified":1447,"faq":65800},"Container Query Data Tables in CSS",[65801,65803,65805,65807],{"q":65713,"a":65802},"A container query reacts to the table wrapper's own width, so an embedded table inside a narrow card or sidebar collapses correctly regardless of viewport. Media queries only see the screen and misjudge embedded contexts.",{"q":65719,"a":65804},"It can. Setting display values other than table on table elements removes their implicit ARIA table roles, so screen readers no longer announce rows, columns, and headers. Restore semantics with explicit role attributes or keep a horizontally scrollable real table as the fallback.",{"q":65735,"a":65806},"Mirror each header into a data-label attribute on the cell and render it with a CSS ::before that reads content: attr(data-label). This keeps the visible label in sync without duplicating markup the screen reader reads twice.",{"q":65751,"a":65808},"No. Dense numeric tables that users scan by column are often better kept scrollable. Reflow suits record-style data where each row is an independent entity, like orders or users.","\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-data-tables",{"title":64102,"description":65797},"mastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fcontainer-query-data-tables\u002Findex","rYb-HrWR00UWMAcB2e8ZQAFHg-N_UlL78EvpFzpzslA",{"id":65814,"title":65815,"body":65816,"description":67295,"extension":1444,"meta":67296,"navigation":333,"path":67305,"seo":67306,"stem":67307,"__hash__":67308},"content\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Findex.md","Responsive Component Patterns: Architecture & Implementation",{"type":7,"value":65817,"toc":67283},[65818,65821,65830,65835,65848,65889,65891,65895,65898,65904,65951,65963,65968,65996,65998,66002,66010,66245,66254,66258,66289,66291,66295,66301,66449,66454,66487,66489,66493,66496,66693,67022,67034,67036,67073,67075,67077,67123,67137,67139,67143,67222,67224,67226,67232,67241,67247,67249,67251,67280],[10,65819,65815],{"id":65820},"responsive-component-patterns-architecture-implementation",[14,65822,65823,65824,65826,65827,65829],{},"Modern interface development demands a decisive shift away from rigid viewport breakpoints toward context-aware architectures. As component libraries scale, relying on global media queries creates brittle, tightly coupled styles that break when modules are placed in unpredictable parent contexts. This guide explores production-ready ",[62,65825,64113],{}," that enable UI modules to adapt dynamically to their available space. By integrating foundational concepts from ",[27,65828,11296],{"href":5777},", engineers can build scalable, maintainable interfaces that prioritize component autonomy over global layout constraints.",[14,65831,65832],{},[62,65833,65834],{},"Core Implementation Goals:",[1393,65836,65837,65839,65842,65845],{},[1396,65838,63405],{},[1396,65840,65841],{},"Implement intrinsic sizing and fluid scaling techniques",[1396,65843,65844],{},"Leverage CSS containment for predictable rendering",[1396,65846,65847],{},"Design state-driven micro-interactions tied to container context",[133,65849,140,65851,140,65854,140,65857,140,65860,140,65862,140,65864,140,65866,140,65868,140,65871,140,65874,140,65876,140,65878,140,65881,140,65883,140,65886],{"viewBox":1771,"role":136,"ariaLabel":65850,"xmlns":138,"style":139},"One component layout that switches from stacked to side-by-side based on its container width, not the viewport",[142,65852,65853],{},"Container-driven layout switch",[146,65855,65856],{},"The same card renders stacked in a narrow container and side-by-side in a wide container.",[150,65858,65859],{"x":152,"y":153,"style":1781},"Same component, container decides layout",[171,65861],{"x":4146,"y":1788,"width":538,"height":1822,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},[150,65863,25443],{"x":1787,"y":9105,"style":1808},[171,65865],{"x":2630,"y":6642,"width":6673,"height":1788,"rx":5901,"fill":177,"opacity":11349},[171,65867],{"x":2630,"y":1830,"width":6673,"height":5139,"rx":5901,"fill":177,"opacity":40019},[150,65869,63439],{"x":1787,"y":22564,"style":65870},"text-anchor:middle;fill:currentColor;font:11px ui-monospace,monospace",[150,65872,150],{"x":1787,"y":65873,"style":65870},"188",[171,65875],{"x":5397,"y":1788,"width":17554,"height":1822,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},[150,65877,25454],{"x":26446,"y":9105,"style":1808},[171,65879],{"x":65880,"y":6642,"width":1830,"height":2614,"rx":5901,"fill":177,"opacity":11349},"324",[171,65882],{"x":18875,"y":6642,"width":8904,"height":2614,"rx":5901,"fill":177,"opacity":40019},[150,65884,63439],{"x":65885,"y":8904,"style":65870},"404",[150,65887,150],{"x":65888,"y":8904,"style":65870},"578",[47,65890],{},[50,65892,65894],{"id":65893},"the-container-first-paradigm","The Container-First Paradigm",[14,65896,65897],{},"Viewport-centric breakpoints fail at scale because they assume a fixed relationship between the screen and the component. In reality, a sidebar card, a modal, and a hero banner may share identical markup but require drastically different layouts. The solution is CSS containment: explicitly defining a boundary where a component's layout and style calculations are isolated from the rest of the document tree.",[14,65899,65900,65901,65903],{},"To establish a containment context, apply ",[18,65902,24401],{}," to the parent wrapper. This tells the browser to track the element's inline and block dimensions for descendant queries.",[281,65905,65907],{"className":438,"code":65906,"language":440,"meta":286,"style":286},"\u002F* Production-ready containment setup *\u002F\n.component-wrapper {\n  container-type: inline-size;\n  container-name: card;\n  \u002F* Optional: isolate layout\u002Fstyle for performance *\u002F\n  contain: layout style;\n}\n",[18,65908,65909,65914,65920,65926,65932,65937,65947],{"__ignoreMap":286},[290,65910,65911],{"class":163,"line":292},[290,65912,65913],{"class":455},"\u002F* Production-ready containment setup *\u002F\n",[290,65915,65916,65918],{"class":163,"line":330},[290,65917,56223],{"class":303},[290,65919,450],{"class":295},[290,65921,65922,65924],{"class":163,"line":337},[290,65923,11509],{"class":461},[290,65925,25565],{"class":295},[290,65927,65928,65930],{"class":163,"line":364},[290,65929,25575],{"class":461},[290,65931,37983],{"class":295},[290,65933,65934],{"class":163,"line":386},[290,65935,65936],{"class":455},"  \u002F* Optional: isolate layout\u002Fstyle for performance *\u002F\n",[290,65938,65939,65941,65943,65945],{"class":163,"line":408},[290,65940,40253],{"class":461},[290,65942,41070],{"class":295},[290,65944,1430],{"class":461},[290,65946,471],{"class":295},[290,65948,65949],{"class":163,"line":428},[290,65950,620],{"class":295},[14,65952,65953,65954,65956,65957,65959,65960,65962],{},"When structuring containment boundaries, always pair ",[18,65955,24401],{}," with a ",[18,65958,26072],{}," in multi-component environments. This prevents query collisions and allows precise targeting. For optimal rule structuring and syntax validation, refer to ",[27,65961,26284],{"href":25340}," before scaling across design systems.",[14,65964,65965],{},[62,65966,65967],{},"Key Architectural Rules:",[3017,65969,65970,65979,65990],{},[1396,65971,65972,65975,65976,65978],{},[62,65973,65974],{},"Explicit Boundaries:"," Never query an element without first declaring ",[18,65977,24401],{},". Implicit containment is not supported.",[1396,65980,65981,2694,65984,65986,65987,65989],{},[62,65982,65983],{},"Inline vs. Block:",[18,65985,26044],{}," is the most performant and widely supported. Use ",[18,65988,42926],{}," only when vertical space dictates layout shifts.",[1396,65991,65992,65995],{},[62,65993,65994],{},"Avoid Thrashing:"," Containment prevents layout thrashing by halting global reflows. The browser only recalculates the isolated subtree when the container resizes.",[47,65997],{},[50,65999,66001],{"id":66000},"fluid-scaling-intrinsic-architecture","Fluid Scaling & Intrinsic Architecture",[14,66003,66004,66005,569,66007,66009],{},"Once containment is established, components should scale mathematically rather than through discrete breakpoints. By combining container query units (",[18,66006,11404],{},[18,66008,12282],{},") with modern sizing functions, you create layouts that fluidly adapt to any available space.",[281,66011,66013],{"className":438,"code":66012,"language":440,"meta":286,"style":286},":root {\n  --space-xs: clamp(0.5rem, 1cqi, 1rem);\n  --space-md: clamp(1rem, 3cqi, 2rem);\n  --space-xl: clamp(1.5rem, 5cqi, 3rem);\n}\n\n.component {\n  width: clamp(16rem, 50cqi, 32rem);\n  padding: var(--space-md);\n  aspect-ratio: 16 \u002F 9;\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));\n  gap: var(--space-xs);\n}\n",[18,66014,66015,66021,66050,66078,66107,66111,66115,66121,66149,66163,66177,66187,66227,66241],{"__ignoreMap":286},[290,66016,66017,66019],{"class":163,"line":292},[290,66018,1554],{"class":303},[290,66020,450],{"class":295},[290,66022,66023,66026,66028,66030,66032,66034,66036,66038,66040,66042,66044,66046,66048],{"class":163,"line":330},[290,66024,66025],{"class":1561},"  --space-xs",[290,66027,465],{"class":295},[290,66029,11555],{"class":461},[290,66031,484],{"class":295},[290,66033,798],{"class":461},[290,66035,801],{"class":541},[290,66037,569],{"class":295},[290,66039,468],{"class":461},[290,66041,11404],{"class":541},[290,66043,569],{"class":295},[290,66045,468],{"class":461},[290,66047,801],{"class":541},[290,66049,500],{"class":295},[290,66051,66052,66054,66056,66058,66060,66062,66064,66066,66068,66070,66072,66074,66076],{"class":163,"line":337},[290,66053,12630],{"class":1561},[290,66055,465],{"class":295},[290,66057,11555],{"class":461},[290,66059,484],{"class":295},[290,66061,468],{"class":461},[290,66063,801],{"class":541},[290,66065,569],{"class":295},[290,66067,1579],{"class":461},[290,66069,11404],{"class":541},[290,66071,569],{"class":295},[290,66073,194],{"class":461},[290,66075,801],{"class":541},[290,66077,500],{"class":295},[290,66079,66080,66083,66085,66087,66089,66091,66093,66095,66097,66099,66101,66103,66105],{"class":163,"line":364},[290,66081,66082],{"class":1561},"  --space-xl",[290,66084,465],{"class":295},[290,66086,11555],{"class":461},[290,66088,484],{"class":295},[290,66090,168],{"class":461},[290,66092,801],{"class":541},[290,66094,569],{"class":295},[290,66096,5911],{"class":461},[290,66098,11404],{"class":541},[290,66100,569],{"class":295},[290,66102,1579],{"class":461},[290,66104,801],{"class":541},[290,66106,500],{"class":295},[290,66108,66109],{"class":163,"line":386},[290,66110,620],{"class":295},[290,66112,66113],{"class":163,"line":408},[290,66114,334],{"emptyLinePlaceholder":333},[290,66116,66117,66119],{"class":163,"line":428},[290,66118,55852],{"class":303},[290,66120,450],{"class":295},[290,66122,66123,66125,66127,66129,66131,66133,66135,66137,66139,66141,66143,66145,66147],{"class":163,"line":517},[290,66124,17904],{"class":461},[290,66126,465],{"class":295},[290,66128,11555],{"class":461},[290,66130,484],{"class":295},[290,66132,9212],{"class":461},[290,66134,801],{"class":541},[290,66136,569],{"class":295},[290,66138,1831],{"class":461},[290,66140,11404],{"class":541},[290,66142,569],{"class":295},[290,66144,5891],{"class":461},[290,66146,801],{"class":541},[290,66148,500],{"class":295},[290,66150,66151,66153,66155,66157,66159,66161],{"class":163,"line":523},[290,66152,10433],{"class":461},[290,66154,465],{"class":295},[290,66156,1622],{"class":461},[290,66158,484],{"class":295},[290,66160,38041],{"class":1561},[290,66162,500],{"class":295},[290,66164,66165,66167,66169,66171,66173,66175],{"class":163,"line":532},[290,66166,57521],{"class":461},[290,66168,465],{"class":295},[290,66170,9212],{"class":461},[290,66172,1203],{"class":295},[290,66174,247],{"class":461},[290,66176,471],{"class":295},[290,66178,66179,66181,66183,66185],{"class":163,"line":551},[290,66180,17742],{"class":461},[290,66182,465],{"class":295},[290,66184,9147],{"class":461},[290,66186,471],{"class":295},[290,66188,66189,66191,66193,66195,66197,66199,66201,66203,66205,66207,66209,66211,66213,66215,66217,66219,66221,66223,66225],{"class":163,"line":586},[290,66190,47811],{"class":461},[290,66192,465],{"class":295},[290,66194,44542],{"class":461},[290,66196,484],{"class":295},[290,66198,44547],{"class":461},[290,66200,569],{"class":295},[290,66202,44552],{"class":461},[290,66204,484],{"class":295},[290,66206,32765],{"class":461},[290,66208,484],{"class":295},[290,66210,165],{"class":461},[290,66212,11018],{"class":541},[290,66214,569],{"class":295},[290,66216,5894],{"class":461},[290,66218,801],{"class":541},[290,66220,24264],{"class":295},[290,66222,468],{"class":461},[290,66224,11964],{"class":541},[290,66226,11616],{"class":295},[290,66228,66229,66231,66233,66235,66237,66239],{"class":163,"line":602},[290,66230,26819],{"class":461},[290,66232,465],{"class":295},[290,66234,1622],{"class":461},[290,66236,484],{"class":295},[290,66238,51926],{"class":1561},[290,66240,500],{"class":295},[290,66242,66243],{"class":163,"line":617},[290,66244,620],{"class":295},[14,66246,66247,66248,66250,66251,66253],{},"This approach synchronizes component scaling with typographic rhythm. When paired with ",[27,66249,12361],{"href":11312},", your entire UI maintains visual harmony across dynamic viewports without hard-coded media queries. For the macro structure these components sit inside, the ",[27,66252,55627],{"href":44306}," guide covers how track sizing and subgrid let children share a single alignment context.",[14,66255,66256],{},[62,66257,3678],{},[1393,66259,66260,66267,66275,66281],{},[1396,66261,1499,66262,69,66264,66266],{},[18,66263,32822],{},[18,66265,51111],{}," to cap intrinsic values before they break layout.",[1396,66268,41160,66269,66271,66272,66274],{},[18,66270,11404],{}," (container query inline) over ",[18,66273,12094],{}," for internal component scaling.",[1396,66276,66277,66278,66280],{},"Define spacing tokens as ",[18,66279,12276],{}," expressions to ensure minimum viable padding at extreme container sizes.",[1396,66282,66283,66284,1692,66287],{},"Reference: ",[86,66285,66286],{},"CSS Values and Units Module Level 4",[86,66288,59060],{},[47,66290],{},[50,66292,66294],{"id":66293},"state-queries-conditional-styling","State Queries & Conditional Styling",[14,66296,66297,66298,66300],{},"Beyond spatial adaptation, modern CSS allows you to query computed styles and CSS custom properties. ",[18,66299,24933],{}," enables theme toggling, density adjustments, and interactive state management without JavaScript.",[281,66302,66304],{"className":438,"code":66303,"language":440,"meta":286,"style":286},"\u002F* Combined dimension and style query *\u002F\n@container card (min-width: 40rem) and style(--theme: dark) {\n  .component__header {\n    background: #1a1a1a;\n    color: #f5f5f5;\n    border-bottom: 1px solid #333;\n  }\n}\n\n\u002F* Fallback for older browsers *\u002F\n@supports not (container-type: inline-size) {\n  @media (min-width: 640px) {\n    .component__header {\n      background: #1a1a1a;\n      color: #f5f5f5;\n    }\n  }\n}\n",[18,66305,66306,66311,66318,66325,66335,66346,66364,66368,66372,66376,66381,66393,66409,66416,66427,66437,66441,66445],{"__ignoreMap":286},[290,66307,66308],{"class":163,"line":292},[290,66309,66310],{"class":455},"\u002F* Combined dimension and style query *\u002F\n",[290,66312,66313,66315],{"class":163,"line":330},[290,66314,12001],{"class":541},[290,66316,66317],{"class":295}," card (min-width: 40rem) and style(--theme: dark) {\n",[290,66319,66320,66323],{"class":163,"line":337},[290,66321,66322],{"class":303},"  .component__header",[290,66324,450],{"class":295},[290,66326,66327,66329,66331,66333],{"class":163,"line":364},[290,66328,9124],{"class":461},[290,66330,465],{"class":295},[290,66332,42293],{"class":461},[290,66334,471],{"class":295},[290,66336,66337,66339,66341,66344],{"class":163,"line":386},[290,66338,36064],{"class":461},[290,66340,465],{"class":295},[290,66342,66343],{"class":461},"#f5f5f5",[290,66345,471],{"class":295},[290,66347,66348,66351,66353,66355,66357,66359,66362],{"class":163,"line":408},[290,66349,66350],{"class":461},"    border-bottom",[290,66352,465],{"class":295},[290,66354,468],{"class":461},[290,66356,674],{"class":541},[290,66358,852],{"class":461},[290,66360,66361],{"class":461}," #333",[290,66363,471],{"class":295},[290,66365,66366],{"class":163,"line":428},[290,66367,771],{"class":295},[290,66369,66370],{"class":163,"line":517},[290,66371,620],{"class":295},[290,66373,66374],{"class":163,"line":523},[290,66375,334],{"emptyLinePlaceholder":333},[290,66377,66378],{"class":163,"line":532},[290,66379,66380],{"class":455},"\u002F* Fallback for older browsers *\u002F\n",[290,66382,66383,66385,66387,66389,66391],{"class":163,"line":551},[290,66384,2086],{"class":541},[290,66386,2116],{"class":541},[290,66388,3595],{"class":295},[290,66390,24401],{"class":461},[290,66392,26104],{"class":295},[290,66394,66395,66397,66399,66401,66403,66405,66407],{"class":163,"line":586},[290,66396,26109],{"class":541},[290,66398,3595],{"class":295},[290,66400,26114],{"class":461},[290,66402,465],{"class":295},[290,66404,14990],{"class":461},[290,66406,674],{"class":541},[290,66408,646],{"class":295},[290,66410,66411,66414],{"class":163,"line":602},[290,66412,66413],{"class":303},"    .component__header",[290,66415,450],{"class":295},[290,66417,66418,66421,66423,66425],{"class":163,"line":617},[290,66419,66420],{"class":461},"      background",[290,66422,465],{"class":295},[290,66424,42293],{"class":461},[290,66426,471],{"class":295},[290,66428,66429,66431,66433,66435],{"class":163,"line":623},[290,66430,64819],{"class":461},[290,66432,465],{"class":295},[290,66434,66343],{"class":461},[290,66436,471],{"class":295},[290,66438,66439],{"class":163,"line":628},[290,66440,8200],{"class":295},[290,66442,66443],{"class":163,"line":634},[290,66444,771],{"class":295},[290,66446,66447],{"class":163,"line":649},[290,66448,620],{"class":295},[14,66450,66451],{},[62,66452,66453],{},"Performance & Specificity Guidelines:",[1393,66455,66456,66462,66481],{},[1396,66457,66458,66461],{},[62,66459,66460],{},"Chain Conditions Sparingly:"," Complex query chains increase style recalculation overhead. Limit to 2-3 conditions per rule.",[1396,66463,66464,66467,66468,569,66470,66472,66473,569,66475,1745,66478,42],{},[62,66465,66466],{},"Optimize Paint Cycles:"," Avoid querying frequently changing properties (e.g., ",[18,66469,103],{},[18,66471,76],{},"). Stick to static design tokens like ",[18,66474,42238],{},[18,66476,66477],{},"--density",[18,66479,66480],{},"--variant",[1396,66482,66483,66486],{},[62,66484,66485],{},"Specificity Management:"," Container queries inherit specificity rules. Keep them scoped to component BEM\u002FITCSS namespaces to prevent global override conflicts.",[47,66488],{},[50,66490,66492],{"id":66491},"production-implementation-micro-interactions","Production Implementation & Micro-Interactions",[14,66494,66495],{},"A truly resilient component pairs responsive architecture with progressive enhancement and accessible micro-interactions. Below is a complete, copy-paste-ready card implementation that demonstrates container-driven layout shifts, hover states, and motion preferences.",[281,66497,66499],{"className":283,"code":66498,"language":285,"meta":286,"style":286},"\u003Carticle class=\"card-container\">\n  \u003Cdiv class=\"card\" style=\"--theme: light\">\n    \u003Cimg\n      class=\"card__media\"\n      src=\"image.webp\"\n      alt=\"Descriptive alt text\"\n      loading=\"lazy\"\n    \u002F>\n    \u003Cdiv class=\"card__content\">\n      \u003Ch3 class=\"card__title\">Adaptive Component\u003C\u002Fh3>\n      \u003Cp class=\"card__desc\">Scales fluidly within any parent context.\u003C\u002Fp>\n      \u003Cbutton class=\"card__action\" aria-label=\"View details\">Explore\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Farticle>\n",[18,66500,66501,66516,66537,66544,66554,66564,66574,66584,66589,66603,66622,66642,66669,66677,66685],{"__ignoreMap":286},[290,66502,66503,66505,66507,66509,66511,66514],{"class":163,"line":292},[290,66504,296],{"class":295},[290,66506,11445],{"class":299},[290,66508,314],{"class":303},[290,66510,307],{"class":295},[290,66512,66513],{"class":310},"\"card-container\"",[290,66515,327],{"class":295},[290,66517,66518,66520,66522,66524,66526,66528,66530,66532,66535],{"class":163,"line":330},[290,66519,367],{"class":295},[290,66521,342],{"class":299},[290,66523,314],{"class":303},[290,66525,307],{"class":295},[290,66527,9295],{"class":310},[290,66529,30472],{"class":303},[290,66531,307],{"class":295},[290,66533,66534],{"class":310},"\"--theme: light\"",[290,66536,327],{"class":295},[290,66538,66539,66541],{"class":163,"line":337},[290,66540,4290],{"class":295},[290,66542,66543],{"class":299},"img\n",[290,66545,66546,66549,66551],{"class":163,"line":364},[290,66547,66548],{"class":303},"      class",[290,66550,307],{"class":295},[290,66552,66553],{"class":310},"\"card__media\"\n",[290,66555,66556,66559,66561],{"class":163,"line":386},[290,66557,66558],{"class":303},"      src",[290,66560,307],{"class":295},[290,66562,66563],{"class":310},"\"image.webp\"\n",[290,66565,66566,66569,66571],{"class":163,"line":408},[290,66567,66568],{"class":303},"      alt",[290,66570,307],{"class":295},[290,66572,66573],{"class":310},"\"Descriptive alt text\"\n",[290,66575,66576,66579,66581],{"class":163,"line":428},[290,66577,66578],{"class":303},"      loading",[290,66580,307],{"class":295},[290,66582,66583],{"class":310},"\"lazy\"\n",[290,66585,66586],{"class":163,"line":517},[290,66587,66588],{"class":295},"    \u002F>\n",[290,66590,66591,66593,66595,66597,66599,66601],{"class":163,"line":523},[290,66592,4290],{"class":295},[290,66594,342],{"class":299},[290,66596,314],{"class":303},[290,66598,307],{"class":295},[290,66600,29603],{"class":310},[290,66602,327],{"class":295},[290,66604,66605,66607,66609,66611,66613,66615,66618,66620],{"class":163,"line":532},[290,66606,36430],{"class":295},[290,66608,2757],{"class":299},[290,66610,314],{"class":303},[290,66612,307],{"class":295},[290,66614,45369],{"class":310},[290,66616,66617],{"class":295},">Adaptive Component\u003C\u002F",[290,66619,2757],{"class":299},[290,66621,327],{"class":295},[290,66623,66624,66626,66628,66630,66632,66635,66638,66640],{"class":163,"line":551},[290,66625,36430],{"class":295},[290,66627,14],{"class":299},[290,66629,314],{"class":303},[290,66631,307],{"class":295},[290,66633,66634],{"class":310},"\"card__desc\"",[290,66636,66637],{"class":295},">Scales fluidly within any parent context.\u003C\u002F",[290,66639,14],{"class":299},[290,66641,327],{"class":295},[290,66643,66644,66646,66648,66650,66652,66655,66657,66659,66662,66665,66667],{"class":163,"line":586},[290,66645,36430],{"class":295},[290,66647,300],{"class":299},[290,66649,314],{"class":303},[290,66651,307],{"class":295},[290,66653,66654],{"class":310},"\"card__action\"",[290,66656,19002],{"class":303},[290,66658,307],{"class":295},[290,66660,66661],{"class":310},"\"View details\"",[290,66663,66664],{"class":295},">Explore\u003C\u002F",[290,66666,300],{"class":299},[290,66668,327],{"class":295},[290,66670,66671,66673,66675],{"class":163,"line":602},[290,66672,36502],{"class":295},[290,66674,342],{"class":299},[290,66676,327],{"class":295},[290,66678,66679,66681,66683],{"class":163,"line":617},[290,66680,4315],{"class":295},[290,66682,342],{"class":299},[290,66684,327],{"class":295},[290,66686,66687,66689,66691],{"class":163,"line":623},[290,66688,431],{"class":295},[290,66690,11445],{"class":299},[290,66692,327],{"class":295},[281,66694,66696],{"className":438,"code":66695,"language":440,"meta":286,"style":286},"\u002F* Container & Progressive Enhancement *\u002F\n.card-container {\n  container-type: inline-size;\n  container-name: card;\n}\n\n@supports (container-type: inline-size) {\n  .card {\n    display: flex;\n    flex-direction: column;\n    gap: var(--space-xs);\n    padding: var(--space-md);\n    border-radius: 0.75rem;\n    background: var(--surface);\n    transition:\n      transform 0.2s ease,\n      box-shadow 0.2s ease;\n  }\n\n  @container card (min-width: 28rem) {\n    .card {\n      flex-direction: row;\n      align-items: center;\n    }\n    .card__media {\n      width: 12rem;\n      height: auto;\n    }\n  }\n\n  \u002F* Micro-interactions & Reduced Motion *\u002F\n  @media (prefers-reduced-motion: no-preference) {\n    .card:hover {\n      transform: translateY(-2px);\n      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n    }\n  }\n}\n",[18,66697,66698,66703,66710,66716,66722,66726,66730,66740,66746,66756,66766,66780,66794,66806,66821,66827,66839,66852,66856,66860,66867,66873,66883,66893,66897,66903,66915,66925,66929,66933,66937,66942,66948,66955,66972,67010,67014,67018],{"__ignoreMap":286},[290,66699,66700],{"class":163,"line":292},[290,66701,66702],{"class":455},"\u002F* Container & Progressive Enhancement *\u002F\n",[290,66704,66705,66708],{"class":163,"line":330},[290,66706,66707],{"class":303},".card-container",[290,66709,450],{"class":295},[290,66711,66712,66714],{"class":163,"line":337},[290,66713,11509],{"class":461},[290,66715,25565],{"class":295},[290,66717,66718,66720],{"class":163,"line":364},[290,66719,25575],{"class":461},[290,66721,37983],{"class":295},[290,66723,66724],{"class":163,"line":386},[290,66725,620],{"class":295},[290,66727,66728],{"class":163,"line":408},[290,66729,334],{"emptyLinePlaceholder":333},[290,66731,66732,66734,66736,66738],{"class":163,"line":428},[290,66733,2086],{"class":541},[290,66735,3595],{"class":295},[290,66737,24401],{"class":461},[290,66739,26104],{"class":295},[290,66741,66742,66744],{"class":163,"line":517},[290,66743,9083],{"class":303},[290,66745,450],{"class":295},[290,66747,66748,66750,66752,66754],{"class":163,"line":523},[290,66749,34590],{"class":461},[290,66751,465],{"class":295},[290,66753,9055],{"class":461},[290,66755,471],{"class":295},[290,66757,66758,66760,66762,66764],{"class":163,"line":532},[290,66759,40107],{"class":461},[290,66761,465],{"class":295},[290,66763,40315],{"class":461},[290,66765,471],{"class":295},[290,66767,66768,66770,66772,66774,66776,66778],{"class":163,"line":551},[290,66769,36027],{"class":461},[290,66771,465],{"class":295},[290,66773,1622],{"class":461},[290,66775,484],{"class":295},[290,66777,51926],{"class":1561},[290,66779,500],{"class":295},[290,66781,66782,66784,66786,66788,66790,66792],{"class":163,"line":586},[290,66783,36040],{"class":461},[290,66785,465],{"class":295},[290,66787,1622],{"class":461},[290,66789,484],{"class":295},[290,66791,38041],{"class":1561},[290,66793,500],{"class":295},[290,66795,66796,66798,66800,66802,66804],{"class":163,"line":602},[290,66797,12759],{"class":461},[290,66799,465],{"class":295},[290,66801,823],{"class":461},[290,66803,801],{"class":541},[290,66805,471],{"class":295},[290,66807,66808,66810,66812,66814,66816,66819],{"class":163,"line":617},[290,66809,9124],{"class":461},[290,66811,465],{"class":295},[290,66813,1622],{"class":461},[290,66815,484],{"class":295},[290,66817,66818],{"class":1561},"--surface",[290,66820,500],{"class":295},[290,66822,66823,66825],{"class":163,"line":623},[290,66824,4745],{"class":461},[290,66826,529],{"class":295},[290,66828,66829,66831,66833,66835,66837],{"class":163,"line":628},[290,66830,20298],{"class":295},[290,66832,566],{"class":461},[290,66834,1886],{"class":541},[290,66836,545],{"class":461},[290,66838,548],{"class":295},[290,66840,66841,66844,66846,66848,66850],{"class":163,"line":634},[290,66842,66843],{"class":295},"      box-shadow ",[290,66845,566],{"class":461},[290,66847,1886],{"class":541},[290,66849,545],{"class":461},[290,66851,471],{"class":295},[290,66853,66854],{"class":163,"line":649},[290,66855,771],{"class":295},[290,66857,66858],{"class":163,"line":660},[290,66859,334],{"emptyLinePlaceholder":333},[290,66861,66862,66864],{"class":163,"line":688},[290,66863,37040],{"class":541},[290,66865,66866],{"class":295}," card (min-width: 28rem) {\n",[290,66868,66869,66871],{"class":163,"line":693},[290,66870,26128],{"class":303},[290,66872,450],{"class":295},[290,66874,66875,66877,66879,66881],{"class":163,"line":698},[290,66876,38020],{"class":461},[290,66878,465],{"class":295},[290,66880,38025],{"class":461},[290,66882,471],{"class":295},[290,66884,66885,66887,66889,66891],{"class":163,"line":704},[290,66886,36150],{"class":461},[290,66888,465],{"class":295},[290,66890,9157],{"class":461},[290,66892,471],{"class":295},[290,66894,66895],{"class":163,"line":710},[290,66896,8200],{"class":295},[290,66898,66899,66901],{"class":163,"line":717},[290,66900,43611],{"class":303},[290,66902,450],{"class":295},[290,66904,66905,66907,66909,66911,66913],{"class":163,"line":730},[290,66906,57028],{"class":461},[290,66908,465],{"class":295},[290,66910,5894],{"class":461},[290,66912,801],{"class":541},[290,66914,471],{"class":295},[290,66916,66917,66919,66921,66923],{"class":163,"line":742},[290,66918,57102],{"class":461},[290,66920,465],{"class":295},[290,66922,250],{"class":461},[290,66924,471],{"class":295},[290,66926,66927],{"class":163,"line":768},[290,66928,8200],{"class":295},[290,66930,66931],{"class":163,"line":774},[290,66932,771],{"class":295},[290,66934,66935],{"class":163,"line":779},[290,66936,334],{"emptyLinePlaceholder":333},[290,66938,66939],{"class":163,"line":784},[290,66940,66941],{"class":455},"  \u002F* Micro-interactions & Reduced Motion *\u002F\n",[290,66943,66944,66946],{"class":163,"line":812},[290,66945,26109],{"class":541},[290,66947,16018],{"class":295},[290,66949,66950,66953],{"class":163,"line":860},[290,66951,66952],{"class":303},"    .card:hover",[290,66954,450],{"class":295},[290,66956,66957,66960,66962,66964,66966,66968,66970],{"class":163,"line":865},[290,66958,66959],{"class":461},"      transform",[290,66961,465],{"class":295},[290,66963,481],{"class":461},[290,66965,484],{"class":295},[290,66967,3561],{"class":461},[290,66969,674],{"class":541},[290,66971,500],{"class":295},[290,66973,66974,66977,66979,66981,66983,66985,66988,66990,66992,66994,66996,66998,67000,67002,67004,67006,67008],{"class":163,"line":871},[290,66975,66976],{"class":461},"      box-shadow",[290,66978,465],{"class":295},[290,66980,487],{"class":461},[290,66982,12650],{"class":461},[290,66984,674],{"class":541},[290,66986,66987],{"class":461}," 12",[290,66989,674],{"class":541},[290,66991,3224],{"class":461},[290,66993,484],{"class":295},[290,66995,487],{"class":461},[290,66997,569],{"class":295},[290,66999,487],{"class":461},[290,67001,569],{"class":295},[290,67003,487],{"class":461},[290,67005,569],{"class":295},[290,67007,1883],{"class":461},[290,67009,500],{"class":295},[290,67011,67012],{"class":163,"line":880},[290,67013,8200],{"class":295},[290,67015,67016],{"class":163,"line":896},[290,67017,771],{"class":295},[290,67019,67020],{"class":163,"line":4734},[290,67021,620],{"class":295},[14,67023,67024,67025,67027,67028,67030,67031,67033],{},"For a deeper dive into how micro-interactions integrate with container states, review the practical implementation in ",[27,67026,1427],{"href":1426},". The same container-first approach extends to other archetypes: see ",[27,67029,63547],{"href":63546}," for menus that collapse on available space, and ",[27,67032,63542],{"href":63541}," for tables that reflow to cards when their column track is too narrow.",[2757,67035,13396],{"id":8357},[3017,67037,67038,67049,67058,67067],{},[1396,67039,67040,67043,67044,67046,67047,42],{},[62,67041,67042],{},"Inspect Containment:"," Open Chrome\u002FEdge DevTools → Elements → Styles. Look for ",[18,67045,24401],{}," and verify the computed ",[18,67048,26072],{},[1396,67050,67051,67054,67055,67057],{},[62,67052,67053],{},"Simulate Resize:"," Use the \"Toggle Device Toolbar\" (Ctrl\u002FCmd + Shift + M) and drag the iframe edges. Watch ",[18,67056,11404],{}," values update live in the Computed panel.",[1396,67059,67060,67063,67064,67066],{},[62,67061,67062],{},"Paint Profiling:"," Open Performance tab → Record → Resize container. Check for \"Layout\" spikes. If present, add ",[18,67065,34746],{}," to the wrapper.",[1396,67068,67069,67072],{},[62,67070,67071],{},"Query Validation:"," In Firefox DevTools, use the \"Container Query\" inspector to visualize active breakpoints and fallback states.",[47,67074],{},[50,67076,15880],{"id":15879},[2250,67078,67079,67093],{},[2253,67080,67081],{},[2256,67082,67083,67085,67087,67089,67091],{},[2259,67084,3737],{},[2259,67086,2276],{},[2259,67088,2297],{},[2259,67090,2287],{},[2259,67092,2308],{},[2269,67094,67095,67109],{},[2256,67096,67097,67101,67103,67105,67107],{},[2274,67098,67099],{},[18,67100,24401],{},[2274,67102,29200],{},[2274,67104,8465],{},[2274,67106,37592],{},[2274,67108,29200],{},[2256,67110,67111,67115,67117,67119,67121],{},[2274,67112,67113],{},[18,67114,24933],{},[2274,67116,24936],{},[2274,67118,24939],{},[2274,67120,24942],{},[2274,67122,24936],{},[14,67124,67125,67127,67128,67130,67131,67133,67134,67136],{},[62,67126,13313],{}," Wrap container-dependent rules in ",[18,67129,26207],{},". Provide baseline mobile-first ",[18,67132,874],{}," queries outside the block for unsupported environments. Avoid polyfills (e.g., ",[18,67135,38256],{},") in production; they introduce significant JS overhead and conflict with native rendering pipelines.",[47,67138],{},[50,67140,67142],{"id":67141},"common-pitfalls-troubleshooting","Common Pitfalls & Troubleshooting",[2250,67144,67145,67155],{},[2253,67146,67147],{},[2256,67148,67149,67151,67153],{},[2259,67150,2338],{},[2259,67152,3876],{},[2259,67154,8563],{},[2269,67156,67157,67172,67192,67207],{},[2256,67158,67159,67164,67169],{},[2274,67160,67161],{},[62,67162,67163],{},"Context Leaks",[2274,67165,3930,67166,67168],{},[18,67167,24401],{}," on direct parent",[2274,67170,67171],{},"Ensure the queried element's immediate ancestor declares containment.",[2256,67173,67174,67179,67186],{},[2274,67175,67176],{},[62,67177,67178],{},"Specificity Conflicts",[2274,67180,33864,67181,67183,67184],{},[18,67182,874],{}," overriding ",[18,67185,12001],{},[2274,67187,67188,67189,67191],{},"Scope container rules to component classes. Use ",[18,67190,12681],{}," to manage cascade order.",[2256,67193,67194,67199,67202],{},[2274,67195,67196],{},[62,67197,67198],{},"Scroll-Induced Layout Shift",[2274,67200,67201],{},"Rapid container dimension changes during scroll",[2274,67203,29271,67204,67206],{},[18,67205,34746],{}," on the wrapper to bound reflows, or debounce JS resize listeners if used.",[2256,67208,67209,67216,67219],{},[2274,67210,67211],{},[62,67212,67213,67215],{},[18,67214,24933],{}," Lag",[2274,67217,67218],{},"Overuse of dynamic custom property queries",[2274,67220,67221],{},"Cache computed values in CSS variables. Limit style queries to static theme\u002Fdensity toggles.",[47,67223],{},[50,67225,1316],{"id":1315},[14,67227,67228,67231],{},[62,67229,67230],{},"Should I replace all media queries with container queries?","\nNo. Media queries remain optimal for page-level layout shifts, viewport-dependent features (e.g., navigation bars, full-bleed sections), and device orientation changes. Container queries should be reserved for component-level adaptability within unpredictable parent contexts.",[14,67233,67234,67237,67238,67240],{},[62,67235,67236],{},"How do container queries impact Core Web Vitals?","\nWhen implemented with proper containment (",[18,67239,34746],{},"), container queries reduce layout thrashing and improve LCP\u002FCLS scores by isolating component rendering from global reflows. The browser skips unnecessary subtree recalculations, leading to faster paint cycles.",[14,67242,67243,67246],{},[62,67244,67245],{},"Can I combine container queries with CSS Grid?","\nAbsolutely. Container queries and Grid are highly complementary. Use Grid for macro-layout structure and container queries to adjust component density, padding, and internal arrangement based on available track space. This combination eliminates the need for breakpoint-heavy grid templates.",[47,67248],{},[50,67250,1391],{"id":1390},[1393,67252,67253,67258,67264,67270,67275],{},[1396,67254,67255,67257],{},[27,67256,11296],{"href":5777}," — the parent guide framing component-first responsive design.",[1396,67259,67260,67263],{},[27,67261,67262],{"href":63546},"Responsive navigation without media queries"," — menus that collapse based on their own width.",[1396,67265,67266,67269],{},[27,67267,67268],{"href":63541},"Container query data tables"," — tables that reflow to stacked cards in narrow tracks.",[1396,67271,67272,67274],{},[27,67273,55627],{"href":44306}," — the macro structure components live inside.",[1396,67276,67277,67279],{},[27,67278,21407],{"href":18534}," — cross-area: layer accessible micro-interactions onto these components.",[1430,67281,67282],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":286,"searchDepth":330,"depth":330,"links":67284},[67285,67286,67287,67288,67291,67292,67293,67294],{"id":65893,"depth":330,"text":65894},{"id":66000,"depth":330,"text":66001},{"id":66293,"depth":330,"text":66294},{"id":66491,"depth":330,"text":66492,"children":67289},[67290],{"id":8357,"depth":337,"text":13396},{"id":15879,"depth":330,"text":15880},{"id":67141,"depth":330,"text":67142},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Responsive CSS component patterns: context-aware architecture, container queries, and modular UI design that eliminates global media query coupling.",{"seoTitle":67297,"datePublished":1447,"dateModified":1447,"faq":67298},"Responsive Component Patterns in CSS",[67299,67301,67303],{"q":67230,"a":67300},"No. Media queries remain optimal for page-level layout shifts, viewport-dependent features like navigation bars and full-bleed sections, and device orientation changes. Container queries should be reserved for component-level adaptability within unpredictable parent contexts.",{"q":67236,"a":67302},"When implemented with proper containment, container queries reduce layout thrashing and improve LCP and CLS by isolating component rendering from global reflows, so the browser skips unnecessary subtree recalculations.",{"q":67245,"a":67304},"Yes. Use Grid for macro-layout structure and container queries to adjust component density, padding, and internal arrangement based on available track space, which eliminates breakpoint-heavy grid templates.","\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns",{"title":65815,"description":67295},"mastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Findex","KS0KWEg_ftot4HKvXN1YkBPwLYehSTBL5r3e5PsS14s",{"id":67310,"title":67311,"body":67312,"description":68787,"extension":1444,"meta":68788,"navigation":333,"path":68801,"seo":68802,"stem":68803,"__hash__":68804},"content\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-navigation-without-media-queries\u002Findex.md","Responsive Navigation Without Media Queries Using Container Queries",{"type":7,"value":67313,"toc":68777},[67314,67317,67328,67330,67334,67342,67359,67361,67363,67366,68457,68460,68517,68519,68525,68553,68555,68559,68564,68670,68677,68679,68681,68702,68704,68706,68714,68723,68733,68745,68747,68749,68775],[10,67315,67311],{"id":67316},"responsive-navigation-without-media-queries-using-container-queries",[14,67318,67319,67320,67322,67323,60326,67325,67327],{},"A navigation bar is the component that most exposes the weakness of viewport breakpoints. The same markup might live in a full-width page header, a constrained 320px sidebar, or a dashboard card, yet a ",[18,67321,874],{}," rule applies one set of breakpoints to all of them. This guide, part of the ",[27,67324,64113],{"href":38097},[27,67326,11296],{"href":5777},", shows how to build a nav that flips from a horizontal row to a stacked disclosure menu based on its own container width, with a CSS-only toggle and a realistic accessibility plan.",[47,67329],{},[50,67331,67333],{"id":67332},"approach-rationale-container-size-over-viewport","Approach rationale: container size over viewport",[14,67335,67336,67337,61252,67339,67341],{},"The decision that drives everything is to stop asking \"how wide is the screen?\" and start asking \"how wide is the space this nav was handed?\". A container query answers the second question. By placing the nav inside a wrapper with ",[18,67338,25409],{},[18,67340,12001],{}," rule evaluates against that wrapper's resolved inline size, so a 360px sidebar collapses the menu even on a 4K display.",[14,67343,67344,67345,67347,67348,67350,67351,67354,67355,67358],{},"CSS alone can also handle the open\u002Fclosed state. A hidden checkbox plus the ",[18,67346,8708],{}," pseudo-class, or the newer ",[18,67349,275],{}," selector, toggles a panel with zero JavaScript. The honest tradeoff: a pure-CSS toggle is keyboard operable but cannot announce ",[18,67352,67353],{},"aria-expanded"," to assistive technology, and a checkbox is semantically a form control, not a menu button. The right production posture is progressive enhancement — ship the CSS toggle as a working no-JS baseline, then layer a real ",[18,67356,67357],{},"\u003Cbutton>"," and a few lines of script on top to manage ARIA state. Everything below treats the CSS solution as that baseline, not the finish line.",[47,67360],{},[50,67362,4203],{"id":4202},[14,67364,67365],{},"This single block is self-contained: paste it into an HTML file and resize the wrapper to watch the nav reflow. The container query handles layout; the checkbox handles disclosure when stacked.",[281,67367,67369],{"className":283,"code":67368,"language":285,"meta":286,"style":286},"\u003C!doctype html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n\u003Cmeta charset=\"utf-8\">\n\u003Cstyle>\n  \u002F* The wrapper is the query container. The nav queries THIS element's width. *\u002F\n  .nav-shell {\n    container-type: inline-size;\n    container-name: nav;\n    \u002F* Resize this to test: a real header would be 100%, a sidebar ~320px. *\u002F\n    max-width: 100%;\n    border: 1px solid #ccc;\n    border-radius: 8px;\n  }\n\n  .nav {\n    display: flex;\n    align-items: center;\n    gap: 1rem;\n    padding: 0.75rem 1rem;\n  }\n\n  .nav__brand { font-weight: 700; margin-right: auto; }\n\n  \u002F* The disclosure checkbox and its label are visually hidden by default,\n     because the wide layout shows links inline and needs no toggle. *\u002F\n  .nav__toggle { position: absolute; opacity: 0; pointer-events: none; }\n  .nav__toggle-label {\n    display: none;\n    cursor: pointer;\n    padding: 0.4rem 0.6rem;\n    border: 1px solid currentColor;\n    border-radius: 6px;\n    font: inherit;\n  }\n\n  .nav__links {\n    display: flex;\n    gap: 1rem;\n    list-style: none;\n    margin: 0;\n    padding: 0;\n  }\n  .nav__links a { text-decoration: none; padding: 0.25rem 0.4rem; }\n\n  \u002F* WIDE state is the default (mobile-up base). When the CONTAINER is narrow,\n     switch to a stacked, toggle-driven menu. We query max-width on the\n     container, not the viewport. *\u002F\n  @container nav (max-width: 480px) {\n    .nav__toggle-label { display: inline-block; }\n\n    \u002F* Collapse the link list into a vertical panel that is hidden until open. *\u002F\n    .nav__links {\n      flex-direction: column;\n      gap: 0;\n      width: 100%;\n      flex-basis: 100%;\n      \u002F* Animate from height 0; overflow clip prevents partial reveal. *\u002F\n      display: grid;\n      grid-template-rows: 0fr;\n      overflow: hidden;\n      transition: grid-template-rows 0.25s ease;\n    }\n    .nav__links > li { min-height: 0; }\n    .nav__links a { display: block; padding: 0.6rem 0.4rem; }\n\n    \u002F* :has() reads the checkbox state on the ancestor so the panel can open.\n       This works because :has() lets a parent style react to a descendant. *\u002F\n    .nav:has(.nav__toggle:checked) .nav__links {\n      grid-template-rows: 1fr;\n    }\n  }\n\n  \u002F* Fallback for engines without :has(): use the adjacent-sibling combinator.\n     The checkbox must precede the list in source order for ~ to reach it. *\u002F\n  @container nav (max-width: 480px) {\n    .nav__toggle:checked ~ .nav__links { grid-template-rows: 1fr; }\n  }\n\n  \u002F* Reduced-motion users get an instant open with no height tween. *\u002F\n  @media (prefers-reduced-motion: reduce) {\n    .nav__links { transition: none; }\n  }\n\u003C\u002Fstyle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv class=\"nav-shell\">\n    \u003Cnav class=\"nav\" aria-label=\"Primary\">\n      \u003Cspan class=\"nav__brand\">Acme\u003C\u002Fspan>\n      \u003C!-- Checkbox precedes the list so the ~ fallback can match. -->\n      \u003Cinput class=\"nav__toggle\" type=\"checkbox\" id=\"nav-toggle\">\n      \u003Clabel class=\"nav__toggle-label\" for=\"nav-toggle\">Menu\u003C\u002Flabel>\n      \u003Cul class=\"nav__links\">\n        \u003Cli>\u003Ca href=\"#\">Products\u003C\u002Fa>\u003C\u002Fli>\n        \u003Cli>\u003Ca href=\"#\">Pricing\u003C\u002Fa>\u003C\u002Fli>\n        \u003Cli>\u003Ca href=\"#\">Docs\u003C\u002Fa>\u003C\u002Fli>\n        \u003Cli>\u003Ca href=\"#\">Contact\u003C\u002Fa>\u003C\u002Fli>\n      \u003C\u002Ful>\n    \u003C\u002Fnav>\n  \u003C\u002Fdiv>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[18,67370,67371,67381,67395,67403,67417,67425,67430,67437,67443,67450,67455,67467,67483,67495,67499,67503,67510,67520,67530,67542,67558,67562,67566,67590,67594,67599,67604,67636,67643,67653,67663,67679,67695,67707,67717,67721,67725,67732,67742,67754,67764,67774,67784,67788,67818,67822,67827,67832,67837,67844,67859,67863,67868,67875,67885,67895,67907,67920,67925,67935,67948,67958,67973,67977,67995,68025,68029,68034,68039,68056,68068,68072,68076,68080,68085,68090,68096,68119,68123,68127,68132,68138,68152,68156,68164,68172,68180,68195,68216,68236,68241,68269,68295,68310,68337,68364,68390,68417,68425,68433,68441,68449],{"__ignoreMap":286},[290,67372,67373,67375,67377,67379],{"class":163,"line":292},[290,67374,8982],{"class":295},[290,67376,35913],{"class":299},[290,67378,8988],{"class":303},[290,67380,327],{"class":295},[290,67382,67383,67385,67387,67389,67391,67393],{"class":163,"line":330},[290,67384,296],{"class":295},[290,67386,285],{"class":299},[290,67388,8999],{"class":303},[290,67390,307],{"class":295},[290,67392,9004],{"class":310},[290,67394,327],{"class":295},[290,67396,67397,67399,67401],{"class":163,"line":337},[290,67398,296],{"class":295},[290,67400,9013],{"class":299},[290,67402,327],{"class":295},[290,67404,67405,67407,67409,67411,67413,67415],{"class":163,"line":364},[290,67406,296],{"class":295},[290,67408,9022],{"class":299},[290,67410,9025],{"class":303},[290,67412,307],{"class":295},[290,67414,9030],{"class":310},[290,67416,327],{"class":295},[290,67418,67419,67421,67423],{"class":163,"line":386},[290,67420,296],{"class":295},[290,67422,1430],{"class":299},[290,67424,327],{"class":295},[290,67426,67427],{"class":163,"line":408},[290,67428,67429],{"class":455},"  \u002F* The wrapper is the query container. The nav queries THIS element's width. *\u002F\n",[290,67431,67432,67435],{"class":163,"line":428},[290,67433,67434],{"class":303},"  .nav-shell",[290,67436,450],{"class":295},[290,67438,67439,67441],{"class":163,"line":517},[290,67440,37025],{"class":461},[290,67442,25565],{"class":295},[290,67444,67445,67447],{"class":163,"line":523},[290,67446,37980],{"class":461},[290,67448,67449],{"class":295},": nav;\n",[290,67451,67452],{"class":163,"line":532},[290,67453,67454],{"class":455},"    \u002F* Resize this to test: a real header would be 100%, a sidebar ~320px. *\u002F\n",[290,67456,67457,67459,67461,67463,67465],{"class":163,"line":551},[290,67458,44622],{"class":461},[290,67460,465],{"class":295},[290,67462,165],{"class":461},[290,67464,11018],{"class":541},[290,67466,471],{"class":295},[290,67468,67469,67471,67473,67475,67477,67479,67481],{"class":163,"line":586},[290,67470,21285],{"class":461},[290,67472,465],{"class":295},[290,67474,468],{"class":461},[290,67476,674],{"class":541},[290,67478,852],{"class":461},[290,67480,50182],{"class":461},[290,67482,471],{"class":295},[290,67484,67485,67487,67489,67491,67493],{"class":163,"line":602},[290,67486,12759],{"class":461},[290,67488,465],{"class":295},[290,67490,176],{"class":461},[290,67492,674],{"class":541},[290,67494,471],{"class":295},[290,67496,67497],{"class":163,"line":617},[290,67498,771],{"class":295},[290,67500,67501],{"class":163,"line":623},[290,67502,334],{"emptyLinePlaceholder":333},[290,67504,67505,67508],{"class":163,"line":628},[290,67506,67507],{"class":303},"  .nav",[290,67509,450],{"class":295},[290,67511,67512,67514,67516,67518],{"class":163,"line":634},[290,67513,34590],{"class":461},[290,67515,465],{"class":295},[290,67517,9055],{"class":461},[290,67519,471],{"class":295},[290,67521,67522,67524,67526,67528],{"class":163,"line":649},[290,67523,42363],{"class":461},[290,67525,465],{"class":295},[290,67527,9157],{"class":461},[290,67529,471],{"class":295},[290,67531,67532,67534,67536,67538,67540],{"class":163,"line":660},[290,67533,36027],{"class":461},[290,67535,465],{"class":295},[290,67537,468],{"class":461},[290,67539,801],{"class":541},[290,67541,471],{"class":295},[290,67543,67544,67546,67548,67550,67552,67554,67556],{"class":163,"line":688},[290,67545,36040],{"class":461},[290,67547,465],{"class":295},[290,67549,823],{"class":461},[290,67551,801],{"class":541},[290,67553,804],{"class":461},[290,67555,801],{"class":541},[290,67557,471],{"class":295},[290,67559,67560],{"class":163,"line":693},[290,67561,771],{"class":295},[290,67563,67564],{"class":163,"line":698},[290,67565,334],{"emptyLinePlaceholder":333},[290,67567,67568,67571,67573,67575,67577,67579,67581,67584,67586,67588],{"class":163,"line":704},[290,67569,67570],{"class":303},"  .nav__brand",[290,67572,790],{"class":295},[290,67574,49146],{"class":461},[290,67576,465],{"class":295},[290,67578,26119],{"class":461},[290,67580,828],{"class":295},[290,67582,67583],{"class":461},"margin-right",[290,67585,465],{"class":295},[290,67587,250],{"class":461},[290,67589,809],{"class":295},[290,67591,67592],{"class":163,"line":710},[290,67593,334],{"emptyLinePlaceholder":333},[290,67595,67596],{"class":163,"line":717},[290,67597,67598],{"class":455},"  \u002F* The disclosure checkbox and its label are visually hidden by default,\n",[290,67600,67601],{"class":163,"line":730},[290,67602,67603],{"class":455},"     because the wide layout shows links inline and needs no toggle. *\u002F\n",[290,67605,67606,67609,67611,67614,67616,67618,67620,67622,67624,67626,67628,67630,67632,67634],{"class":163,"line":742},[290,67607,67608],{"class":303},"  .nav__toggle",[290,67610,790],{"class":295},[290,67612,67613],{"class":461},"position",[290,67615,465],{"class":295},[290,67617,1927],{"class":461},[290,67619,828],{"class":295},[290,67621,76],{"class":461},[290,67623,465],{"class":295},[290,67625,487],{"class":461},[290,67627,828],{"class":295},[290,67629,34421],{"class":461},[290,67631,465],{"class":295},[290,67633,72],{"class":461},[290,67635,809],{"class":295},[290,67637,67638,67641],{"class":163,"line":768},[290,67639,67640],{"class":303},"  .nav__toggle-label",[290,67642,450],{"class":295},[290,67644,67645,67647,67649,67651],{"class":163,"line":774},[290,67646,34590],{"class":461},[290,67648,465],{"class":295},[290,67650,72],{"class":461},[290,67652,471],{"class":295},[290,67654,67655,67657,67659,67661],{"class":163,"line":779},[290,67656,61933],{"class":461},[290,67658,465],{"class":295},[290,67660,18849],{"class":461},[290,67662,471],{"class":295},[290,67664,67665,67667,67669,67671,67673,67675,67677],{"class":163,"line":784},[290,67666,36040],{"class":461},[290,67668,465],{"class":295},[290,67670,169],{"class":461},[290,67672,801],{"class":541},[290,67674,39313],{"class":461},[290,67676,801],{"class":541},[290,67678,471],{"class":295},[290,67680,67681,67683,67685,67687,67689,67691,67693],{"class":163,"line":812},[290,67682,21285],{"class":461},[290,67684,465],{"class":295},[290,67686,468],{"class":461},[290,67688,674],{"class":541},[290,67690,852],{"class":461},[290,67692,855],{"class":461},[290,67694,471],{"class":295},[290,67696,67697,67699,67701,67703,67705],{"class":163,"line":860},[290,67698,12759],{"class":461},[290,67700,465],{"class":295},[290,67702,5901],{"class":461},[290,67704,674],{"class":541},[290,67706,471],{"class":295},[290,67708,67709,67711,67713,67715],{"class":163,"line":865},[290,67710,44461],{"class":461},[290,67712,465],{"class":295},[290,67714,1970],{"class":461},[290,67716,471],{"class":295},[290,67718,67719],{"class":163,"line":871},[290,67720,771],{"class":295},[290,67722,67723],{"class":163,"line":880},[290,67724,334],{"emptyLinePlaceholder":333},[290,67726,67727,67730],{"class":163,"line":896},[290,67728,67729],{"class":303},"  .nav__links",[290,67731,450],{"class":295},[290,67733,67734,67736,67738,67740],{"class":163,"line":4734},[290,67735,34590],{"class":461},[290,67737,465],{"class":295},[290,67739,9055],{"class":461},[290,67741,471],{"class":295},[290,67743,67744,67746,67748,67750,67752],{"class":163,"line":4742},[290,67745,36027],{"class":461},[290,67747,465],{"class":295},[290,67749,468],{"class":461},[290,67751,801],{"class":541},[290,67753,471],{"class":295},[290,67755,67756,67758,67760,67762],{"class":163,"line":4761},[290,67757,45127],{"class":461},[290,67759,465],{"class":295},[290,67761,72],{"class":461},[290,67763,471],{"class":295},[290,67765,67766,67768,67770,67772],{"class":163,"line":4766},[290,67767,39260],{"class":461},[290,67769,465],{"class":295},[290,67771,487],{"class":461},[290,67773,471],{"class":295},[290,67775,67776,67778,67780,67782],{"class":163,"line":4786},[290,67777,36040],{"class":461},[290,67779,465],{"class":295},[290,67781,487],{"class":461},[290,67783,471],{"class":295},[290,67785,67786],{"class":163,"line":9635},[290,67787,771],{"class":295},[290,67789,67790,67792,67794,67796,67798,67800,67802,67804,67806,67808,67810,67812,67814,67816],{"class":163,"line":9641},[290,67791,67729],{"class":303},[290,67793,7556],{"class":299},[290,67795,790],{"class":295},[290,67797,60524],{"class":461},[290,67799,465],{"class":295},[290,67801,72],{"class":461},[290,67803,828],{"class":295},[290,67805,793],{"class":461},[290,67807,465],{"class":295},[290,67809,1668],{"class":461},[290,67811,801],{"class":541},[290,67813,50584],{"class":461},[290,67815,801],{"class":541},[290,67817,809],{"class":295},[290,67819,67820],{"class":163,"line":9647},[290,67821,334],{"emptyLinePlaceholder":333},[290,67823,67824],{"class":163,"line":9665},[290,67825,67826],{"class":455},"  \u002F* WIDE state is the default (mobile-up base). When the CONTAINER is narrow,\n",[290,67828,67829],{"class":163,"line":9683},[290,67830,67831],{"class":455},"     switch to a stacked, toggle-driven menu. We query max-width on the\n",[290,67833,67834],{"class":163,"line":9688},[290,67835,67836],{"class":455},"     container, not the viewport. *\u002F\n",[290,67838,67839,67841],{"class":163,"line":9694},[290,67840,37040],{"class":541},[290,67842,67843],{"class":295}," nav (max-width: 480px) {\n",[290,67845,67846,67849,67851,67853,67855,67857],{"class":163,"line":9700},[290,67847,67848],{"class":303},"    .nav__toggle-label",[290,67850,790],{"class":295},[290,67852,59],{"class":461},[290,67854,465],{"class":295},[290,67856,17747],{"class":461},[290,67858,809],{"class":295},[290,67860,67861],{"class":163,"line":9710},[290,67862,334],{"emptyLinePlaceholder":333},[290,67864,67865],{"class":163,"line":9716},[290,67866,67867],{"class":455},"    \u002F* Collapse the link list into a vertical panel that is hidden until open. *\u002F\n",[290,67869,67870,67873],{"class":163,"line":9738},[290,67871,67872],{"class":303},"    .nav__links",[290,67874,450],{"class":295},[290,67876,67877,67879,67881,67883],{"class":163,"line":9748},[290,67878,38020],{"class":461},[290,67880,465],{"class":295},[290,67882,40315],{"class":461},[290,67884,471],{"class":295},[290,67886,67887,67889,67891,67893],{"class":163,"line":9754},[290,67888,38032],{"class":461},[290,67890,465],{"class":295},[290,67892,487],{"class":461},[290,67894,471],{"class":295},[290,67896,67897,67899,67901,67903,67905],{"class":163,"line":9760},[290,67898,57028],{"class":461},[290,67900,465],{"class":295},[290,67902,165],{"class":461},[290,67904,11018],{"class":541},[290,67906,471],{"class":295},[290,67908,67909,67912,67914,67916,67918],{"class":163,"line":9777},[290,67910,67911],{"class":461},"      flex-basis",[290,67913,465],{"class":295},[290,67915,165],{"class":461},[290,67917,11018],{"class":541},[290,67919,471],{"class":295},[290,67921,67922],{"class":163,"line":9787},[290,67923,67924],{"class":455},"      \u002F* Animate from height 0; overflow clip prevents partial reveal. *\u002F\n",[290,67926,67927,67929,67931,67933],{"class":163,"line":9793},[290,67928,37053],{"class":461},[290,67930,465],{"class":295},[290,67932,9147],{"class":461},[290,67934,471],{"class":295},[290,67936,67937,67940,67942,67944,67946],{"class":163,"line":9799},[290,67938,67939],{"class":461},"      grid-template-rows",[290,67941,465],{"class":295},[290,67943,487],{"class":461},[290,67945,11964],{"class":541},[290,67947,471],{"class":295},[290,67949,67950,67952,67954,67956],{"class":163,"line":9805},[290,67951,64522],{"class":461},[290,67953,465],{"class":295},[290,67955,7808],{"class":461},[290,67957,471],{"class":295},[290,67959,67960,67962,67965,67967,67969,67971],{"class":163,"line":9811},[290,67961,36625],{"class":461},[290,67963,67964],{"class":295},": grid-template-rows ",[290,67966,1668],{"class":461},[290,67968,1886],{"class":541},[290,67970,545],{"class":461},[290,67972,471],{"class":295},[290,67974,67975],{"class":163,"line":9820},[290,67976,8200],{"class":295},[290,67978,67979,67981,67983,67985,67987,67989,67991,67993],{"class":163,"line":9829},[290,67980,67872],{"class":303},[290,67982,24017],{"class":541},[290,67984,32288],{"class":299},[290,67986,790],{"class":295},[290,67988,37680],{"class":461},[290,67990,465],{"class":295},[290,67992,487],{"class":461},[290,67994,809],{"class":295},[290,67996,67997,67999,68001,68003,68005,68007,68009,68011,68013,68015,68017,68019,68021,68023],{"class":163,"line":27197},[290,67998,67872],{"class":303},[290,68000,7556],{"class":299},[290,68002,790],{"class":295},[290,68004,59],{"class":461},[290,68006,465],{"class":295},[290,68008,68],{"class":461},[290,68010,828],{"class":295},[290,68012,793],{"class":461},[290,68014,465],{"class":295},[290,68016,232],{"class":461},[290,68018,801],{"class":541},[290,68020,50584],{"class":461},[290,68022,801],{"class":541},[290,68024,809],{"class":295},[290,68026,68027],{"class":163,"line":27202},[290,68028,334],{"emptyLinePlaceholder":333},[290,68030,68031],{"class":163,"line":27209},[290,68032,68033],{"class":455},"    \u002F* :has() reads the checkbox state on the ancestor so the panel can open.\n",[290,68035,68036],{"class":163,"line":27220},[290,68037,68038],{"class":455},"       This works because :has() lets a parent style react to a descendant. *\u002F\n",[290,68040,68041,68044,68046,68049,68051,68054],{"class":163,"line":27235},[290,68042,68043],{"class":303},"    .nav:has",[290,68045,484],{"class":295},[290,68047,68048],{"class":303},".nav__toggle:checked",[290,68050,490],{"class":295},[290,68052,68053],{"class":303},".nav__links",[290,68055,450],{"class":295},[290,68057,68058,68060,68062,68064,68066],{"class":163,"line":27240},[290,68059,67939],{"class":461},[290,68061,465],{"class":295},[290,68063,468],{"class":461},[290,68065,11964],{"class":541},[290,68067,471],{"class":295},[290,68069,68070],{"class":163,"line":43950},[290,68071,8200],{"class":295},[290,68073,68074],{"class":163,"line":43959},[290,68075,771],{"class":295},[290,68077,68078],{"class":163,"line":43968},[290,68079,334],{"emptyLinePlaceholder":333},[290,68081,68082],{"class":163,"line":43977},[290,68083,68084],{"class":455},"  \u002F* Fallback for engines without :has(): use the adjacent-sibling combinator.\n",[290,68086,68087],{"class":163,"line":43986},[290,68088,68089],{"class":455},"     The checkbox must precede the list in source order for ~ to reach it. *\u002F\n",[290,68091,68092,68094],{"class":163,"line":43995},[290,68093,37040],{"class":541},[290,68095,67843],{"class":295},[290,68097,68098,68101,68104,68107,68109,68111,68113,68115,68117],{"class":163,"line":45140},[290,68099,68100],{"class":303},"    .nav__toggle:checked",[290,68102,68103],{"class":541}," ~",[290,68105,68106],{"class":303}," .nav__links",[290,68108,790],{"class":295},[290,68110,45255],{"class":461},[290,68112,465],{"class":295},[290,68114,468],{"class":461},[290,68116,11964],{"class":541},[290,68118,809],{"class":295},[290,68120,68121],{"class":163,"line":45145},[290,68122,771],{"class":295},[290,68124,68125],{"class":163,"line":45151},[290,68126,334],{"emptyLinePlaceholder":333},[290,68128,68129],{"class":163,"line":45157},[290,68130,68131],{"class":455},"  \u002F* Reduced-motion users get an instant open with no height tween. *\u002F\n",[290,68133,68134,68136],{"class":163,"line":45165},[290,68135,26109],{"class":541},[290,68137,877],{"class":295},[290,68139,68140,68142,68144,68146,68148,68150],{"class":163,"line":45173},[290,68141,67872],{"class":303},[290,68143,790],{"class":295},[290,68145,887],{"class":461},[290,68147,465],{"class":295},[290,68149,72],{"class":461},[290,68151,809],{"class":295},[290,68153,68154],{"class":163,"line":45184},[290,68155,771],{"class":295},[290,68157,68158,68160,68162],{"class":163,"line":45199},[290,68159,431],{"class":295},[290,68161,1430],{"class":299},[290,68163,327],{"class":295},[290,68165,68166,68168,68170],{"class":163,"line":45211},[290,68167,431],{"class":295},[290,68169,9013],{"class":299},[290,68171,327],{"class":295},[290,68173,68174,68176,68178],{"class":163,"line":45225},[290,68175,296],{"class":295},[290,68177,9239],{"class":299},[290,68179,327],{"class":295},[290,68181,68182,68184,68186,68188,68190,68193],{"class":163,"line":45230},[290,68183,367],{"class":295},[290,68185,342],{"class":299},[290,68187,314],{"class":303},[290,68189,307],{"class":295},[290,68191,68192],{"class":310},"\"nav-shell\"",[290,68194,327],{"class":295},[290,68196,68197,68199,68201,68203,68205,68208,68210,68212,68214],{"class":163,"line":45235},[290,68198,4290],{"class":295},[290,68200,18917],{"class":299},[290,68202,314],{"class":303},[290,68204,307],{"class":295},[290,68206,68207],{"class":310},"\"nav\"",[290,68209,19002],{"class":303},[290,68211,307],{"class":295},[290,68213,47030],{"class":310},[290,68215,327],{"class":295},[290,68217,68218,68220,68222,68224,68226,68229,68232,68234],{"class":163,"line":45240},[290,68219,36430],{"class":295},[290,68221,290],{"class":299},[290,68223,314],{"class":303},[290,68225,307],{"class":295},[290,68227,68228],{"class":310},"\"nav__brand\"",[290,68230,68231],{"class":295},">Acme\u003C\u002F",[290,68233,290],{"class":299},[290,68235,327],{"class":295},[290,68237,68238],{"class":163,"line":45246},[290,68239,68240],{"class":455},"      \u003C!-- Checkbox precedes the list so the ~ fallback can match. -->\n",[290,68242,68243,68245,68247,68249,68251,68254,68256,68258,68260,68262,68264,68267],{"class":163,"line":45264},[290,68244,36430],{"class":295},[290,68246,18985],{"class":299},[290,68248,314],{"class":303},[290,68250,307],{"class":295},[290,68252,68253],{"class":310},"\"nav__toggle\"",[290,68255,393],{"class":303},[290,68257,307],{"class":295},[290,68259,28057],{"class":310},[290,68261,345],{"class":303},[290,68263,307],{"class":295},[290,68265,68266],{"class":310},"\"nav-toggle\"",[290,68268,327],{"class":295},[290,68270,68271,68273,68275,68277,68279,68282,68284,68286,68288,68291,68293],{"class":163,"line":45294},[290,68272,36430],{"class":295},[290,68274,28035],{"class":299},[290,68276,314],{"class":303},[290,68278,307],{"class":295},[290,68280,68281],{"class":310},"\"nav__toggle-label\"",[290,68283,28620],{"class":303},[290,68285,307],{"class":295},[290,68287,68266],{"class":310},[290,68289,68290],{"class":295},">Menu\u003C\u002F",[290,68292,28035],{"class":299},[290,68294,327],{"class":295},[290,68296,68297,68299,68301,68303,68305,68308],{"class":163,"line":45299},[290,68298,36430],{"class":295},[290,68300,1393],{"class":299},[290,68302,314],{"class":303},[290,68304,307],{"class":295},[290,68306,68307],{"class":310},"\"nav__links\"",[290,68309,327],{"class":295},[290,68311,68312,68314,68316,68318,68320,68322,68324,68326,68329,68331,68333,68335],{"class":163,"line":45308},[290,68313,36461],{"class":295},[290,68315,1396],{"class":299},[290,68317,9303],{"class":295},[290,68319,27],{"class":299},[290,68321,17687],{"class":303},[290,68323,307],{"class":295},[290,68325,48567],{"class":310},[290,68327,68328],{"class":295},">Products\u003C\u002F",[290,68330,27],{"class":299},[290,68332,11472],{"class":295},[290,68334,1396],{"class":299},[290,68336,327],{"class":295},[290,68338,68339,68341,68343,68345,68347,68349,68351,68353,68356,68358,68360,68362],{"class":163,"line":45317},[290,68340,36461],{"class":295},[290,68342,1396],{"class":299},[290,68344,9303],{"class":295},[290,68346,27],{"class":299},[290,68348,17687],{"class":303},[290,68350,307],{"class":295},[290,68352,48567],{"class":310},[290,68354,68355],{"class":295},">Pricing\u003C\u002F",[290,68357,27],{"class":299},[290,68359,11472],{"class":295},[290,68361,1396],{"class":299},[290,68363,327],{"class":295},[290,68365,68366,68368,68370,68372,68374,68376,68378,68380,68382,68384,68386,68388],{"class":163,"line":45326},[290,68367,36461],{"class":295},[290,68369,1396],{"class":299},[290,68371,9303],{"class":295},[290,68373,27],{"class":299},[290,68375,17687],{"class":303},[290,68377,307],{"class":295},[290,68379,48567],{"class":310},[290,68381,60995],{"class":295},[290,68383,27],{"class":299},[290,68385,11472],{"class":295},[290,68387,1396],{"class":299},[290,68389,327],{"class":295},[290,68391,68392,68394,68396,68398,68400,68402,68404,68406,68409,68411,68413,68415],{"class":163,"line":45343},[290,68393,36461],{"class":295},[290,68395,1396],{"class":299},[290,68397,9303],{"class":295},[290,68399,27],{"class":299},[290,68401,17687],{"class":303},[290,68403,307],{"class":295},[290,68405,48567],{"class":310},[290,68407,68408],{"class":295},">Contact\u003C\u002F",[290,68410,27],{"class":299},[290,68412,11472],{"class":295},[290,68414,1396],{"class":299},[290,68416,327],{"class":295},[290,68418,68419,68421,68423],{"class":163,"line":45358},[290,68420,36493],{"class":295},[290,68422,1393],{"class":299},[290,68424,327],{"class":295},[290,68426,68427,68429,68431],{"class":163,"line":45379},[290,68428,36502],{"class":295},[290,68430,18917],{"class":299},[290,68432,327],{"class":295},[290,68434,68435,68437,68439],{"class":163,"line":45395},[290,68436,4315],{"class":295},[290,68438,342],{"class":299},[290,68440,327],{"class":295},[290,68442,68443,68445,68447],{"class":163,"line":45416},[290,68444,431],{"class":295},[290,68446,9239],{"class":299},[290,68448,327],{"class":295},[290,68450,68451,68453,68455],{"class":163,"line":45437},[290,68452,431],{"class":295},[290,68454,285],{"class":299},[290,68456,327],{"class":295},[14,68458,68459],{},"The diagram below shows the two end states the single rule produces.",[133,68461,140,68463,140,68466,140,68469,140,68471,140,68473,140,68475,140,68477,140,68481,140,68483,140,68485,140,68487,140,68490,140,68493,140,68495,140,68497,140,68499,140,68502,140,68506,140,68508,140,68510,140,68512,140,68514],{"viewBox":4133,"role":136,"ariaLabel":68462,"xmlns":138,"style":139},"A navigation bar shown horizontal in a wide container and stacked behind a menu toggle in a narrow container",[142,68464,68465],{},"Nav reflow by container width",[146,68467,68468],{},"Left: wide container with brand and inline links. Right: narrow container with brand, menu toggle, and stacked links.",[150,68470,65505],{"x":2602,"y":153,"style":26429},[150,68472,65508],{"x":5887,"y":153,"style":26429},[171,68474],{"x":158,"y":5139,"width":6740,"height":9105,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},[171,68476],{"x":4146,"y":6634,"width":2630,"height":153,"rx":5911,"fill":177,"opacity":1668},[150,68478,68480],{"x":5892,"y":68479,"style":6653},"93","Acme",[171,68482],{"x":1787,"y":6634,"width":4146,"height":153,"rx":5911,"fill":72,"stroke":167,"opacity":232},[171,68484],{"x":32987,"y":6634,"width":4146,"height":153,"rx":5911,"fill":72,"stroke":167,"opacity":232},[171,68486],{"x":19916,"y":6634,"width":4146,"height":153,"rx":5911,"fill":72,"stroke":167,"opacity":232},[171,68488],{"x":32080,"y":6634,"width":68489,"height":153,"rx":5911,"fill":72,"stroke":167,"opacity":232},"34",[150,68491,68492],{"x":2602,"y":1787,"style":10248},"@container nav (min-width)",[171,68494],{"x":15019,"y":5139,"width":8947,"height":37881,"rx":836,"fill":72,"stroke":167,"strokeWidth":168,"opacity":798},[171,68496],{"x":37885,"y":29485,"width":2630,"height":26451,"rx":5911,"fill":177,"opacity":1668},[150,68498,68480],{"x":63456,"y":159,"style":6653},[171,68500],{"x":68501,"y":29485,"width":1788,"height":26451,"rx":5911,"fill":72,"stroke":167,"opacity":21064},"588",[150,68503,68505],{"x":68504,"y":159,"style":6653},"616","Menu",[171,68507],{"x":37885,"y":5159,"width":29490,"height":26451,"rx":249,"fill":72,"stroke":167,"opacity":14604},[171,68509],{"x":37885,"y":40954,"width":29490,"height":26451,"rx":249,"fill":72,"stroke":167,"opacity":14604},[171,68511],{"x":37885,"y":12454,"width":29490,"height":26451,"rx":249,"fill":72,"stroke":167,"opacity":14604},[171,68513],{"x":37885,"y":11367,"width":29490,"height":26451,"rx":249,"fill":72,"stroke":167,"opacity":14604},[150,68515,68516],{"x":5887,"y":6641,"style":10248},":has(:checked) opens",[47,68518],{},[50,68520,65567,68522,68524],{"id":68521},"key-technique-callout-has-reading-a-siblings-state",[18,68523,275],{}," reading a sibling's state",[14,68526,68527,68528,68531,68532,68534,68535,68537,68538,68541,68542,24192,68544,80,68547,68549,68550,68552],{},"The line that makes the disclosure work without script is ",[18,68529,68530],{},".nav:has(.nav__toggle:checked) .nav__links",". Historically CSS could only style downward, so a checkbox could influence later siblings via ",[18,68533,27879],{}," but never an ancestor or arbitrary cousins. ",[18,68536,275],{}," removes that limitation: it lets the ",[18,68539,68540],{},".nav"," element match conditionally on the presence of a checked descendant, so styling the link panel no longer depends on its source position relative to the input. The grid trick — animating ",[18,68543,45255],{},[18,68545,68546],{},"0fr",[18,68548,47348],{}," — gives a smooth height transition that ",[18,68551,42224],{}," hacks cannot, because there is no magic-number ceiling to guess.",[47,68554],{},[50,68556,68558],{"id":68557},"variation-a-focus-visible-accessible-toggle","Variation: a focus-visible accessible toggle",[14,68560,68561,68562,42],{},"The CSS baseline is keyboard operable, but the label has no visible focus indicator on many engines because the real focus lands on the hidden checkbox. Forward the focus ring to the label and respect motion preferences with this addition. For the broader treatment, see the guide on ",[27,68563,24536],{"href":3349},[281,68565,68567],{"className":438,"code":68566,"language":440,"meta":286,"style":286},"\u002F* Surface the hidden checkbox's focus state on its visible label. *\u002F\n.nav__toggle:focus-visible + .nav__toggle-label {\n  outline: 2px solid #7aa2ff;\n  outline-offset: 2px;\n}\n\n\u002F* Larger hit target and clearer affordance in the narrow state. *\u002F\n@container nav (max-width: 480px) {\n  .nav__toggle-label { min-height: 44px; display: inline-flex; align-items: center; }\n}\n",[18,68568,68569,68574,68586,68602,68614,68618,68622,68627,68633,68666],{"__ignoreMap":286},[290,68570,68571],{"class":163,"line":292},[290,68572,68573],{"class":455},"\u002F* Surface the hidden checkbox's focus state on its visible label. *\u002F\n",[290,68575,68576,68579,68581,68584],{"class":163,"line":330},[290,68577,68578],{"class":303},".nav__toggle:focus-visible",[290,68580,3592],{"class":541},[290,68582,68583],{"class":303}," .nav__toggle-label",[290,68585,450],{"class":295},[290,68587,68588,68590,68592,68594,68596,68598,68600],{"class":163,"line":337},[290,68589,1617],{"class":461},[290,68591,465],{"class":295},[290,68593,194],{"class":461},[290,68595,674],{"class":541},[290,68597,852],{"class":461},[290,68599,59430],{"class":461},[290,68601,471],{"class":295},[290,68603,68604,68606,68608,68610,68612],{"class":163,"line":364},[290,68605,1647],{"class":461},[290,68607,465],{"class":295},[290,68609,194],{"class":461},[290,68611,674],{"class":541},[290,68613,471],{"class":295},[290,68615,68616],{"class":163,"line":386},[290,68617,620],{"class":295},[290,68619,68620],{"class":163,"line":408},[290,68621,334],{"emptyLinePlaceholder":333},[290,68623,68624],{"class":163,"line":428},[290,68625,68626],{"class":455},"\u002F* Larger hit target and clearer affordance in the narrow state. *\u002F\n",[290,68628,68629,68631],{"class":163,"line":517},[290,68630,12001],{"class":541},[290,68632,67843],{"class":295},[290,68634,68635,68637,68639,68641,68643,68645,68647,68649,68651,68653,68655,68657,68660,68662,68664],{"class":163,"line":523},[290,68636,67640],{"class":303},[290,68638,790],{"class":295},[290,68640,37680],{"class":461},[290,68642,465],{"class":295},[290,68644,8879],{"class":461},[290,68646,674],{"class":541},[290,68648,828],{"class":295},[290,68650,59],{"class":461},[290,68652,465],{"class":295},[290,68654,27407],{"class":461},[290,68656,828],{"class":295},[290,68658,68659],{"class":461},"align-items",[290,68661,465],{"class":295},[290,68663,9157],{"class":461},[290,68665,809],{"class":295},[290,68667,68668],{"class":163,"line":532},[290,68669,620],{"class":295},[14,68671,68672,68673,68676],{},"When you progressively enhance with JavaScript, replace the label\u002Fcheckbox with a ",[18,68674,68675],{},"\u003Cbutton aria-expanded=\"false\" aria-controls=\"nav-links\">"," and toggle both the attribute and a class. Keep the container query untouched; only the disclosure mechanism upgrades.",[47,68678],{},[50,68680,1299],{"id":1298},[14,68682,68683,68684,68686,68687,68689,68690,68692,68693,68695,68696,68698,68699,68701],{},"Size container queries are Baseline and have shipped since Chrome and Edge 105, Safari 16, and Firefox 110, so ",[18,68685,12001],{}," is safe in 2026. The ",[18,68688,275],{}," selector arrived later — Chrome and Edge 105, Safari 15.4, Firefox 121 — but is now widely available; the ",[18,68691,27879],{}," sibling rule in the code is a complete fallback for the disclosure where ",[18,68694,275],{}," is absent. If you must support pre-2023 engines, wrap the whole component in ",[18,68697,26207],{}," and serve a ",[18,68700,874],{},"-based collapse as the legacy path.",[47,68703],{},[50,68705,1316],{"id":1315},[14,68707,68708,68713],{},[62,68709,18658,68710,68712],{},[18,68711,12001],{}," instead of media queries for navigation?","\nA container query reacts to the nav's own available width, so the same component collapses correctly whether it sits in a wide header, a narrow sidebar, or a card. Media queries only know the viewport, which breaks reusable components placed in unpredictable contexts.",[14,68715,68716,68719,68720,68722],{},[62,68717,68718],{},"Can a CSS-only disclosure menu be accessible?","\nPartly. A hidden checkbox toggle is keyboard operable and works without JavaScript, but it cannot expose ",[18,68721,67353],{}," state to assistive technology. For production, progressively enhance with a real button plus a small script while keeping the CSS toggle as the no-JS baseline.",[14,68724,68725,37757,68730,68732],{},[62,68726,5714,68727,68729],{},[18,68728,275],{}," have good enough browser support in 2026?",[18,68731,275],{}," shipped in Chrome and Edge 105, Safari 15.4, and Firefox 121, so it is broadly available. Provide a checkbox sibling-selector fallback for the rare engine that lacks it.",[14,68734,68735,68738,68739,68741,68742,68744],{},[62,68736,68737],{},"Why does my container query never match on the nav?","\nThe queried ancestor needs ",[18,68740,24401],{},". Set ",[18,68743,25409],{}," on the nav's wrapper, because an element cannot query its own size — only that of a containing ancestor.",[47,68746],{},[50,68748,1391],{"id":1390},[1393,68750,68751,68755,68760,68765,68770],{},[1396,68752,68753,65763],{},[27,68754,38098],{"href":38097},[1396,68756,68757,68759],{},[27,68758,10100],{"href":1426}," — sibling pattern using the same containment setup.",[1396,68761,68762,68764],{},[27,68763,64055],{"href":63541}," — another component that reflows on container width.",[1396,68766,68767,68769],{},[27,68768,8763],{"href":4036}," — naming and scoping rules for real codebases.",[1396,68771,68772,68774],{},[27,68773,18535],{"href":18534}," — cross-area guide on stateful CSS interactions for nav links.",[1430,68776,36799],{},{"title":286,"searchDepth":330,"depth":330,"links":68778},[68779,68780,68781,68783,68784,68785,68786],{"id":67332,"depth":330,"text":67333},{"id":4202,"depth":330,"text":4203},{"id":68521,"depth":330,"text":68782},"Key technique callout: :has() reading a sibling's state",{"id":68557,"depth":330,"text":68558},{"id":1298,"depth":330,"text":1299},{"id":1315,"depth":330,"text":1316},{"id":1390,"depth":330,"text":1391},"Build a navigation bar that switches from horizontal to a stacked menu based on its container width using @container, :has(), and a CSS-only disclosure toggle.",{"seoTitle":68789,"datePublished":1447,"dateModified":1447,"faq":68790},"Responsive Nav Without Media Queries",[68791,68794,68796,68799],{"q":68792,"a":68793},"Why use @container instead of media queries for navigation?","A container query reacts to the nav's own available width, so the same component collapses correctly whether it sits in a wide header, a narrow sidebar, or a card. Media queries only know the viewport, which breaks reusable components.",{"q":68718,"a":68795},"Partly. A hidden checkbox toggle is keyboard operable and works without JavaScript, but it cannot expose aria-expanded state. For production, progressively enhance with a real button plus a small script while keeping the CSS toggle as the no-JS baseline.",{"q":68797,"a":68798},"Does :has() have good enough browser support in 2026?","Yes. :has() shipped in Chrome and Edge 105, Safari 15.4, and Firefox 121, so it is broadly available. Provide a checkbox sibling-selector fallback for the rare engine that lacks it.",{"q":68737,"a":68800},"The queried ancestor needs container-type. Set container-type: inline-size on the nav's wrapper, because an element cannot query its own size, only that of a containing ancestor.","\u002Fmastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-navigation-without-media-queries",{"title":67311,"description":68787},"mastering-container-queries-responsive-layouts\u002Fresponsive-component-patterns\u002Fresponsive-navigation-without-media-queries\u002Findex","GExiFgIdaHGmBDgkcpaR_bX7wrzqTTOk7np6x8AUN4k",1781797086346]